mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-11-14 23:12:58 +08:00
Merge branch 'master' into patch-1
This commit is contained in:
commit
8b2f83e6c7
@ -19,6 +19,9 @@ _Read this in other languages:_
|
||||
[_Español_](README.es-ES.md),
|
||||
[_Português_](README.pt-BR.md)
|
||||
|
||||
*Note that this project is meant to be used for learning and researching purposes
|
||||
only and it is **not** meant to be used for production.*
|
||||
|
||||
## Data Structures
|
||||
|
||||
A data structure is a particular way of organizing and storing data in a computer so that it can
|
||||
@ -67,6 +70,7 @@ a set of rules that precisely define a sequence of operations.
|
||||
* `B` [Pascal's Triangle](src/algorithms/math/pascal-triangle)
|
||||
* `B` [Complex Number](src/algorithms/math/complex-number) - complex numbers and basic operations with them
|
||||
* `B` [Radian & Degree](src/algorithms/math/radian) - radians to degree and backwards conversion
|
||||
* `B` [Fast Powering](src/algorithms/math/fast-powering)
|
||||
* `A` [Integer Partition](src/algorithms/math/integer-partition)
|
||||
* `A` [Liu Hui π Algorithm](src/algorithms/math/liu-hui) - approximate π calculations based on N-gons
|
||||
* `A` [Discrete Fourier Transform](src/algorithms/math/fourier-transform) - decompose a function of time (a signal) into the frequencies that make it up
|
||||
@ -105,6 +109,9 @@ a set of rules that precisely define a sequence of operations.
|
||||
* `B` [Shellsort](src/algorithms/sorting/shell-sort)
|
||||
* `B` [Counting Sort](src/algorithms/sorting/counting-sort)
|
||||
* `B` [Radix Sort](src/algorithms/sorting/radix-sort)
|
||||
* **Linked Lists**
|
||||
* `B` [Straight Traversal](src/algorithms/linked-list/traversal)
|
||||
* `B` [Reverse Traversal](src/algorithms/linked-list/reverse-traversal)
|
||||
* **Trees**
|
||||
* `B` [Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
|
||||
* `B` [Breadth-First Search](src/algorithms/tree/breadth-first-search) (BFS)
|
||||
@ -163,6 +170,7 @@ algorithm is an abstraction higher than a computer program.
|
||||
* `B` [Tree Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
|
||||
* `B` [Graph Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
|
||||
* `B` [Jump Game](src/algorithms/uncategorized/jump-game)
|
||||
* `B` [Fast Powering](src/algorithms/math/fast-powering)
|
||||
* `A` [Permutations](src/algorithms/sets/permutations) (with and without repetitions)
|
||||
* `A` [Combinations](src/algorithms/sets/combinations) (with and without repetitions)
|
||||
* **Dynamic Programming** - build up a solution using previously found sub-solutions
|
||||
|
19
src/algorithms/linked-list/reverse-traversal/README.md
Normal file
19
src/algorithms/linked-list/reverse-traversal/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Reversed Linked List Traversal
|
||||
|
||||
The task is to traverse the given linked list in reversed order.
|
||||
|
||||
For example for the following linked list:
|
||||
|
||||
![](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
|
||||
|
||||
The order of traversal should be:
|
||||
|
||||
```text
|
||||
37 → 99 → 12
|
||||
```
|
||||
|
||||
The time complexity is `O(n)` because we visit every node only once.
|
||||
|
||||
## Reference
|
||||
|
||||
- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
|
@ -0,0 +1,36 @@
|
||||
import LinkedList from '../../../../data-structures/linked-list/LinkedList';
|
||||
import reverseTraversal from '../reverseTraversal';
|
||||
|
||||
describe('reverseTraversal', () => {
|
||||
it('should traverse linked list in reverse order', () => {
|
||||
const linkedList = new LinkedList();
|
||||
|
||||
linkedList
|
||||
.append(1)
|
||||
.append(2)
|
||||
.append(3);
|
||||
|
||||
const traversedNodeValues = [];
|
||||
const traversalCallback = (nodeValue) => {
|
||||
traversedNodeValues.push(nodeValue);
|
||||
};
|
||||
|
||||
reverseTraversal(linkedList, traversalCallback);
|
||||
|
||||
expect(traversedNodeValues).toEqual([3, 2, 1]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// it('should reverse traversal the linked list with callback', () => {
|
||||
// const linkedList = new LinkedList();
|
||||
//
|
||||
// linkedList
|
||||
// .append(1)
|
||||
// .append(2)
|
||||
// .append(3);
|
||||
//
|
||||
// expect(linkedList.toString()).toBe('1,2,3');
|
||||
// expect(linkedList.reverseTraversal(linkedList.head, value => value * 2)).toEqual([6, 4, 2]);
|
||||
// expect(() => linkedList.reverseTraversal(linkedList.head)).toThrow();
|
||||
// });
|
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Traversal callback function.
|
||||
* @callback traversalCallback
|
||||
* @param {*} nodeValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {LinkedListNode} node
|
||||
* @param {traversalCallback} callback
|
||||
*/
|
||||
function reverseTraversalRecursive(node, callback) {
|
||||
if (node) {
|
||||
reverseTraversalRecursive(node.next, callback);
|
||||
callback(node.value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LinkedList} linkedList
|
||||
* @param {traversalCallback} callback
|
||||
*/
|
||||
export default function reverseTraversal(linkedList, callback) {
|
||||
reverseTraversalRecursive(linkedList.head, callback);
|
||||
}
|
19
src/algorithms/linked-list/traversal/README.md
Normal file
19
src/algorithms/linked-list/traversal/README.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Linked List Traversal
|
||||
|
||||
The task is to traverse the given linked list in straight order.
|
||||
|
||||
For example for the following linked list:
|
||||
|
||||
![](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg)
|
||||
|
||||
The order of traversal should be:
|
||||
|
||||
```text
|
||||
12 → 99 → 37
|
||||
```
|
||||
|
||||
The time complexity is `O(n)` because we visit every node only once.
|
||||
|
||||
## Reference
|
||||
|
||||
- [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
|
@ -0,0 +1,22 @@
|
||||
import LinkedList from '../../../../data-structures/linked-list/LinkedList';
|
||||
import traversal from '../traversal';
|
||||
|
||||
describe('traversal', () => {
|
||||
it('should traverse linked list', () => {
|
||||
const linkedList = new LinkedList();
|
||||
|
||||
linkedList
|
||||
.append(1)
|
||||
.append(2)
|
||||
.append(3);
|
||||
|
||||
const traversedNodeValues = [];
|
||||
const traversalCallback = (nodeValue) => {
|
||||
traversedNodeValues.push(nodeValue);
|
||||
};
|
||||
|
||||
traversal(linkedList, traversalCallback);
|
||||
|
||||
expect(traversedNodeValues).toEqual([1, 2, 3]);
|
||||
});
|
||||
});
|
18
src/algorithms/linked-list/traversal/traversal.js
Normal file
18
src/algorithms/linked-list/traversal/traversal.js
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Traversal callback function.
|
||||
* @callback traversalCallback
|
||||
* @param {*} nodeValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {LinkedList} linkedList
|
||||
* @param {traversalCallback} callback
|
||||
*/
|
||||
export default function traversal(linkedList, callback) {
|
||||
let currentNode = linkedList.head;
|
||||
|
||||
while (currentNode) {
|
||||
callback(currentNode.value);
|
||||
currentNode = currentNode.next;
|
||||
}
|
||||
}
|
@ -2,10 +2,10 @@
|
||||
|
||||
#### Get Bit
|
||||
|
||||
This method shifts the relevant bit to the zeroth position.
|
||||
Then we perform `AND` operation with one which has bit
|
||||
This method shifts the relevant bit to the zeroth position.
|
||||
Then we perform `AND` operation with one which has bit
|
||||
pattern like `0001`. This clears all bits from the original
|
||||
number except the relevant one. If the relevant bit is one,
|
||||
number except the relevant one. If the relevant bit is one,
|
||||
the result is `1`, otherwise the result is `0`.
|
||||
|
||||
> See [getBit.js](getBit.js) for further details.
|
||||
@ -24,7 +24,7 @@ other bits of the number.
|
||||
This method shifts `1` over by `bitPosition` bits, creating a
|
||||
value that looks like `00100`. Than it inverts this mask to get
|
||||
the number that looks like `11011`. Then `AND` operation is
|
||||
being applied to both the number and the mask. That operation
|
||||
being applied to both the number and the mask. That operation
|
||||
unsets the bit.
|
||||
|
||||
> See [clearBit.js](clearBit.js) for further details.
|
||||
@ -35,21 +35,37 @@ This method is a combination of "Clear Bit" and "Set Bit" methods.
|
||||
|
||||
> See [updateBit.js](updateBit.js) for further details.
|
||||
|
||||
#### isEven
|
||||
|
||||
This method determines if the number provided is even.
|
||||
It is based on the fact that odd numbers have their last
|
||||
right bit to be set to 1.
|
||||
|
||||
```text
|
||||
Number: 5 = 0b0101
|
||||
isEven: false
|
||||
|
||||
Number: 4 = 0b0100
|
||||
isEven: true
|
||||
```
|
||||
|
||||
> See [isEven.js](isEven.js) for further details.
|
||||
|
||||
#### Multiply By Two
|
||||
|
||||
This method shifts original number by one bit to the left.
|
||||
Thus all binary number components (powers of two) are being
|
||||
multiplying by two and thus the number itself is being
|
||||
multiplying by two and thus the number itself is being
|
||||
multiplied by two.
|
||||
|
||||
```
|
||||
Before the shift
|
||||
Number: 0b0101 = 5
|
||||
Powers of two: 0 + 2^2 + 0 + 2^0
|
||||
Powers of two: 0 + 2^2 + 0 + 2^0
|
||||
|
||||
After the shift
|
||||
Number: 0b1010 = 10
|
||||
Powers of two: 2^3 + 0 + 2^1 + 0
|
||||
Powers of two: 2^3 + 0 + 2^1 + 0
|
||||
```
|
||||
|
||||
> See [multiplyByTwo.js](multiplyByTwo.js) for further details.
|
||||
@ -58,17 +74,17 @@ Powers of two: 2^3 + 0 + 2^1 + 0
|
||||
|
||||
This method shifts original number by one bit to the right.
|
||||
Thus all binary number components (powers of two) are being
|
||||
divided by two and thus the number itself is being
|
||||
divided by two and thus the number itself is being
|
||||
divided by two without remainder.
|
||||
|
||||
```
|
||||
Before the shift
|
||||
Number: 0b0101 = 5
|
||||
Powers of two: 0 + 2^2 + 0 + 2^0
|
||||
Powers of two: 0 + 2^2 + 0 + 2^0
|
||||
|
||||
After the shift
|
||||
Number: 0b0010 = 2
|
||||
Powers of two: 0 + 0 + 2^1 + 0
|
||||
Powers of two: 0 + 0 + 2^1 + 0
|
||||
```
|
||||
|
||||
> See [divideByTwo.js](divideByTwo.js) for further details.
|
||||
@ -87,11 +103,30 @@ inverting all of the bits of the number and adding 1 to it.
|
||||
0001 1
|
||||
0010 2
|
||||
0011 3
|
||||
```
|
||||
```
|
||||
|
||||
> See [switchSign.js](switchSign.js) for further details.
|
||||
|
||||
#### Multiply Two Numbers
|
||||
#### Multiply Two Signed Numbers
|
||||
|
||||
This method multiplies two signed integer numbers using bitwise operators.
|
||||
This method is based on the following facts:
|
||||
|
||||
```text
|
||||
a * b can be written in the below formats:
|
||||
0 if a is zero or b is zero or both a and b are zeroes
|
||||
2a * (b/2) if b is even
|
||||
2a * (b - 1)/2 + a if b is odd and positive
|
||||
2a * (b + 1)/2 - a if b is odd and negative
|
||||
```
|
||||
|
||||
The advantage of this approach is that in each recursive step one of the operands
|
||||
reduces to half its original value. Hence, the run time complexity is `O(log(b)` where `b` is
|
||||
the operand that reduces to half on each recursive step.
|
||||
|
||||
> See [multiply.js](multiply.js) for further details.
|
||||
|
||||
#### Multiply Two Unsigned Numbers
|
||||
|
||||
This method multiplies two integer numbers using bitwise operators.
|
||||
This method is based on that "Every number can be denoted as the sum of powers of 2".
|
||||
@ -111,7 +146,7 @@ Then multiplying number `x` by `19` is equivalent of:
|
||||
x * 19 = x * 2^4 + x * 2^1 + x * 2^0
|
||||
```
|
||||
|
||||
Now we need to remember that `x * 2^4` is equivalent of shifting `x` left
|
||||
Now we need to remember that `x * 2^4` is equivalent of shifting `x` left
|
||||
by `4` bits (`x << 4`).
|
||||
|
||||
> See [multiplyUnsigned.js](multiplyUnsigned.js) for further details.
|
||||
@ -158,7 +193,7 @@ When we shift 1 four times it will become bigger than 5.
|
||||
|
||||
#### Is Power of Two
|
||||
|
||||
This method checks if a number provided is power of two. It uses the following
|
||||
This method checks if a number provided is power of two. It uses the following
|
||||
property. Let's say that `powerNumber` is a number that has been formed as a power
|
||||
of two (i.e. 2, 4, 8, 16 etc.). Then if we'll do `&` operation between `powerNumber`
|
||||
and `powerNumber - 1` it will return `0` (in case if number is power of two).
|
||||
|
19
src/algorithms/math/bits/__test__/isEven.test.js
Normal file
19
src/algorithms/math/bits/__test__/isEven.test.js
Normal file
@ -0,0 +1,19 @@
|
||||
import isEven from '../isEven';
|
||||
|
||||
describe('isEven', () => {
|
||||
it('should detect if a number is even', () => {
|
||||
expect(isEven(0)).toBe(true);
|
||||
expect(isEven(2)).toBe(true);
|
||||
expect(isEven(-2)).toBe(true);
|
||||
expect(isEven(1)).toBe(false);
|
||||
expect(isEven(-1)).toBe(false);
|
||||
expect(isEven(-3)).toBe(false);
|
||||
expect(isEven(3)).toBe(false);
|
||||
expect(isEven(8)).toBe(true);
|
||||
expect(isEven(9)).toBe(false);
|
||||
expect(isEven(121)).toBe(false);
|
||||
expect(isEven(122)).toBe(true);
|
||||
expect(isEven(1201)).toBe(false);
|
||||
expect(isEven(1202)).toBe(true);
|
||||
});
|
||||
});
|
18
src/algorithms/math/bits/__test__/multiply.test.js
Normal file
18
src/algorithms/math/bits/__test__/multiply.test.js
Normal file
@ -0,0 +1,18 @@
|
||||
import multiply from '../multiply';
|
||||
|
||||
describe('multiply', () => {
|
||||
it('should multiply two numbers', () => {
|
||||
expect(multiply(0, 0)).toBe(0);
|
||||
expect(multiply(2, 0)).toBe(0);
|
||||
expect(multiply(0, 2)).toBe(0);
|
||||
expect(multiply(1, 2)).toBe(2);
|
||||
expect(multiply(2, 1)).toBe(2);
|
||||
expect(multiply(6, 6)).toBe(36);
|
||||
expect(multiply(-2, 4)).toBe(-8);
|
||||
expect(multiply(4, -2)).toBe(-8);
|
||||
expect(multiply(-4, -4)).toBe(16);
|
||||
expect(multiply(4, -5)).toBe(-20);
|
||||
expect(multiply(2, 121)).toBe(242);
|
||||
expect(multiply(121, 2)).toBe(242);
|
||||
});
|
||||
});
|
7
src/algorithms/math/bits/isEven.js
Normal file
7
src/algorithms/math/bits/isEven.js
Normal file
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @param {number} number
|
||||
* @return {boolean}
|
||||
*/
|
||||
export default function isEven(number) {
|
||||
return (number & 1) === 0;
|
||||
}
|
40
src/algorithms/math/bits/multiply.js
Normal file
40
src/algorithms/math/bits/multiply.js
Normal file
@ -0,0 +1,40 @@
|
||||
import multiplyByTwo from './multiplyByTwo';
|
||||
import divideByTwo from './divideByTwo';
|
||||
import isEven from './isEven';
|
||||
|
||||
/**
|
||||
* Multiply two signed numbers using bitwise operations.
|
||||
*
|
||||
* If a is zero or b is zero or if both a and b are zeros:
|
||||
* multiply(a, b) = 0
|
||||
*
|
||||
* If b is even:
|
||||
* multiply(a, b) = multiply(2a, b/2)
|
||||
*
|
||||
* If b is odd and b is positive:
|
||||
* multiply(a, b) = multiply(2a, (b-1)/2) + a
|
||||
*
|
||||
* If b is odd and b is negative:
|
||||
* multiply(a, b) = multiply(2a, (b+1)/2) - a
|
||||
*
|
||||
* Time complexity: O(log b)
|
||||
*
|
||||
* @param {number} a
|
||||
* @param {number} b
|
||||
* @return {number}
|
||||
*/
|
||||
export default function multiply(a, b) {
|
||||
// If a is zero or b is zero or if both a and b are zeros then the production is also zero.
|
||||
if (b === 0 || a === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Otherwise we will have four different cases that are described above.
|
||||
const multiplyByOddPositive = () => multiply(multiplyByTwo(a), divideByTwo(b - 1)) + a;
|
||||
const multiplyByOddNegative = () => multiply(multiplyByTwo(a), divideByTwo(b + 1)) - a;
|
||||
|
||||
const multiplyByEven = () => multiply(multiplyByTwo(a), divideByTwo(b));
|
||||
const multiplyByOdd = () => (b > 0 ? multiplyByOddPositive() : multiplyByOddNegative());
|
||||
|
||||
return isEven(b) ? multiplyByEven() : multiplyByOdd();
|
||||
}
|
68
src/algorithms/math/fast-powering/README.md
Normal file
68
src/algorithms/math/fast-powering/README.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Fast Powering Algorithm
|
||||
|
||||
**The power of a number** says how many times to use the number in a
|
||||
multiplication.
|
||||
|
||||
It is written as a small number to the right and above the base number.
|
||||
|
||||
![Power](https://www.mathsisfun.com/algebra/images/exponent-8-2.svg)
|
||||
|
||||
## Naive Algorithm Complexity
|
||||
|
||||
How to find `a` raised to the power `b`?
|
||||
|
||||
We multiply `a` to itself, `b` times. That
|
||||
is, `a^b = a * a * a * ... * a` (`b` occurrences of `a`).
|
||||
|
||||
This operation will take `O(n)` time since we need to do multiplication operation
|
||||
exactly `n` times.
|
||||
|
||||
## Fast Power Algorithm
|
||||
|
||||
Can we do better than naive algorithm does? Yes we may solve the task of
|
||||
powering in `O(log(n))` time.
|
||||
|
||||
The algorithm uses divide and conquer approach to compute power. Currently the
|
||||
algorithm work for two positive integers `X` and `Y`.
|
||||
|
||||
The idea behind the algorithm is based on the fact that:
|
||||
|
||||
For **even** `Y`:
|
||||
|
||||
```text
|
||||
X^Y = X^(Y/2) * X^(Y/2)
|
||||
```
|
||||
|
||||
For **odd** `Y`:
|
||||
|
||||
```text
|
||||
X^Y = X^(Y//2) * X^(Y//2) * X
|
||||
where Y//2 is result of division of Y by 2 without reminder.
|
||||
```
|
||||
|
||||
**For example**
|
||||
|
||||
```text
|
||||
2^4 = (2 * 2) * (2 * 2) = (2^2) * (2^2)
|
||||
```
|
||||
|
||||
```text
|
||||
2^5 = (2 * 2) * (2 * 2) * 2 = (2^2) * (2^2) * (2)
|
||||
```
|
||||
|
||||
Now, since on each step we need to compute the same `X^(Y/2)` power twice we may optimise
|
||||
it by saving it to some intermediate variable to avoid its duplicate calculation.
|
||||
|
||||
**Time Complexity**
|
||||
|
||||
Since each iteration we split the power by half then we will call function
|
||||
recursively `log(n)` times. This the time complexity of the algorithm is reduced to:
|
||||
|
||||
```text
|
||||
O(log(n))
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [YouTube](https://www.youtube.com/watch?v=LUWavfN9zEo&index=80&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&t=0s)
|
||||
- [Wikipedia](https://en.wikipedia.org/wiki/Exponentiation_by_squaring)
|
@ -0,0 +1,23 @@
|
||||
import fastPowering from '../fastPowering';
|
||||
|
||||
describe('fastPowering', () => {
|
||||
it('should compute power in log(n) time', () => {
|
||||
expect(fastPowering(1, 1)).toBe(1);
|
||||
expect(fastPowering(2, 0)).toBe(1);
|
||||
expect(fastPowering(2, 2)).toBe(4);
|
||||
expect(fastPowering(2, 3)).toBe(8);
|
||||
expect(fastPowering(2, 4)).toBe(16);
|
||||
expect(fastPowering(2, 5)).toBe(32);
|
||||
expect(fastPowering(2, 6)).toBe(64);
|
||||
expect(fastPowering(2, 7)).toBe(128);
|
||||
expect(fastPowering(2, 8)).toBe(256);
|
||||
expect(fastPowering(3, 4)).toBe(81);
|
||||
expect(fastPowering(190, 2)).toBe(36100);
|
||||
expect(fastPowering(11, 5)).toBe(161051);
|
||||
expect(fastPowering(13, 11)).toBe(1792160394037);
|
||||
expect(fastPowering(9, 16)).toBe(1853020188851841);
|
||||
expect(fastPowering(16, 16)).toBe(18446744073709552000);
|
||||
expect(fastPowering(7, 21)).toBe(558545864083284000);
|
||||
expect(fastPowering(100, 9)).toBe(1000000000000000000);
|
||||
});
|
||||
});
|
30
src/algorithms/math/fast-powering/fastPowering.js
Normal file
30
src/algorithms/math/fast-powering/fastPowering.js
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Fast Powering Algorithm.
|
||||
* Recursive implementation to compute power.
|
||||
*
|
||||
* Complexity: log(n)
|
||||
*
|
||||
* @param {number} base - Number that will be raised to the power.
|
||||
* @param {number} power - The power that number will be raised to.
|
||||
* @return {number}
|
||||
*/
|
||||
export default function fastPowering(base, power) {
|
||||
if (power === 0) {
|
||||
// Anything that is raised to the power of zero is 1.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (power % 2 === 0) {
|
||||
// If the power is even...
|
||||
// we may recursively redefine the result via twice smaller powers:
|
||||
// x^8 = x^4 * x^4.
|
||||
const multiplier = fastPowering(base, power / 2);
|
||||
return multiplier * multiplier;
|
||||
}
|
||||
|
||||
// If the power is odd...
|
||||
// we may recursively redefine the result via twice smaller powers:
|
||||
// x^9 = x^4 * x^4 * x.
|
||||
const multiplier = fastPowering(base, Math.floor(power / 2));
|
||||
return multiplier * multiplier * base;
|
||||
}
|
@ -230,4 +230,32 @@ export default class DoublyLinkedList {
|
||||
toString(callback) {
|
||||
return this.toArray().map(node => node.toString(callback)).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse a linked list.
|
||||
* @returns {DoublyLinkedList}
|
||||
*/
|
||||
reverse() {
|
||||
let currNode = this.head;
|
||||
let prevNode = null;
|
||||
let nextNode = null;
|
||||
|
||||
while (currNode) {
|
||||
// Store next node.
|
||||
nextNode = currNode.next;
|
||||
|
||||
// Change next node of the current node so it would link to previous node.
|
||||
currNode.next = prevNode;
|
||||
|
||||
// Move prevNode and currNode nodes one step forward.
|
||||
prevNode = currNode;
|
||||
currNode = nextNode;
|
||||
}
|
||||
|
||||
// Reset head and tail.
|
||||
this.tail = this.head;
|
||||
this.head = prevNode;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -228,4 +228,30 @@ describe('DoublyLinkedList', () => {
|
||||
expect(node.value.customValue).toBe('test2');
|
||||
expect(linkedList.find({ value: 2, customValue: 'test5' })).toBeNull();
|
||||
});
|
||||
|
||||
it('should reverse linked list', () => {
|
||||
const linkedList = new DoublyLinkedList();
|
||||
|
||||
// Add test values to linked list.
|
||||
linkedList
|
||||
.append(1)
|
||||
.append(2)
|
||||
.append(3);
|
||||
|
||||
expect(linkedList.toString()).toBe('1,2,3');
|
||||
expect(linkedList.head.value).toBe(1);
|
||||
expect(linkedList.tail.value).toBe(3);
|
||||
|
||||
// Reverse linked list.
|
||||
linkedList.reverse();
|
||||
expect(linkedList.toString()).toBe('3,2,1');
|
||||
expect(linkedList.head.value).toBe(3);
|
||||
expect(linkedList.tail.value).toBe(1);
|
||||
|
||||
// Reverse linked list back to initial state.
|
||||
linkedList.reverse();
|
||||
expect(linkedList.toString()).toBe('1,2,3');
|
||||
expect(linkedList.head.value).toBe(1);
|
||||
expect(linkedList.tail.value).toBe(3);
|
||||
});
|
||||
});
|
||||
|
@ -207,4 +207,32 @@ export default class LinkedList {
|
||||
toString(callback) {
|
||||
return this.toArray().map(node => node.toString(callback)).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse a linked list.
|
||||
* @returns {LinkedList}
|
||||
*/
|
||||
reverse() {
|
||||
let currNode = this.head;
|
||||
let prevNode = null;
|
||||
let nextNode = null;
|
||||
|
||||
while (currNode) {
|
||||
// Store next node.
|
||||
nextNode = currNode.next;
|
||||
|
||||
// Change next node of the current node so it would link to previous node.
|
||||
currNode.next = prevNode;
|
||||
|
||||
// Move prevNode and currNode nodes one step forward.
|
||||
prevNode = currNode;
|
||||
currNode = nextNode;
|
||||
}
|
||||
|
||||
// Reset head and tail.
|
||||
this.tail = this.head;
|
||||
this.head = prevNode;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -110,24 +110,24 @@ Traverse(head)
|
||||
Pre: head is the head node in the list
|
||||
Post: the items in the list have been traversed
|
||||
n ← head
|
||||
while n = 0
|
||||
while n != ø
|
||||
yield n.value
|
||||
n ← n.next
|
||||
end while
|
||||
end Traverse
|
||||
```
|
||||
|
||||
|
||||
### Traverse in Reverse
|
||||
|
||||
```text
|
||||
ReverseTraversal(head, tail)
|
||||
Pre: head and tail belong to the same list
|
||||
Post: the items in the list have been traversed in reverse order
|
||||
if tail = ø
|
||||
if tail != ø
|
||||
curr ← tail
|
||||
while curr = head
|
||||
while curr != head
|
||||
prev ← head
|
||||
while prev.next = curr
|
||||
while prev.next != curr
|
||||
prev ← prev.next
|
||||
end while
|
||||
yield curr.value
|
||||
|
@ -217,4 +217,30 @@ describe('LinkedList', () => {
|
||||
expect(node.value.customValue).toBe('test2');
|
||||
expect(linkedList.find({ value: 2, customValue: 'test5' })).toBeNull();
|
||||
});
|
||||
|
||||
it('should reverse linked list', () => {
|
||||
const linkedList = new LinkedList();
|
||||
|
||||
// Add test values to linked list.
|
||||
linkedList
|
||||
.append(1)
|
||||
.append(2)
|
||||
.append(3);
|
||||
|
||||
expect(linkedList.toString()).toBe('1,2,3');
|
||||
expect(linkedList.head.value).toBe(1);
|
||||
expect(linkedList.tail.value).toBe(3);
|
||||
|
||||
// Reverse linked list.
|
||||
linkedList.reverse();
|
||||
expect(linkedList.toString()).toBe('3,2,1');
|
||||
expect(linkedList.head.value).toBe(3);
|
||||
expect(linkedList.tail.value).toBe(1);
|
||||
|
||||
// Reverse linked list back to initial state.
|
||||
linkedList.reverse();
|
||||
expect(linkedList.toString()).toBe('1,2,3');
|
||||
expect(linkedList.head.value).toBe(1);
|
||||
expect(linkedList.tail.value).toBe(3);
|
||||
});
|
||||
});
|
||||
|
@ -2,8 +2,8 @@ import LinkedList from '../linked-list/LinkedList';
|
||||
|
||||
export default class Stack {
|
||||
constructor() {
|
||||
// We're going to implement Queue based on LinkedList since this
|
||||
// structures a quite similar. Compare push/pop operations of the Stack
|
||||
// We're going to implement Stack based on LinkedList since these
|
||||
// structures are quite similar. Compare push/pop operations of the Stack
|
||||
// with append/deleteTail operations of LinkedList.
|
||||
this.linkedList = new LinkedList();
|
||||
}
|
||||
@ -12,7 +12,7 @@ export default class Stack {
|
||||
* @return {boolean}
|
||||
*/
|
||||
isEmpty() {
|
||||
// The queue is empty in case if its linked list don't have tail.
|
||||
// The stack is empty if its linked list doesn't have a tail.
|
||||
return !this.linkedList.tail;
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ export default class Stack {
|
||||
*/
|
||||
peek() {
|
||||
if (this.isEmpty()) {
|
||||
// If linked list is empty then there is nothing to peek from.
|
||||
// If the linked list is empty then there is nothing to peek from.
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ export default class Stack {
|
||||
*/
|
||||
push(value) {
|
||||
// Pushing means to lay the value on top of the stack. Therefore let's just add
|
||||
// new value at the end of the linked list.
|
||||
// the new value at the end of the linked list.
|
||||
this.linkedList.append(value);
|
||||
}
|
||||
|
||||
@ -42,8 +42,8 @@ export default class Stack {
|
||||
* @return {*}
|
||||
*/
|
||||
pop() {
|
||||
// Let's try to delete the last node from linked list (the tail).
|
||||
// If there is no tail in linked list (it is empty) just return null.
|
||||
// Let's try to delete the last node (the tail) from the linked list.
|
||||
// If there is no tail (the linked list is empty) just return null.
|
||||
const removedTail = this.linkedList.deleteTail();
|
||||
return removedTail ? removedTail.value : null;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user