mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-11-14 06:52:59 +08:00
Add Tower of Hanoi.
This commit is contained in:
parent
26ba21b34c
commit
44b0a99a80
@ -77,6 +77,7 @@
|
|||||||
* [Eulerian Path and Eulerian Circuit](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/eulerian-path) - Fleury's algorithm
|
* [Eulerian Path and Eulerian Circuit](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/eulerian-path) - Fleury's algorithm
|
||||||
* [Strongly Connected Components](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/strongly-connected-components) - Kosaraju's algorithm
|
* [Strongly Connected Components](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/strongly-connected-components) - Kosaraju's algorithm
|
||||||
* **Uncategorized**
|
* **Uncategorized**
|
||||||
|
* [Tower of Hanoi](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/uncategorized/hanoi-tower)
|
||||||
* Union-Find
|
* Union-Find
|
||||||
* Maze
|
* Maze
|
||||||
* Sudoku
|
* Sudoku
|
||||||
@ -89,6 +90,7 @@
|
|||||||
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
|
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
|
||||||
* [Kruskal’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
|
* [Kruskal’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
|
||||||
* **Divide and Conquer**
|
* **Divide and Conquer**
|
||||||
|
* [Tower of Hanoi](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/uncategorized/hanoi-tower)
|
||||||
* [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
|
* [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
|
||||||
* [Permutations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/permutations) (with and without repetitions)
|
* [Permutations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/permutations) (with and without repetitions)
|
||||||
* [Combinations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/combinations) (with and without repetitions)
|
* [Combinations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/combinations) (with and without repetitions)
|
||||||
|
29
src/algorithms/uncategorized/hanoi-tower/README.md
Normal file
29
src/algorithms/uncategorized/hanoi-tower/README.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Tower of Hanoi
|
||||||
|
|
||||||
|
The Tower of Hanoi (also called the Tower of Brahma or Lucas'
|
||||||
|
Tower and sometimes pluralized) is a mathematical game or puzzle.
|
||||||
|
It consists of three rods and a number of disks of different sizes,
|
||||||
|
which can slide onto any rod. The puzzle starts with the disks in
|
||||||
|
a neat stack in ascending order of size on one rod, the smallest
|
||||||
|
at the top, thus making a conical shape.
|
||||||
|
|
||||||
|
The objective of the puzzle is to move the entire stack to another
|
||||||
|
rod, obeying the following simple rules:
|
||||||
|
|
||||||
|
- Only one disk can be moved at a time.
|
||||||
|
- Each move consists of taking the upper disk from one of the
|
||||||
|
stacks and placing it on top of another stack or on an empty rod.
|
||||||
|
- No disk may be placed on top of a smaller disk.
|
||||||
|
|
||||||
|
![Hanoi Tower](https://upload.wikimedia.org/wikipedia/commons/8/8d/Iterative_algorithm_solving_a_6_disks_Tower_of_Hanoi.gif)
|
||||||
|
|
||||||
|
Animation of an iterative algorithm solving 6-disk problem
|
||||||
|
|
||||||
|
With `3` disks, the puzzle can be solved in `7` moves. The minimal
|
||||||
|
number of moves required to solve a Tower of Hanoi puzzle
|
||||||
|
is `2n − 1`, where `n` is the number of disks.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Wikipedia](https://en.wikipedia.org/wiki/Tower_of_Hanoi)
|
||||||
|
- [HackerEarth](https://www.hackerearth.com/blog/algorithms/tower-hanoi-recursion-game-algorithm-explained/)
|
@ -0,0 +1,39 @@
|
|||||||
|
import hanoiTower from '../hanoiTower';
|
||||||
|
|
||||||
|
describe('hanoiTower', () => {
|
||||||
|
it('should solve tower of hanoi puzzle with 2 discs', () => {
|
||||||
|
const moveCallbackMock = jest.fn();
|
||||||
|
|
||||||
|
hanoiTower(2, moveCallbackMock);
|
||||||
|
|
||||||
|
expect(moveCallbackMock).toHaveBeenCalledTimes(3);
|
||||||
|
|
||||||
|
expect(moveCallbackMock.mock.calls[0][0]).toBe(1);
|
||||||
|
expect(moveCallbackMock.mock.calls[0][1]).toEqual([1, 2]);
|
||||||
|
expect(moveCallbackMock.mock.calls[0][2]).toEqual([]);
|
||||||
|
|
||||||
|
expect(moveCallbackMock.mock.calls[1][0]).toBe(2);
|
||||||
|
expect(moveCallbackMock.mock.calls[1][1]).toEqual([2]);
|
||||||
|
expect(moveCallbackMock.mock.calls[1][2]).toEqual([]);
|
||||||
|
|
||||||
|
expect(moveCallbackMock.mock.calls[2][0]).toBe(1);
|
||||||
|
expect(moveCallbackMock.mock.calls[2][1]).toEqual([1]);
|
||||||
|
expect(moveCallbackMock.mock.calls[2][2]).toEqual([2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should solve tower of hanoi puzzle with 3 discs', () => {
|
||||||
|
const moveCallbackMock = jest.fn();
|
||||||
|
|
||||||
|
hanoiTower(3, moveCallbackMock);
|
||||||
|
|
||||||
|
expect(moveCallbackMock).toHaveBeenCalledTimes(7);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should solve tower of hanoi puzzle with 6 discs', () => {
|
||||||
|
const moveCallbackMock = jest.fn();
|
||||||
|
|
||||||
|
hanoiTower(6, moveCallbackMock);
|
||||||
|
|
||||||
|
expect(moveCallbackMock).toHaveBeenCalledTimes(63);
|
||||||
|
});
|
||||||
|
});
|
94
src/algorithms/uncategorized/hanoi-tower/hanoiTower.js
Normal file
94
src/algorithms/uncategorized/hanoi-tower/hanoiTower.js
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import Stack from '../../../data-structures/stack/Stack';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Stack} fromPole
|
||||||
|
* @param {Stack} toPole
|
||||||
|
* @param {function(disc: number, fromPole: number[], toPole: number[])} moveCallback
|
||||||
|
*/
|
||||||
|
function moveDisc(fromPole, toPole, moveCallback) {
|
||||||
|
moveCallback(fromPole.peek(), fromPole.toArray(), toPole.toArray());
|
||||||
|
|
||||||
|
const disc = fromPole.pop();
|
||||||
|
toPole.push(disc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} numberOfDiscs
|
||||||
|
* @param {Stack} fromPole
|
||||||
|
* @param {Stack} withPole
|
||||||
|
* @param {Stack} toPole
|
||||||
|
* @param {function(disc: number, fromPole: number[], toPole: number[])} moveCallback
|
||||||
|
*/
|
||||||
|
function hanoiTowerRecursive({
|
||||||
|
numberOfDiscs,
|
||||||
|
fromPole,
|
||||||
|
withPole,
|
||||||
|
toPole,
|
||||||
|
moveCallback,
|
||||||
|
}) {
|
||||||
|
if (numberOfDiscs === 1) {
|
||||||
|
// Base case with just one disc.
|
||||||
|
moveDisc(fromPole, toPole, moveCallback);
|
||||||
|
} else {
|
||||||
|
// In case if there are more discs then move them recursively.
|
||||||
|
|
||||||
|
// Expose the bottom disc on fromPole stack.
|
||||||
|
hanoiTowerRecursive({
|
||||||
|
numberOfDiscs: numberOfDiscs - 1,
|
||||||
|
fromPole,
|
||||||
|
withPole: toPole,
|
||||||
|
toPole: withPole,
|
||||||
|
moveCallback,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Move the disc that was exposed to its final destination.
|
||||||
|
hanoiTowerRecursive({
|
||||||
|
numberOfDiscs: 1,
|
||||||
|
fromPole,
|
||||||
|
withPole,
|
||||||
|
toPole,
|
||||||
|
moveCallback,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Move temporary tower from auxiliary pole to its final destination.
|
||||||
|
hanoiTowerRecursive({
|
||||||
|
numberOfDiscs: numberOfDiscs - 1,
|
||||||
|
fromPole: withPole,
|
||||||
|
withPole: fromPole,
|
||||||
|
toPole,
|
||||||
|
moveCallback,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} numberOfDiscs
|
||||||
|
* @param {function(disc: number, fromPole: number[], toPole: number[])} moveCallback
|
||||||
|
*/
|
||||||
|
export default function hanoiTower(numberOfDiscs, moveCallback) {
|
||||||
|
// Each of three poles of Tower of Hanoi puzzle is represented as a stack
|
||||||
|
// that might contain elements (discs). Each disc is represented as a number.
|
||||||
|
// Larger discs have bigger number equivalent.
|
||||||
|
|
||||||
|
// The pole from where the discs should be moved.
|
||||||
|
const fromPole = new Stack();
|
||||||
|
|
||||||
|
// The middle pole that should be used as a helper.
|
||||||
|
const withPole = new Stack();
|
||||||
|
|
||||||
|
// The destination pole where all discs need to be moved.
|
||||||
|
const toPole = new Stack();
|
||||||
|
|
||||||
|
// Let's create the discs and put them to the fromPole.
|
||||||
|
for (let discSize = numberOfDiscs; discSize > 0; discSize -= 1) {
|
||||||
|
fromPole.push(discSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
hanoiTowerRecursive({
|
||||||
|
numberOfDiscs,
|
||||||
|
fromPole,
|
||||||
|
withPole,
|
||||||
|
toPole,
|
||||||
|
moveCallback,
|
||||||
|
});
|
||||||
|
}
|
@ -13,7 +13,7 @@ export default class Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {LinkedListNode}
|
* @return {*}
|
||||||
*/
|
*/
|
||||||
peek() {
|
peek() {
|
||||||
if (!this.linkedList.tail) {
|
if (!this.linkedList.tail) {
|
||||||
|
Loading…
Reference in New Issue
Block a user