Skip to content

Commit 86bd34f

Browse files
authored
Merge pull request #12 from trekhleb/master
Add iterative version of Euclidean algorithm
2 parents 3feffd3 + afa4948 commit 86bd34f

File tree

11 files changed

+155
-41
lines changed

11 files changed

+155
-41
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
**Contributing New Translation**
1010

1111
- Create new `README.xx-XX.md` file with translation alongside with
12-
main `README.md` file where `xx-XX` is locale and country/region codes.
12+
main `README.md` file where `xx-XX` is [locale and country/region codes](http://www.lingoes.net/en/translator/langcode.htm).
1313
For example `en-US`, `zh-CN`, `zh-TW`, `ko-KR` etc.
1414
- You may also translate all other sub-folders by creating
1515
related `README.xx-XX.md` files in each of them.

README.zh-CN.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ _Read this in other languages:_
5151
* `B` [阶乘](src/algorithms/math/factorial)
5252
* `B` [斐波那契数](src/algorithms/math/fibonacci)
5353
* `B` [素数检测](src/algorithms/math/primality-test) (排除法)
54-
* `B` [欧几里得算法](src/algorithms/math/euclidean-algorithm) - 计算最大公约数 (GCD)
54+
* `B` [欧几里得算法](src/algorithms/math/euclidean-algorithm) - 计算最大公约数 (GCD)
5555
* `B` [最小公倍数](src/algorithms/math/least-common-multiple) (LCM)
5656
* `B` [素数筛](src/algorithms/math/sieve-of-eratosthenes) - 查找所有素数达到任何给定限制
57-
* `B` [判断2次方数](src/algorithms/math/is-power-of-two) - 检查数字是否为2的幂 (原生和按位算法)
57+
* `B` [判断2次方数](src/algorithms/math/is-power-of-two) - 检查数字是否为2的幂 (原生和按位算法)
5858
* `B` [杨辉三角形](src/algorithms/math/pascal-triangle)
5959
* `A` [整数拆分](src/algorithms/math/integer-partition)
6060
* `A` [割圆术](src/algorithms/math/liu-hui) - 基于N-gons的近似π计算
@@ -73,7 +73,7 @@ _Read this in other languages:_
7373
* **字符串**
7474
* `A` [莱温斯坦距离](src/algorithms/string/levenshtein-distance) - 两个序列之间的最小编辑距离
7575
* `B` [汉明距离](src/algorithms/string/hamming-distance) - 符号不同的位置数
76-
* `A` [克努斯-莫里斯-普拉特算法](src/algorithms/string/knuth-morris-pratt) - 子串搜索
76+
* `A` [KMP算法](src/algorithms/string/knuth-morris-pratt) (克努斯-莫里斯-普拉特算法) - 子串搜索 (模式匹配)
7777
* `A` [字符串快速查找](src/algorithms/string/rabin-karp) - 子串搜索
7878
* `A` [最长公共子串](src/algorithms/string/longest-common-substring)
7979
* `A` [正则表达式匹配](src/algorithms/string/regular-expression-matching)
@@ -101,11 +101,11 @@ _Read this in other languages:_
101101
* `A` [戴克斯特拉算法](src/algorithms/graph/dijkstra) - 找到图中所有顶点的最短路径
102102
* `A` [贝尔曼-福特算法](src/algorithms/graph/bellman-ford) - 找到图中所有顶点的最短路径
103103
* `A` [弗洛伊德算法](src/algorithms/graph/floyd-warshall) - 找到所有顶点对 之间的最短路径
104-
* `A` [判圈算法](src/algorithms/graph/detect-cycle) - 对于有向图和无向图 (基于DFS和不相交集的版本)
105-
* `A` [普林演算法](src/algorithms/graph/prim) - 寻找加权无向图的最小生成树 (MST)
106-
* `B` [克鲁斯克尔演算法](src/algorithms/graph/kruskal) - 寻找加权无向图的最小生成树 (MST)
104+
* `A` [判圈算法](src/algorithms/graph/detect-cycle) - 对于有向图和无向图 (基于DFS和不相交集的版本)
105+
* `A` [普林演算法](src/algorithms/graph/prim) - 寻找加权无向图的最小生成树 (MST)
106+
* `B` [克鲁斯克尔演算法](src/algorithms/graph/kruskal) - 寻找加权无向图的最小生成树 (MST)
107107
* `A` [拓扑排序](src/algorithms/graph/topological-sorting) - DFS 方法
108-
* `A` [关节点](src/algorithms/graph/articulation-points) - Tarjan算法 (基于DFS)
108+
* `A` [关节点](src/algorithms/graph/articulation-points) - Tarjan算法 (基于DFS)
109109
* `A` [](src/algorithms/graph/bridges) - 基于DFS的算法
110110
* `A` [欧拉回径与一笔画问题](src/algorithms/graph/eulerian-path) - Fleury的算法 - 一次访问每个边
111111
* `A` [哈密顿图](src/algorithms/graph/hamiltonian-cycle) - 恰好访问每个顶点一次
@@ -116,7 +116,7 @@ _Read this in other languages:_
116116
* `B` [旋转矩阵](src/algorithms/uncategorized/square-matrix-rotation) - 原地算法
117117
* `B` [跳跃 游戏](src/algorithms/uncategorized/jump-game) - 回溯, 动态编程 (自上而下+自下而上) 和贪婪的例子
118118
* `B` [独特(唯一) 路径](src/algorithms/uncategorized/unique-paths) - 回溯, 动态编程和基于Pascal三角形的例子
119-
* `B` [雨水收集](src/algorithms/uncategorized/rain-terraces) - 诱捕雨水问题 (动态编程和暴力版本)
119+
* `B` [雨水收集](src/algorithms/uncategorized/rain-terraces) - 诱捕雨水问题 (动态编程和暴力版本)
120120
* `A` [八皇后问题](src/algorithms/uncategorized/n-queens)
121121
* `A` [骑士巡逻](src/algorithms/uncategorized/knight-tour)
122122

src/algorithms/math/euclidean-algorithm/__test__/euclieanAlgorithm.test.js renamed to src/algorithms/math/euclidean-algorithm/__test__/euclideanAlgorithm.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import euclideanAlgorithm from '../euclideanAlgorithm';
22

33
describe('euclideanAlgorithm', () => {
4-
it('should calculate GCD', () => {
4+
it('should calculate GCD recursively', () => {
55
expect(euclideanAlgorithm(0, 0)).toBe(0);
66
expect(euclideanAlgorithm(2, 0)).toBe(2);
77
expect(euclideanAlgorithm(0, 2)).toBe(2);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import euclideanAlgorithmIterative from '../euclideanAlgorithmIterative';
2+
3+
describe('euclideanAlgorithmIterative', () => {
4+
it('should calculate GCD iteratively', () => {
5+
expect(euclideanAlgorithmIterative(0, 0)).toBe(0);
6+
expect(euclideanAlgorithmIterative(2, 0)).toBe(2);
7+
expect(euclideanAlgorithmIterative(0, 2)).toBe(2);
8+
expect(euclideanAlgorithmIterative(1, 2)).toBe(1);
9+
expect(euclideanAlgorithmIterative(2, 1)).toBe(1);
10+
expect(euclideanAlgorithmIterative(6, 6)).toBe(6);
11+
expect(euclideanAlgorithmIterative(2, 4)).toBe(2);
12+
expect(euclideanAlgorithmIterative(4, 2)).toBe(2);
13+
expect(euclideanAlgorithmIterative(12, 4)).toBe(4);
14+
expect(euclideanAlgorithmIterative(4, 12)).toBe(4);
15+
expect(euclideanAlgorithmIterative(5, 13)).toBe(1);
16+
expect(euclideanAlgorithmIterative(27, 13)).toBe(1);
17+
expect(euclideanAlgorithmIterative(24, 60)).toBe(12);
18+
expect(euclideanAlgorithmIterative(60, 24)).toBe(12);
19+
expect(euclideanAlgorithmIterative(252, 105)).toBe(21);
20+
expect(euclideanAlgorithmIterative(105, 252)).toBe(21);
21+
expect(euclideanAlgorithmIterative(1071, 462)).toBe(21);
22+
expect(euclideanAlgorithmIterative(462, 1071)).toBe(21);
23+
expect(euclideanAlgorithmIterative(462, -1071)).toBe(21);
24+
expect(euclideanAlgorithmIterative(-462, -1071)).toBe(21);
25+
});
26+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
/**
2+
* Recursive version of Euclidean Algorithm of finding greatest common divisor (GCD).
23
* @param {number} originalA
34
* @param {number} originalB
45
* @return {number}
56
*/
67
export default function euclideanAlgorithm(originalA, originalB) {
8+
// Make input numbers positive.
79
const a = Math.abs(originalA);
810
const b = Math.abs(originalB);
911

12+
// To make algorithm work faster instead of subtracting one number from the other
13+
// we may use modulo operation.
1014
return (b === 0) ? a : euclideanAlgorithm(b, a % b);
1115
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Iterative version of Euclidean Algorithm of finding greatest common divisor (GCD).
3+
* @param {number} originalA
4+
* @param {number} originalB
5+
* @return {number}
6+
*/
7+
export default function euclideanAlgorithmIterative(originalA, originalB) {
8+
// Make input numbers positive.
9+
let a = Math.abs(originalA);
10+
let b = Math.abs(originalB);
11+
12+
// Subtract one number from another until both numbers would become the same.
13+
// This will be out GCD. Also quit the loop if one of the numbers is zero.
14+
while (a && b && a !== b) {
15+
[a, b] = a > b ? [a - b, b] : [a, b - a];
16+
}
17+
18+
// Return the number that is not equal to zero since the last subtraction (it will be a GCD).
19+
return a || b;
20+
}

src/algorithms/string/levenshtein-distance/README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ to assist natural language translation based on translation memory.
5050

5151
Let’s take a simple example of finding minimum edit distance between
5252
strings `ME` and `MY`. Intuitively you already know that minimum edit distance
53-
here is `1` operation and this operation. And it is a replacing `E` with `Y`. But
53+
here is `1` operation and this operation. And it is replacing `E` with `Y`. But
5454
let’s try to formalize it in a form of the algorithm in order to be able to
5555
do more complex examples like transforming `Saturday` into `Sunday`.
5656

@@ -62,7 +62,7 @@ is being calculated based on three previously possible transformations.
6262

6363
To explain this further let’s draw the following matrix:
6464

65-
![Levenshtein Matrix](https://cdn-images-1.medium.com/max/1600/1*2d46ug_PL5LfeqztckoYGw.jpeg)
65+
![Levenshtein Matrix](https://cdn-images-1.medium.com/max/1600/1*aTunSUoy0BJyYBVn4tWGrA.png)
6666

6767
- Cell `(0:1)` contains red number 1. It means that we need 1 operation to
6868
transform `M` to an empty string. And it is by deleting `M`. This is why this number is red.
@@ -75,12 +75,12 @@ to transform an empty string to `MY`. And it is by inserting `Y` and `M`.
7575
- Cell `(1:1)` contains number 0. It means that it costs nothing
7676
to transform `M` into `M`.
7777
- Cell `(1:2)` contains red number 1. It means that we need 1 operation
78-
to transform `ME` to `M`. And it is be deleting `E`.
78+
to transform `ME` to `M`. And it is by deleting `E`.
7979
- And so on...
8080

8181
This looks easy for such small matrix as ours (it is only `3x3`). But here you
8282
may find basic concepts that may be applied to calculate all those numbers for
83-
bigger matrices (let’s say `9x7` one, for `Saturday → Sunday` transformation).
83+
bigger matrices (let’s say a `9x7` matrix for `Saturday → Sunday` transformation).
8484

8585
According to the formula you only need three adjacent cells `(i-1:j)`, `(i-1:j-1)`, and `(i:j-1)` to
8686
calculate the number for current cell `(i:j)`. All we need to do is to find the
@@ -89,24 +89,24 @@ letters in `i`'s row and `j`'s column.
8989

9090
You may clearly see the recursive nature of the problem.
9191

92-
![Levenshtein Matrix](https://cdn-images-1.medium.com/max/2000/1*JdHQ5TeKiDlE-iKK1s_2vw.jpeg)
92+
![Levenshtein Matrix](https://cdn-images-1.medium.com/max/1600/1*w8UB4DSvBnAK6mBXRGQDjw.png)
9393

9494
Let's draw a decision graph for this problem.
9595

96-
![Minimum Edit Distance Decision Graph](https://cdn-images-1.medium.com/max/1600/1*SGwYUpXH9H1xUeTvJk0e7Q.jpeg)
96+
![Minimum Edit Distance Decision Graph](https://cdn-images-1.medium.com/max/1600/1*8jD0qvr5B9PwRFM_9z7q9A.png)
9797

9898
You may see a number of overlapping sub-problems on the picture that are marked
9999
with red. Also there is no way to reduce the number of operations and make it
100-
less then a minimum of those three adjacent cells from the formula.
100+
less than a minimum of those three adjacent cells from the formula.
101101

102102
Also you may notice that each cell number in the matrix is being calculated
103103
based on previous ones. Thus the tabulation technique (filling the cache in
104104
bottom-up direction) is being applied here.
105105

106-
Applying this principles further we may solve more complicated cases like
106+
Applying this principle further we may solve more complicated cases like
107107
with `Saturday → Sunday` transformation.
108108

109-
![Levenshtein distance](https://cdn-images-1.medium.com/max/1600/1*fPEHiImYLKxSTUhrGbYq3g.jpeg)
109+
![Levenshtein distance](https://cdn-images-1.medium.com/max/1600/1*geMdmZcdU1bZbHIoh6KO3Q.png)
110110

111111
## References
112112

src/data-structures/stack/Stack.js

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ export default class Stack {
44
constructor() {
55
// We're going to implement Stack based on LinkedList since these
66
// structures are quite similar. Compare push/pop operations of the Stack
7-
// with append/deleteTail operations of LinkedList.
7+
// with prepend/deleteHead operations of LinkedList.
88
this.linkedList = new LinkedList();
99
}
1010

1111
/**
1212
* @return {boolean}
1313
*/
1414
isEmpty() {
15-
// The stack is empty if its linked list doesn't have a tail.
16-
return !this.linkedList.tail;
15+
// The stack is empty if its linked list doesn't have a head.
16+
return !this.linkedList.head;
1717
}
1818

1919
/**
@@ -25,27 +25,27 @@ export default class Stack {
2525
return null;
2626
}
2727

28-
// Just read the value from the end of linked list without deleting it.
29-
return this.linkedList.tail.value;
28+
// Just read the value from the start of linked list without deleting it.
29+
return this.linkedList.head.value;
3030
}
3131

3232
/**
3333
* @param {*} value
3434
*/
3535
push(value) {
3636
// Pushing means to lay the value on top of the stack. Therefore let's just add
37-
// the new value at the end of the linked list.
38-
this.linkedList.append(value);
37+
// the new value at the start of the linked list.
38+
this.linkedList.prepend(value);
3939
}
4040

4141
/**
4242
* @return {*}
4343
*/
4444
pop() {
45-
// Let's try to delete the last node (the tail) from the linked list.
46-
// If there is no tail (the linked list is empty) just return null.
47-
const removedTail = this.linkedList.deleteTail();
48-
return removedTail ? removedTail.value : null;
45+
// Let's try to delete the first node (the head) from the linked list.
46+
// If there is no head (the linked list is empty) just return null.
47+
const removedHead = this.linkedList.deleteHead();
48+
return removedHead ? removedHead.value : null;
4949
}
5050

5151
/**
@@ -54,8 +54,7 @@ export default class Stack {
5454
toArray() {
5555
return this.linkedList
5656
.toArray()
57-
.map(linkedListNode => linkedListNode.value)
58-
.reverse();
57+
.map(linkedListNode => linkedListNode.value);
5958
}
6059

6160
/**

src/data-structures/stack/__test__/Stack.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('Stack', () => {
1313
stack.push(1);
1414
stack.push(2);
1515

16-
expect(stack.toString()).toBe('1,2');
16+
expect(stack.toString()).toBe('2,1');
1717
});
1818

1919
it('should peek data from stack', () => {
@@ -58,7 +58,7 @@ describe('Stack', () => {
5858

5959
const stringifier = value => `${value.key}:${value.value}`;
6060

61-
expect(stack.toString(stringifier)).toBe('key1:test1,key2:test2');
61+
expect(stack.toString(stringifier)).toBe('key2:test2,key1:test1');
6262
expect(stack.pop().value).toBe('test2');
6363
expect(stack.pop().value).toBe('test1');
6464
});

src/data-structures/tree/avl-tree/AvlTree.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ export default class AvlTree extends BinarySearchTree {
2121
* @return {boolean}
2222
*/
2323
remove(value) {
24-
throw new Error(`Can't remove ${value}. Remove method is not implemented yet`);
24+
// Do standard BST removal.
25+
super.remove(value);
26+
27+
// Balance the tree starting from the root node.
28+
this.balance(this.root);
2529
}
2630

2731
/**

0 commit comments

Comments
 (0)