Merge branch 'master' into patch-1

This commit is contained in:
Oleksii Trekhleb 2018-09-08 23:18:23 +03:00 committed by GitHub
commit 8b2f83e6c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 520 additions and 26 deletions

View File

@ -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

View 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)

View File

@ -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();
// });

View File

@ -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);
}

View 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)

View File

@ -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]);
});
});

View 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;
}
}

View File

@ -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).

View 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);
});
});

View 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);
});
});

View File

@ -0,0 +1,7 @@
/**
* @param {number} number
* @return {boolean}
*/
export default function isEven(number) {
return (number & 1) === 0;
}

View 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();
}

View 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)

View File

@ -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);
});
});

View 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;
}

View File

@ -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;
}
}

View File

@ -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);
});
});

View File

@ -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;
}
}

View File

@ -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

View File

@ -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);
});
});

View File

@ -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;
}