Skip to content

Commit 9df9b4c

Browse files
authored
Merge pull request algorithm-archivists#118 from Gustorn/js
Add/improve JS implementations
2 parents 45270ad + 198342c commit 9df9b4c

File tree

11 files changed

+325
-71
lines changed

11 files changed

+325
-71
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
function grahamScan(points) {
2+
// First, sort the points so the one with the lowest y-coordinate comes first (the pivot)
3+
points = [...points].sort((a, b) => (a.y - b.y));
4+
const pivot = points[0];
5+
6+
// Then sort all remaining points based on the angle between the pivot and itself
7+
const hull = points.slice(1).sort((a, b) => polarAngle(a, pivot) - polarAngle(b, pivot));
8+
9+
// The pivot is always on the hull
10+
hull.unshift(pivot);
11+
12+
let n = hull.length;
13+
let m = 1;
14+
for (let i = 2; i < n; i++) {
15+
while (ccw(hull[m - 1], hull[m], hull[i]) <= 0) {
16+
if (m > 1) {
17+
m -= 1;
18+
} else if (m === i) {
19+
break;
20+
} else {
21+
i += 1;
22+
}
23+
}
24+
25+
m += 1;
26+
[hull[i], hull[m]] = [hull[m], hull[i]];
27+
}
28+
29+
return hull.slice(0, m + 1);
30+
}
31+
32+
function polarAngle(a, b) {
33+
return Math.atan2(a.y - b.y, a.x - b.x);
34+
}
35+
36+
function ccw(a, b, c) {
37+
return (b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y);
38+
}
39+
40+
const points = [
41+
{ x: 1, y: 3 },
42+
{ x: 2, y: 4 },
43+
{ x: 4, y: 0 },
44+
{ x: 1, y: 0 },
45+
{ x: 0, y: 2 },
46+
{ x: 2, y: 2 },
47+
{ x: 3, y: 4 },
48+
{ x: 3, y: 1 },
49+
];
50+
51+
const convexHull = grahamScan(points);
52+
convexHull.forEach(p => console.log(`(${p.x}, ${p.y})`));

chapters/computational_geometry/gift_wrapping/graham_scan/graham_scan.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ We can find whether a rotation is counter-clockwise with trigonometric functions
1818
[import:6-7, lang:"haskell"](code/haskell/grahamScan.hs)
1919
{% sample lang="c" %}
2020
[import:24-26, lang:"c_cpp"](code/c/graham.c)
21+
{% sample lang="js" %}
22+
[import:36-38, lang:"javascript"](code/javascript/graham-scan.js)
2123
{% endmethod %}
2224

2325
If the output of this function is 0, the points are collinear.
@@ -38,6 +40,8 @@ In the end, the code should look something like this:
3840
[import:9-18, lang:"haskell"](code/haskell/grahamScan.hs)
3941
{% sample lang="c" %}
4042
[import:65-95, lang:"c_cpp"](code/c/graham.c)
43+
{% sample lang="js" %}
44+
[import:1-30, lang:"javascript"](code/javascript/graham-scan.js)
4145
{% endmethod %}
4246

4347
### Bibliography
@@ -56,6 +60,8 @@ In the end, the code should look something like this:
5660
{% sample lang="c" %}
5761
### C
5862
[import, lang:"c_cpp"](code/c/graham.c)
63+
{% sample lang="js" %}
64+
[import, lang:"javascript"](code/javascript/graham-scan.js)
5965
{% endmethod %}
6066

6167
<script>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
function jarvisMarch(points) {
2+
const hull = [];
3+
4+
let pointOnHull = points.reduce((leftmost, current) => leftmost.x < current.x ? leftmost : current);
5+
do {
6+
hull.push(pointOnHull);
7+
pointOnHull = points.reduce(chooseNextPointOnHull(pointOnHull));
8+
} while (pointOnHull !== hull[0]);
9+
10+
return hull;
11+
}
12+
13+
function chooseNextPointOnHull(currentPoint) {
14+
return function (nextPoint, candidate) {
15+
if (nextPoint === currentPoint || isLeftOf({ a: currentPoint, b: nextPoint }, candidate)) {
16+
return candidate;
17+
}
18+
return nextPoint;
19+
}
20+
}
21+
22+
function isLeftOf({ a, b }, p) {
23+
return (b.x - a.x) * (p.y - a.y) > (p.x - a.x) * (b.y - a.y);
24+
}
25+
26+
const points = [
27+
{ x: 1, y: 3 },
28+
{ x: 2, y: 4 },
29+
{ x: 4, y: 0 },
30+
{ x: 1, y: 0 },
31+
{ x: 0, y: 2 },
32+
{ x: 2, y: 2 },
33+
{ x: 3, y: 4 },
34+
{ x: 3, y: 1 },
35+
];
36+
37+
const convexHull = jarvisMarch(points);
38+
convexHull.forEach(p => console.log(`(${p.x}, ${p.y})`));
39+

chapters/computational_geometry/gift_wrapping/jarvis_march/jarvis_march.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ Program.cs
3838
{% sample lang="c" %}
3939
### C
4040
[import, lang:"c_cpp"](code/c/jarvis_march.c)
41+
{% sample lang="js" %}
42+
### JavaScript
43+
[import, lang:"javascript"](code/javascript/jarvis-march.js)
4144
{% endmethod %}
4245

4346
<script>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
function encode(str) {
2+
const tree = createTree(str);
3+
const codebook = createCodebook(tree);
4+
return {
5+
string: [...str].map(c => codebook[c]).join(""),
6+
tree,
7+
codebook
8+
};
9+
10+
function createTree(str) {
11+
const chars = [...str];
12+
const charCounts = chars.reduce((counts, char) => {
13+
counts[char] = (counts[char] || 0) + 1;
14+
return counts;
15+
}, {});
16+
17+
const nodes = Object.entries(charCounts).map(([key, weight]) => ({ key, weight }));
18+
const priorityQueue = makeQueue(nodes);
19+
while (priorityQueue.data.length > 1) {
20+
const left = priorityQueue.dequeue();
21+
const right = priorityQueue.dequeue();
22+
priorityQueue.enqueue({ weight: left.weight + right.weight, left, right });
23+
}
24+
return priorityQueue.dequeue();
25+
}
26+
27+
function createCodebook(tree) {
28+
return recurse(tree, "", {});
29+
30+
function recurse(node, bitstring, dict) {
31+
if (!node.left && !node.right) {
32+
dict[node.key] = bitstring;
33+
} else {
34+
if (node.left) {
35+
recurse(node.left, bitstring + "0", dict);
36+
}
37+
38+
if (node.right) {
39+
recurse(node.right, bitstring + "1", dict);
40+
}
41+
}
42+
return dict;
43+
}
44+
}
45+
}
46+
47+
function decode(bitstring, tree) {
48+
const result = [];
49+
let node = tree;
50+
51+
for (const bit of [...bitstring]) {
52+
node = bit === "0" ? node.left : node.right;
53+
if (!node.left && !node.right) {
54+
result.push(node.key);
55+
node = tree;
56+
}
57+
}
58+
59+
return result.join("");
60+
}
61+
62+
// This queue implementation is horribly inefficient, but a proper, heap-based implementation would
63+
// be longer that the algorithm itself
64+
function makeQueue(iterable) {
65+
return {
66+
data: [...iterable].sort((a, b) => a.weight - b.weight),
67+
enqueue(value) {
68+
const target = this.data.findIndex(x => x.weight > value.weight);
69+
if (target === -1) {
70+
this.data.push(value);
71+
} else {
72+
this.data = [...this.data.slice(0, target), value, ...this.data.slice(target)];
73+
}
74+
},
75+
dequeue() {
76+
return this.data.shift();
77+
}
78+
};
79+
}
80+
81+
const encoded = encode("bibbity bobbity");
82+
const decoded = decode(encoded.string, encoded.tree);
83+
console.log(encoded.string);
84+
console.log(decoded);

chapters/data_compression/huffman/huffman.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ Program.cs
8181
{% sample lang="py" %}
8282
### Python
8383
[import, lang:"python"](code/python/huffman.py)
84+
{% sample lang="js" %}
85+
### JavaScript
86+
[import, lang:"javascript"](code/javascript/huffman.js)
8487
{% endmethod %}
8588

8689

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
class Person {
2+
constructor(name) {
3+
this.name = name;
4+
}
5+
6+
get hasFiance() {
7+
return !!this.fiance;
8+
}
9+
10+
prefers(other) {
11+
return this.preferences.indexOf(other) < this.preferences.indexOf(this.fiance);
12+
}
13+
14+
engageTo(other) {
15+
if (other.hasFiance) {
16+
other.fiance.fiance = undefined;
17+
}
18+
19+
this.fiance = other;
20+
other.fiance = this;
21+
}
22+
}
23+
24+
function stableMarriage(guys, girls) {
25+
const bachelors = [...guys];
26+
while (bachelors.length > 0) {
27+
const guy = bachelors.shift();
28+
for (const girl of guy.preferences) {
29+
if (!girl.hasFiance) {
30+
guy.engageTo(girl);
31+
break;
32+
} else if (girl.prefers(guy)) {
33+
bachelors.push(girl.fiance);
34+
guy.engageTo(girl);
35+
break;
36+
}
37+
}
38+
}
39+
}
40+
41+
function shuffle(iterable) {
42+
const array = [...iterable];
43+
for (let i = array.length - 1; i > 0; i--) {
44+
const j = Math.floor(Math.random() * (i + 1));
45+
[array[i], array[j]] = [array[j], array[i]];
46+
}
47+
return array;
48+
}
49+
50+
const guys = [..."ABCDE"].map(name => new Person(name));
51+
const girls = [..."FGHIJ"].map(name => new Person(name));
52+
53+
console.log("Guys");
54+
for (const guy of guys) {
55+
guy.preferences = shuffle(girls);
56+
console.log(`${guy.name}: ${guy.preferences.map(p => p.name).join()}`)
57+
}
58+
59+
console.log("\nGirls");
60+
for (const girl of girls) {
61+
girl.preferences = shuffle(guys);
62+
console.log(`${girl.name}: ${girl.preferences.map(p => p.name).join()}`)
63+
}
64+
65+
stableMarriage(guys, girls);
66+
67+
console.log("\nPairings");
68+
for (const guy of guys) {
69+
console.log(`${guy.name}: ${guy.fiance.name}`);
70+
}
71+

chapters/decision_problems/stable_marriage/stable_marriage.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ I am incredibly interested to see what you guys do and how you implement the alg
3434
{% sample lang="cpp" %}
3535
### C++
3636
[import, lang:"c_cpp"](code/c++/stable_marriage.cpp)
37+
{% sample lang="js" %}
38+
### JavaScript
39+
[import, lang:"javascript"](code/javascript/stable-marriage.js)
3740
{% sample lang="cs" %}
3841
### C# #
3942
GaleShapleyAlgorithm.cs

chapters/tree_traversal/code/javascript/Tree_example.js

Lines changed: 0 additions & 63 deletions
This file was deleted.

0 commit comments

Comments
 (0)