How would I apply these to my day-to-day work? (Click to expand)
- As a programmer, we have to solve problems every day. If you want to solve problems well, then it's good to know about a broad range of solutions. A lot of times, it's more efficient to learn existing resources than stumble upon the answer yourself. The more tools and practice you have, the better. This book helps you understand the tradeoffs among data structures and reason about algorithms performance.
+ As a programmer, we have to solve problems every day. If you want to solve problems well, it's good to know about a broad range of solutions. Often, it's more efficient to learn existing resources than stumble upon the answer yourself. The more tools and practice you have, the better. This book helps you understand the tradeoffs among data structures and reason about algorithms performance.
@@ -513,4 +510,4 @@ Reach out to me at one of the following places!
[](LICENSE)
-
+
diff --git a/benchmarks/array-rotation-implementations/01-array-rotation.js b/benchmarks/array-rotation-implementations/01-array-rotation.js
new file mode 100644
index 00000000..907357fd
--- /dev/null
+++ b/benchmarks/array-rotation-implementations/01-array-rotation.js
@@ -0,0 +1,9 @@
+// brute force: O(nd) | O(1)
+function rotLeft(a, d) {
+ for (let i = 0; i < d; i++) { // O(d)
+ a.push(a.shift()); // O(n), shift O(n)
+ }
+ return a;
+}
+
+module.exports = rotLeft;
diff --git a/benchmarks/array-rotation-implementations/01a-array-rotation.js b/benchmarks/array-rotation-implementations/01a-array-rotation.js
new file mode 100644
index 00000000..2053166f
--- /dev/null
+++ b/benchmarks/array-rotation-implementations/01a-array-rotation.js
@@ -0,0 +1,11 @@
+// modulus for rotations: O(n^2) | O(1)
+function rotLeft(a, d) {
+ const len = a.length;
+ const rot = d % len;
+ for (let i = 0; i < rot; i++) { // O(n^2)
+ a.push(a.shift()); // O(n)
+ }
+ return a;
+}
+
+module.exports = rotLeft;
diff --git a/benchmarks/array-rotation-implementations/02-array-rotation.js b/benchmarks/array-rotation-implementations/02-array-rotation.js
new file mode 100644
index 00000000..321d545e
--- /dev/null
+++ b/benchmarks/array-rotation-implementations/02-array-rotation.js
@@ -0,0 +1,12 @@
+// additional space: O(n) | O(n)
+function rotLeft(a, d) {
+ const len = a.length;
+ const rot = d % len;
+ const b = [];
+ for (let i = 0; i < len; i++) { // O(n)
+ b[i] = a[(rot + i) % len]; // O(1)
+ }
+ return b;
+}
+
+module.exports = rotLeft;
diff --git a/benchmarks/array-rotation-implementations/array-rotation.spec.js b/benchmarks/array-rotation-implementations/array-rotation.spec.js
new file mode 100644
index 00000000..2ff3b80c
--- /dev/null
+++ b/benchmarks/array-rotation-implementations/array-rotation.spec.js
@@ -0,0 +1,17 @@
+/*eslint-disable */
+// npx jest benchmarks/two-sum-implementations/two-sum.spec.js --watch --collectCoverage
+const implementations = [
+ { name: 1, fn: require('./01-array-rotation') },
+ { name: '1a', fn: require('./01a-array-rotation') },
+ { name: 2, fn: require('./02-array-rotation') },
+];
+
+implementations.forEach(({name, fn}) => {
+ describe(`Two Sum: ${name}`, () => {
+ it('should work on worst case', () => {
+ const rots = 1000;
+ const array = [1, 2, 3];
+ expect(fn(array, rots)).toEqual([2,3,1]);
+ });
+ });
+});
diff --git a/benchmarks/two-sum-implementations/01a-two-sum.js b/benchmarks/two-sum-implementations/01a-two-sum.js
new file mode 100644
index 00000000..47d10818
--- /dev/null
+++ b/benchmarks/two-sum-implementations/01a-two-sum.js
@@ -0,0 +1,13 @@
+// Brute force: O(n^2) | O(1)
+function twoSum(nums, target) {
+ for (let i = 0; i < nums.length - 1; i++) { // O(n^2)
+ const diff = target - nums[i];
+ const offset = i + 1;
+ const idx = nums.slice(offset).findIndex((n) => n === diff); // O(n)
+ const j = offset + idx;
+ if (idx > -1) return [i, j];
+ }
+ return [];
+}
+
+module.exports = twoSum;
diff --git a/benchmarks/two-sum-implementations/02-two-sum.js b/benchmarks/two-sum-implementations/02-two-sum.js
index 9703e670..602d37d7 100644
--- a/benchmarks/two-sum-implementations/02-two-sum.js
+++ b/benchmarks/two-sum-implementations/02-two-sum.js
@@ -1,4 +1,4 @@
-// With a HashMap: O(n) | O(n)
+// [map w/dups handling] → O(n^2) | O(n)
function twoSum(nums, target) {
const map = nums.reduce((m, v, i) => { // O(n)
const ids = m.get(v) || [];
@@ -8,8 +8,9 @@ function twoSum(nums, target) {
for (let i = 0; i < nums.length; i++) { // O(n)
const diff = target - nums[i];
- if (map.has(diff) && i !== map.get(diff)) {
- return [i, map.get(diff)];
+ if (map.has(diff)) {
+ const id = map.get(diff).find((j) => j > i);
+ if (id > -1) return [i, id];
}
}
diff --git a/benchmarks/two-sum-implementations/02a-two-sum.js b/benchmarks/two-sum-implementations/02a-two-sum.js
new file mode 100644
index 00000000..68b411d5
--- /dev/null
+++ b/benchmarks/two-sum-implementations/02a-two-sum.js
@@ -0,0 +1,24 @@
+// [map w/dups handling] → O(n^2) | O(n)
+function twoSum(nums, target) {
+ const map = mapify(nums);
+
+ for (let i = 0; i < nums.length; i++) { // O(n)
+ const diff = target - nums[i];
+ if (map.has(diff)) {
+ const id = map.get(diff).find((j) => j > i);
+ if (id > -1) return [i, id];
+ }
+ }
+
+ return [];
+}
+
+function mapify(nums) {
+ return nums.reduce((m, v, i) => { // O(n)
+ const ids = m.get(v) || [];
+ ids.push(i);
+ return m.set(v, ids);
+ }, new Map());
+}
+
+module.exports = twoSum;
diff --git a/benchmarks/two-sum-implementations/04-two-sum.js b/benchmarks/two-sum-implementations/04-two-sum.js
new file mode 100644
index 00000000..45b0f7c9
--- /dev/null
+++ b/benchmarks/two-sum-implementations/04-two-sum.js
@@ -0,0 +1,23 @@
+// sort + two pointers: O(n log n) | O(1)
+function twoSum(nums, target) {
+ nums.sort((a, b) => a - b);
+
+ let lo = 0;
+ let hi = nums.length - 1;
+
+ while (lo < hi) {
+ const sum = nums[lo] + nums[hi];
+ if (sum === target) {
+ return [lo, hi];
+ }
+
+ if (sum > target) {
+ hi--;
+ } else {
+ lo++;
+ }
+ }
+ return [];
+}
+
+module.exports = twoSum;
diff --git a/benchmarks/two-sum-implementations/two-sum.spec.js b/benchmarks/two-sum-implementations/two-sum.spec.js
new file mode 100644
index 00000000..c7f5fefb
--- /dev/null
+++ b/benchmarks/two-sum-implementations/two-sum.spec.js
@@ -0,0 +1,23 @@
+/*eslint-disable */
+// npx jest benchmarks/two-sum-implementations/two-sum.spec.js --watch --collectCoverage
+const implementations = [
+ { name: 1, fn: require('./01-two-sum') },
+ { name: '1a', fn: require('./01a-two-sum') },
+ { name: 2, fn: require('./02-two-sum') },
+ { name: '2a', fn: require('./02a-two-sum') },
+ { name: 3, fn: require('./03-two-sum') },
+ { name: 4, fn: require('./04-two-sum') },
+];
+
+implementations.forEach(({name, fn}) => {
+ describe(`Two Sum: ${name}`, () => {
+ xit('should work', () => {
+ expect(fn([1, 2, 3].concat(Array(1e2 - 3).fill(7)), 4)).toEqual([0, 2]);
+ });
+
+ it('should work on worst case', () => {
+ const size = 100;
+ expect(fn([...Array(size).fill(2), 3, 3 * size * 10], 3 * size * 10 + 3)).toEqual([size, size + 1]);
+ });
+ });
+});
diff --git a/book/A-time-complexity-cheatsheet.asc b/book/A-time-complexity-cheatsheet.asc
index 64400aaa..b4089138 100644
--- a/book/A-time-complexity-cheatsheet.asc
+++ b/book/A-time-complexity-cheatsheet.asc
@@ -1,6 +1,5 @@
[appendix]
-[[a-time-complexity-cheatsheet]]
-== Cheatsheet
+== Cheatsheet [[a-time-complexity-cheatsheet]]
This section summerize what we are going to cover in the rest of this book.
diff --git a/book/B-self-balancing-binary-search-trees.asc b/book/B-self-balancing-binary-search-trees.asc
index 249f510c..d099b8b1 100644
--- a/book/B-self-balancing-binary-search-trees.asc
+++ b/book/B-self-balancing-binary-search-trees.asc
@@ -1,6 +1,5 @@
[appendix]
-[[b-self-balancing-binary-search-trees]]
-== Self-balancing Binary Search Trees
+== Self-balancing Binary Search Trees [[b-self-balancing-binary-search-trees]]
Binary Search Trees (BST) are an excellent data structure to find elements very fast _O(log n)_.
However, when the BST branches have different branch sizes, then the performance suffers.
@@ -28,8 +27,7 @@ As you might notice, we balanced the tree in the example by doing a rotation.
To be more specific we rotated node `1` to the left to balance the tree.
Let's examine all the possible rotation we can do to balance a tree.
-[[tree-rotations]]
-=== Tree Rotations
+=== Tree Rotations [[tree-rotations]]
(((Tree Rotations)))
We can do single rotations left and right and also we can do double rotations.
Let's go one by one.
@@ -38,8 +36,8 @@ Let's go one by one.
Right rotation moves a node on the right as a child of another node.
-Take a look at the `@example` in the code below.
-As you can see we have an unbalanced tree `4-3-2-1`.
+Take a look at the examples in the code in the next section.
+As you will see we have an unbalanced tree `4-3-2-1`.
We want to balance the tree, for that we need to do a right rotation of node 3.
So, we move node 3 as the right child of the previous child.
@@ -142,4 +140,3 @@ This rotation is also referred to as `RL rotation`.
=== Self-balancing trees implementations
So far, we have study how to make tree rotations which are the basis for self-balancing trees. There are different implementations of self-balancing trees such a Red-Black Tree and AVL Tree.
-
diff --git a/book/C-AVL-tree.asc b/book/C-AVL-tree.asc
index 07bae068..1a8afe83 100644
--- a/book/C-AVL-tree.asc
+++ b/book/C-AVL-tree.asc
@@ -1,6 +1,5 @@
[appendix]
-[[c-avl-tree]]
-== AVL Tree
+== AVL Tree [[c-avl-tree]]
(((AVL Tree)))
(((Tree, AVL)))
AVL Tree is named after their inventors (**A**delson-**V**elsky and **L**andis).
@@ -60,4 +59,4 @@ include::../src/data-structures/trees/avl-tree.js[tag=balance]
The first thing we do is to see if one subtree is longer than the other.
If so, then we check the children balance to determine if need a single or double rotation and in which direction.
-You can review <