Skip to content

Commit ad9848e

Browse files
authored
Merge branch 'TheAlgorithms:master' into add-ugly
2 parents e50adb0 + db5510d commit ad9848e

23 files changed

+630
-3
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* This generates an array of unique sub"sets" (represented by ascendingly sorted subarrays)
3+
* of size k out of n+1 numbers from 1 to n.
4+
*
5+
* By using a backtracking algorithm we can incrementally build sub"sets" while dropping candidates
6+
* that cannot contribute anymore to a valid solution.
7+
* Steps:
8+
* - From the starting number (i.e. "1") generate all combinations of k numbers.
9+
* - Once we got all combinations for the given number we can discard it (“backtracks”)
10+
* and repeat the same process for the next number.
11+
*/
12+
export function generateCombinations(n: number, k: number): number[][] {
13+
let combinationsAcc: number[][] = [];
14+
let currentCombination: number[] = [];
15+
16+
function generateAllCombos(
17+
n: number,
18+
k: number,
19+
startCursor: number
20+
): number[][] {
21+
if (k === 0) {
22+
if (currentCombination.length > 0) {
23+
combinationsAcc.push(currentCombination.slice());
24+
}
25+
return combinationsAcc;
26+
}
27+
28+
const endCursor = n - k + 2;
29+
for (let i = startCursor; i < endCursor; i++) {
30+
currentCombination.push(i);
31+
generateAllCombos(n, k - 1, i + 1);
32+
currentCombination.pop();
33+
}
34+
return combinationsAcc;
35+
}
36+
37+
return generateAllCombos(n, k, 1);
38+
}

backtracking/generateParentheses.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Given a number n pairs of parentheses, generate all combinations of valid parentheses
3+
* @param {number} n: Number of given parentheses
4+
* @return {string[]} result: Array that contains all valid parentheses
5+
* @see https://leetcode.com/problems/generate-parentheses/
6+
*/
7+
8+
const generateParentheses = (n: number): string[] => {
9+
const result: string[] = [];
10+
11+
const solve = (chars: string, openParentheses: number, closedParentheses: number) => {
12+
if (openParentheses === n && closedParentheses === n) {
13+
result.push(chars);
14+
return;
15+
}
16+
17+
if (openParentheses <= n) {
18+
solve(chars + "(", openParentheses + 1, closedParentheses);
19+
}
20+
21+
if (closedParentheses < openParentheses) {
22+
solve(chars + ")", openParentheses, closedParentheses + 1);
23+
}
24+
};
25+
26+
solve("", 0, 0);
27+
28+
return result;
29+
};
30+
31+
export { generateParentheses };
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { generateCombinations } from "../all-combinations-of-size-k";
2+
3+
const cases = [
4+
[
5+
3,
6+
2,
7+
[
8+
[1, 2],
9+
[1, 3],
10+
[2, 3],
11+
],
12+
],
13+
[4, 2, [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]],
14+
[0, 0, []],
15+
[2, 3, []],
16+
] as const;
17+
18+
describe("AllCombinationsOfSizeK", () => {
19+
it.each(cases)(
20+
"create all combinations given n=%p and k=%p",
21+
(n, k, expectedCombos) => {
22+
const combinations = generateCombinations(n, k);
23+
expect(combinations).toEqual(expectedCombos);
24+
}
25+
);
26+
});
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { generateParentheses } from "../generateParentheses";
2+
3+
const cases: [number, string[]][] = [
4+
[0, [""]],
5+
[1, ["()"]],
6+
[2, ["(())", "()()"]],
7+
[3, ["((()))", "(()())", "(())()", "()(())", "()()()"]],
8+
[
9+
4,
10+
[
11+
"(((())))",
12+
"((()()))",
13+
"((())())",
14+
"((()))()",
15+
"(()(()))",
16+
"(()()())",
17+
"(()())()",
18+
"(())(())",
19+
"(())()()",
20+
"()((()))",
21+
"()(()())",
22+
"()(())()",
23+
"()()(())",
24+
"()()()()",
25+
],
26+
],
27+
[
28+
5,
29+
[
30+
"((((()))))",
31+
"(((()())))",
32+
"(((())()))",
33+
"(((()))())",
34+
"(((())))()",
35+
"((()(())))",
36+
"((()()()))",
37+
"((()())())",
38+
"((()()))()",
39+
"((())(()))",
40+
"((())()())",
41+
"((())())()",
42+
"((()))(())",
43+
"((()))()()",
44+
"(()((())))",
45+
"(()(()()))",
46+
"(()(())())",
47+
"(()(()))()",
48+
"(()()(()))",
49+
"(()()()())",
50+
"(()()())()",
51+
"(()())(())",
52+
"(()())()()",
53+
"(())((()))",
54+
"(())(()())",
55+
"(())(())()",
56+
"(())()(())",
57+
"(())()()()",
58+
"()(((())))",
59+
"()((()()))",
60+
"()((())())",
61+
"()((()))()",
62+
"()(()(()))",
63+
"()(()()())",
64+
"()(()())()",
65+
"()(())(())",
66+
"()(())()()",
67+
"()()((()))",
68+
"()()(()())",
69+
"()()(())()",
70+
"()()()(())",
71+
"()()()()()",
72+
],
73+
],
74+
];
75+
76+
describe("Generate Parentheses", () => {
77+
test.each(cases)(
78+
"generate all valid parentheses of input %n",
79+
(n: number, expected: string[]) => {
80+
expect(generateParentheses(n)).toStrictEqual(expected);
81+
}
82+
);
83+
});

bit_manipulation/is_power_of_2.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* This code will check whether the given number is a power of two or not.
3+
* @author dev-madhurendra<https://github.com/dev-madhurendra>
4+
* @explanation
5+
6+
A number will be a power of two if only one bit is set and rest are unset.
7+
This is true for all the cases except 01 because (2^0 = 1) which is not a power of 2.
8+
For eg: 10 (2^1 = 2), 100 (2^2 = 4), 10000 (2^4 = 16)
9+
10+
@see: https://www.hackerearth.com/practice/notes/round-a-number-to-the-next-power-of-2/
11+
12+
If we will subtract 1 from a number that is a power of 2 we will get it's
13+
1's complement.And we know that 1's complement is just opp. of that number.
14+
So, (n & (n-1)) will be 0.
15+
16+
For eg: (1000 & (1000-1))
17+
1 0 0 0 // Original Number (8)
18+
0 1 1 1 // After Subtracting 1 (8-1 = 7)
19+
_______
20+
0 0 0 0 // will become 0
21+
* @param {number}
22+
* @returns {boolean}
23+
*/
24+
25+
export const isPowerOfTwo = (n: number): boolean => n > 0 && (n & (n - 1)) === 0

bit_manipulation/is_power_of_4.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @author : dev-madhurendra<https://github.com/dev-madhurendra>
3+
* Checks whether the given number is a power of four or not.
4+
*
5+
* A number is considered a power of four if and only if there is a single '1' bit in its binary representation,
6+
* and that '1' bit is at the first position, followed by an even number of '0' bits.
7+
*
8+
* @param {number} n - The input number to check.
9+
* @returns {boolean} True if the number is a power of four, false otherwise.
10+
*
11+
* @example
12+
* const result = isPowerOfFour(16); // Returns true (16 is 4^2)
13+
* const result2 = isPowerOfFour(5); // Returns false (5 is not a power of four)
14+
*/
15+
export const isPowerOfFour = (n: number): boolean => ((n > 0) && ((n & n - 1) === 0) && (n % 3 === 1))
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { isPowerOfTwo } from "../is_power_of_2"
2+
3+
4+
describe('IsPowerOfTwo' , () => {
5+
it.each([
6+
[0, false],
7+
[1, true],
8+
[4, true],
9+
[1024, true],
10+
[1025, false],
11+
])('Check if %i is a power of 2 or not', (number, expected) => {
12+
expect(isPowerOfTwo(number)).toBe(expected);
13+
});
14+
})
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { isPowerOfFour } from "../is_power_of_4"
2+
3+
describe('IsPowerOfFour', () => {
4+
it.each([
5+
[0, false],
6+
[4, true],
7+
[16, true],
8+
[12, false],
9+
[64, true],
10+
[-64, false]
11+
])('should return the number %i is power of four or not', (n, expected) => {
12+
expect(isPowerOfFour(n)).toBe(expected)
13+
})
14+
})
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Trie } from "../tries";
2+
3+
describe('Trie', () => {
4+
let trie: Trie;
5+
6+
beforeEach(() => {
7+
trie = new Trie();
8+
});
9+
10+
it('should add and find a word', () => {
11+
trie.add('apple');
12+
expect(trie.find('apple')).toBe(true);
13+
});
14+
15+
it('should not find a word that was not added', () => {
16+
trie.add('apple');
17+
expect(trie.find('banana')).toBe(false);
18+
});
19+
20+
it('should not find a partial word', () => {
21+
trie.add('apple');
22+
expect(trie.find('app')).toBe(false);
23+
});
24+
25+
it('should add and find multiple words', () => {
26+
trie.add('apple');
27+
trie.add('banana');
28+
trie.add('cherry');
29+
expect(trie.find('apple')).toBe(true);
30+
expect(trie.find('banana')).toBe(true);
31+
expect(trie.find('cherry')).toBe(true);
32+
});
33+
34+
it('should find words with a common prefix', () => {
35+
trie.add('apple');
36+
trie.add('appetizer');
37+
expect(trie.find('app', true)).toBe(true);
38+
expect(trie.find('app', false)).toBe(false);
39+
});
40+
});

data_structures/tries/tries.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* Represents a node in a Trie data structure.
3+
*/
4+
class TrieNode {
5+
/**
6+
* An object that stores child nodes for each character in the alphabet.
7+
*/
8+
children: { [key: string]: TrieNode } = {};
9+
10+
/**
11+
* Indicates whether the node represents the end of a word.
12+
*/
13+
isWord: boolean = false;
14+
}
15+
16+
/**
17+
* Trie Data structure for storing and searching words.
18+
*/
19+
export class Trie {
20+
/**
21+
* The root node of the Trie.
22+
*/
23+
root: TrieNode = new TrieNode();
24+
25+
/**
26+
* Creates a new Trie instance.
27+
*/
28+
constructor() {}
29+
30+
/**
31+
* Inserts a word into the Trie.
32+
*
33+
* @param word - The word to insert into the Trie.
34+
*/
35+
private insertNode(node: TrieNode, word: string): void {
36+
for (const char of word) {
37+
if (!node.children[char]) {
38+
node.children[char] = new TrieNode();
39+
}
40+
node = node.children[char];
41+
}
42+
node.isWord = true;
43+
}
44+
45+
/**
46+
* Searches for a word in the Trie.
47+
*
48+
* @param word - The word to search for.
49+
* @param isPrefixMatch - Indicates whether to perform a prefix match (default: false).
50+
* If true, the method returns true if the Trie contains words with the specified prefix.
51+
* If false, the method returns true only if an exact match is found.
52+
* @returns True if the word (or prefix) is found in the Trie; otherwise, false.
53+
*/
54+
public find(word: string, isPrefixMatch: boolean = false): boolean {
55+
return this.searchNode(this.root, word, isPrefixMatch);
56+
}
57+
58+
/**
59+
* Adds a word to the Trie.
60+
*
61+
* @param word - The word to add to the Trie.
62+
* @returns The Trie instance, allowing for method chaining.
63+
*/
64+
public add(word: string): this {
65+
this.insertNode(this.root, word);
66+
return this;
67+
}
68+
69+
/**
70+
* Searches for a word in the Trie.
71+
*
72+
* @param node - The current Trie node being examined.
73+
* @param word - The word to search for.
74+
* @param prefixMatch - Indicates whether to perform a prefix match.
75+
* @returns True if the word (or prefix) is found in the Trie; otherwise, false.
76+
* @private
77+
*/
78+
private searchNode(
79+
node: TrieNode,
80+
word: string,
81+
prefixMatch: boolean
82+
): boolean {
83+
for (const char of word) {
84+
if (!node.children[char]) {
85+
return false;
86+
}
87+
node = node.children[char];
88+
}
89+
return prefixMatch || node.isWord;
90+
}
91+
}

0 commit comments

Comments
 (0)