Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 64a4a4f

Browse files
chore(lab): add new exercises
* chore(lab): critial routers exercise * solve the problem but with repetitions * 🔧 chore (lab): solve the problem * --wip-- [skip ci] * --wip-- [skip ci] * feat(lab): integer to words * feat(lab): improve algo to cover corner cases * chore(lab): alien dictionary exercise * chore(lab): sum exercises added * new exercise * using dijkstra algorithm * new exercises * update exercises * new exercise * solution * 🔧 chore eslint
1 parent bcc81b4 commit 64a4a4f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+11752
-5974
lines changed

‎.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ module.exports = {
1515

1616
// https://eslint.org/docs/rules/no-plusplus
1717
// allows unary operators ++ and -- in the afterthought (final expression) of a for loop.
18-
'no-plusplus': [2, { 'allowForLoopAfterthoughts': true }],
18+
'no-plusplus': [0, { 'allowForLoopAfterthoughts': true }],
19+
'no-continue': [0],
1920

2021
// Allow for..of
2122
'no-restricted-syntax': [0, 'ForOfStatement'],

‎lab/exercises/10-mixed/2sum-1.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
/**
3+
* Given a SORTED array and a target, return the indices of the 2 number that sum up target
4+
*
5+
* @param {number[]} nums
6+
* @param {numer} target
7+
* @returns
8+
*
9+
* @runtime O(n)
10+
*/
11+
function twoSum(nums, target) {
12+
const len = nums.length - 1;
13+
let lo = 0;
14+
let hi = len;
15+
16+
while (lo < hi && hi > 0 && lo < len) {
17+
const sum = nums[lo] + nums[hi];
18+
if (sum === target) {
19+
return [lo, hi];
20+
}
21+
if (sum > target) {
22+
hi--;
23+
} else {
24+
lo++;
25+
}
26+
}
27+
28+
return [];
29+
}
30+
31+
module.exports = twoSum;

‎lab/exercises/10-mixed/2sum.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
/**
3+
* Given an array and a target, return the indices of the 2 number that sum up target
4+
*
5+
* @param {number[]} nums
6+
* @param {numer} target
7+
* @returns
8+
*
9+
* @runtime O(n)
10+
*/
11+
function twoSum(nums, target) {
12+
const map = new Map();
13+
14+
for (let i = 0; i < nums.length; i++) {
15+
const diff = target - nums[i];
16+
if (map.has(diff)) {
17+
return [map.get(diff), i];
18+
}
19+
map.set(nums[i], i);
20+
}
21+
22+
return [];
23+
}
24+
25+
module.exports = twoSum;

‎lab/exercises/10-mixed/2sum.spec.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const fn = require('./2sum');
2+
3+
describe('2 sum', () => {
4+
it('should work', () => {
5+
expect(fn([-1, 0, 1], 0)).toEqual(expect.arrayContaining([0, 2]));
6+
});
7+
8+
it('should work', () => {
9+
expect(fn([2, 7, 11, 15], 9)).toEqual([0, 1]);
10+
});
11+
12+
it('should work', () => {
13+
expect(fn([2, 7, 11, 15], 18)).toEqual([1, 2]);
14+
});
15+
16+
it('should be empty', () => {
17+
expect(fn([2, 7, 11, 15], 1)).toEqual([]);
18+
});
19+
20+
it('should should work with non-sorted', () => {
21+
expect(fn([3, 2, 4], 6)).toEqual([1, 2]);
22+
});
23+
});

‎lab/exercises/10-mixed/3sum-1.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* @param {number[]} nums
3+
* @return {number[][]}
4+
*
5+
* @runtime O(n^3)
6+
*/
7+
function threeSum(nums) {
8+
const ans = new Set();
9+
10+
for (let i = 0; i < nums.length; i++) {
11+
for (let j = i + 1; j < nums.length; j++) {
12+
for (let k = j + 1; k < nums.length; k++) {
13+
if (nums[i] + nums[j] + nums[k] === 0) {
14+
ans.add(JSON.stringify([nums[i], nums[j], nums[k]].sort()));
15+
}
16+
}
17+
}
18+
}
19+
20+
return Array.from(ans).map((s) => JSON.parse(s));
21+
}
22+
23+
module.exports = threeSum;
24+
25+
// Given an array find the unique triplets elements that sum zero.
26+
27+
// Brute force: O(n^3)
28+
// Using twoSum: O(n^2)
29+
//

‎lab/exercises/10-mixed/3sum-2.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @param {number[]} nums
3+
* @return {number[][]}
4+
*
5+
* @runtime O(n^2)
6+
* @space O(n)
7+
*/
8+
const threeSum = function (nums) {
9+
const array = nums.reduce((acc, n, i) => { // O(n^2)
10+
if (i > nums.length - 2) return acc;
11+
const partial = twoSum(nums, -n, i + 1);
12+
const res = partial.map((p) => [n, ...p]);
13+
// console.log({i, n, partial, res, acc})
14+
return acc.concat(res);
15+
}, []);
16+
17+
// remove dups
18+
const set = array.reduce((acc, n) => {
19+
const str = n.sort((a, b) => a - b).toString();
20+
return acc.add(str);
21+
}, new Set());
22+
23+
// convert to array of nums
24+
return [...set].map((a) => a.split(',').map((n) => +n));
25+
};
26+
27+
function twoSum(nums, target, start) { // O(n)
28+
const ans = [];
29+
const map = new Map();
30+
31+
for (let i = start; i < nums.length; i++) {
32+
if (map.has(target - nums[i])) {
33+
ans.push([target - nums[i], nums[i]]);
34+
}
35+
map.set(nums[i], i);
36+
}
37+
38+
return ans;
39+
}
40+
41+
module.exports = threeSum;
42+
43+
// Given an array find the unique triplets elements that sum zero.
44+
45+
// Brute force: O(n^3)
46+
// Using twoSum: O(n^2)
47+
//

‎lab/exercises/10-mixed/3sum.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/**
2+
* @param {number[]} nums
3+
* @return {number[][]}
4+
*
5+
* @runtime O(n^2) and skips duplicates
6+
* @space O(1)
7+
*/
8+
function threeSum(nums) {
9+
const ans = [];
10+
11+
nums.sort((a, b) => a - b); // sort: O(n log n)
12+
13+
for (let i = 0; i < nums.length - 2; i++) { // O(n^2)
14+
if (i > 0 && nums[i - 1] === nums[i]) continue; // skip duplicates
15+
16+
let lo = i + 1;
17+
let hi = nums.length - 1;
18+
19+
while (lo < hi) {
20+
const sum = nums[i] + nums[lo] + nums[hi];
21+
if (sum === 0) {
22+
ans.push([nums[i], nums[lo], nums[hi]]);
23+
// console.log([nums[i], nums[lo], nums[hi]]);
24+
lo++;
25+
hi--;
26+
while (lo < hi && nums[lo - 1] === nums[lo]) lo++; // skip duplicates
27+
while (lo < hi && nums[hi + 1] === nums[hi]) hi--; // skip duplicates
28+
} else if (sum < 0) {
29+
lo++;
30+
while (lo < hi && nums[lo - 1] === nums[lo]) lo++; // skip duplicates
31+
} else {
32+
hi--;
33+
while (lo < hi && nums[hi + 1] === nums[hi]) hi--; // skip duplicates
34+
}
35+
}
36+
}
37+
38+
return ans;
39+
}
40+
41+
module.exports = threeSum;
42+
43+
// Given an array find the unique triplets elements that sum zero.
44+
45+
// Brute force: O(n^3)
46+
// Using twoSum: O(n^2)
47+
//

‎lab/exercises/10-mixed/3sum.spec.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const fn = require('./3sum');
2+
3+
describe('3 Sum', () => {
4+
it('should work', () => {
5+
expect(fn([-1, 0, 1, 2, -1, 4])).toEqual(expect.arrayContaining([
6+
expect.arrayContaining([-1, 0, 1]),
7+
expect.arrayContaining([-1, 2, -1]),
8+
]));
9+
});
10+
11+
it('should work', () => {
12+
const actual = fn([-2, 0, 1, 1, 2]);
13+
expect(actual.length).toEqual(2);
14+
expect(actual).toEqual(expect.arrayContaining([
15+
expect.arrayContaining([-2, 0, 2]),
16+
expect.arrayContaining([-2, 1, 1]),
17+
]));
18+
});
19+
20+
it('should work', () => {
21+
const actual = fn([-2, -4, -2, -2, 0, 1, 2, 2, 2, 3, 3, 4, 4, 6, 6]);
22+
expect(actual.length).toEqual(6);
23+
expect(actual).toEqual(expect.arrayContaining([
24+
expect.arrayContaining([-4, -2, 6]),
25+
expect.arrayContaining([-4, 0, 4]),
26+
expect.arrayContaining([-4, 1, 3]),
27+
expect.arrayContaining([-4, 2, 2]),
28+
expect.arrayContaining([-2, -2, 4]),
29+
expect.arrayContaining([-2, 0, 2]),
30+
]));
31+
});
32+
33+
it('should work with many zeros', () => {
34+
const actual = fn(Array(5).fill(0));
35+
expect(actual.length).toEqual(1);
36+
expect(JSON.stringify(actual)).toEqual('[[0,0,0]]'); // jest negative zero workaround
37+
});
38+
39+
it('should work with large arrays', () => {
40+
const actual = fn(Array(3000).fill(0));
41+
expect(actual.length).toEqual(1);
42+
expect(JSON.stringify(actual)).toEqual('[[0,0,0]]');
43+
});
44+
});
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
2+
/**
3+
* Add nodes and edges into a graph using adjacency list
4+
*
5+
* @class Graph
6+
*/
7+
class Graph {
8+
constructor() {
9+
this.nodes = new Map();
10+
}
11+
12+
// add node or directed edge
13+
add(node1, node2) {
14+
// console.log({node1, node2})
15+
const adj = this.nodes.has(node1) ? this.nodes.get(node1) : new Set();
16+
if (node2) adj.add(node2);
17+
this.nodes.set(node1, adj);
18+
}
19+
}
20+
21+
/**
22+
* DFS + tarjan for loop detection
23+
*
24+
* @param {Graph} g
25+
* @param {Map} node
26+
* @param {Set} set
27+
* @param {Map} [parent=null]
28+
* @param {Map} [grouping=new Map()]
29+
* @param {number} [depth=0]
30+
* @returns {boolean} true if has a loop, false otherwise
31+
*/
32+
function hasLoopOrAddToSet(g, node, set, parent = null, grouping = new Map(), depth = 0) {
33+
if (set.has(node)) set.delete(node);
34+
set.add(node);
35+
grouping.set(node, depth);
36+
37+
// console.log({node, adjs: g.nodes.get(node)});
38+
39+
for (const adj of g.nodes.get(node)) {
40+
// if (adj === parent) continue; // only for indirected graph
41+
42+
if (!grouping.has(adj)) {
43+
if (hasLoopOrAddToSet(g, adj, set, node, grouping, depth + 1)) return true;
44+
}
45+
46+
const minGroup = Math.min(grouping.get(adj), grouping.get(node));
47+
grouping.set(node, minGroup);
48+
49+
if (grouping.get(adj) === grouping.get(node)) return true;
50+
}
51+
}
52+
53+
54+
/**
55+
* Find the order of the alien alphabet given a list of words on lexicographical order.
56+
*
57+
* @param {string[]} words
58+
* @return {string} The alien alphabet order.
59+
*/
60+
function alienOrder(words) {
61+
const g = new Graph();
62+
if (!words || words.length < 2) return words && words.join('');
63+
64+
for (let i = 1; i < words.length; i++) { // O(n) * O(k)
65+
const w1 = words[i - 1];
66+
const w2 = words[i];
67+
let j = 0;
68+
69+
while (j < w1.length && j < w2.length && w1[j] === w2[j]) { // O(k), k = max word length
70+
g.add(w1[j++]);
71+
}
72+
73+
if (j === w2.length && w1.length > w2.length) {
74+
return ''; // shorter words should come first.
75+
}
76+
77+
if (w1[j]) g.add(w1[j], w2[j]);
78+
[...w1.slice(j)].forEach((n) => g.add(n));
79+
[...w2.slice(j)].forEach((n) => g.add(n));
80+
}
81+
82+
// console.log({ g: JSON.stringify(g) });
83+
// console.log({ g: g.nodes });
84+
85+
const set = new Set();
86+
for (const [node] of g.nodes) { // O(?)
87+
if (hasLoopOrAddToSet(g, node, set)) { // DFS: O(E + V), V: total unique letters
88+
return '';
89+
}
90+
}
91+
92+
return [...set].join('');
93+
}
94+
95+
module.exports = alienOrder;
96+
97+
// Find the order of the alien alphabet given a list of words on lexicographical order.
98+
99+
// take words in pair, and build a dependency graph.
100+
// skip while w1.char === w2.char
101+
// add the first diff chars as a adj nodes
102+
// add each letter as a node in the graph.
103+
104+
// find the order of the alien alph
105+
// iterate over each node
106+
// dfs + tarjan to detect loops
107+
// add each visited node to a set
108+
// if there’s a loop return ‘’
109+
// return set

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /