From 8676c1b9fe8e68a32e8a21b8f4ab0db25516631b Mon Sep 17 00:00:00 2001 From: HatimLokhandwala Date: Tue, 4 Sep 2018 19:51:09 +0530 Subject: [PATCH 01/12] Adding math algorithm to compute power and its tests (#172) * Adding math algorithm to compute power and its tests * adding more test cases, updating compute power js * Updating ReadMe for power computation algorithm --- src/algorithms/math/compute-power/README.md | 35 +++++++++++++++++++ .../__test__/computePower.test.js | 16 +++++++++ src/algorithms/math/compute-power/power.js | 23 ++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 src/algorithms/math/compute-power/README.md create mode 100644 src/algorithms/math/compute-power/__test__/computePower.test.js create mode 100644 src/algorithms/math/compute-power/power.js diff --git a/src/algorithms/math/compute-power/README.md b/src/algorithms/math/compute-power/README.md new file mode 100644 index 00000000..a0219c06 --- /dev/null +++ b/src/algorithms/math/compute-power/README.md @@ -0,0 +1,35 @@ +# Power(a,b) + +This computes power of (a,b) +eg: power(2,3) = 8 +power(10,0) = 1 + +The algorithm uses divide and conquer approach to compute power. +Currently the algorithm work for two positive integers X and Y +Lets say there are two numbers X and Y. +At each step of the algorithm: + 1. if Y is even + then power(X, Y/2) * power(X, Y/2) is computed + 2. if Y is odd + then X * power(X, Y/2) * power(X, Y/2) is computed + +At each step since power(X,Y/2) is called twice, this is optimised by saving the result of power(X, Y/2) in a variable (lets say res). +And then res is multiplied by self. + +Illustration through example +power (2,5) + - 2 * power(2,2) * power(2,2) + power(2,2) + - power(2,1) * power(2,1) + power(2,1) + - return 2 + +Going up the tree once the end values are computed + power(2,1) = 2 + power(2,2) = power(2,1) * power(2,1) = 2 * 2 = 4 +power(2,5) = 2 * power(2,2) * power(2,2) = 2 * 4 * 4 = 32 + + +Complexity relation: T(n) = T(n/2) + 1 + +Time complexity of the algorithm: O(logn) diff --git a/src/algorithms/math/compute-power/__test__/computePower.test.js b/src/algorithms/math/compute-power/__test__/computePower.test.js new file mode 100644 index 00000000..944d562b --- /dev/null +++ b/src/algorithms/math/compute-power/__test__/computePower.test.js @@ -0,0 +1,16 @@ +import computePower from '../power'; + +describe('computePower', () => { + it('should compute Power', () => { + expect(computePower(1, 1)).toBe(1); + expect(computePower(2, 0)).toBe(1); + expect(computePower(3, 4)).toBe(81); + expect(computePower(190, 2)).toBe(36100); + expect(computePower(16, 16)).toBe(18446744073709552000); + expect(computePower(100, 9)).toBe(1000000000000000000); + expect(computePower(9, 16)).toBe(1853020188851841); + expect(computePower(11, 5)).toBe(161051); + expect(computePower(13, 11)).toBe(1792160394037); + expect(computePower(7, 21)).toBe(558545864083284000); + }); +}); diff --git a/src/algorithms/math/compute-power/power.js b/src/algorithms/math/compute-power/power.js new file mode 100644 index 00000000..2fc3ee68 --- /dev/null +++ b/src/algorithms/math/compute-power/power.js @@ -0,0 +1,23 @@ +/** + * @param {number1} number + * @param {number2} number + * @return {number1^number2} + */ + +// recursive implementation to compute power +export default function computePower(number1, number2) { + let val = 0; + let res = 0; + if (number2 === 0) { // if number2 is 0 + val = 1; + } else if (number2 === 1) { // if number2 is 1 return number 1 as it is + val = number1; + } else if (number2 % 2 === 0) { // if number2 is even + res = computePower(number1, number2 / 2); + val = res * res; + } else { // if number2 is odd + res = computePower(number1, Math.floor(number2 / 2)); + val = res * res * number1; + } + return val; +} From 8116aa7cfb69e77b36eaf1441e5f96c8051cf863 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Tue, 4 Sep 2018 17:35:48 +0300 Subject: [PATCH 02/12] Refactor fast powering algorithm. --- README.md | 2 ++ .../__test__/computePower.test.js | 16 ------------- .../README.md | 6 ++++- .../__test__/fastPowering.test.js | 23 +++++++++++++++++++ .../fastPowering.js} | 16 ++++++------- 5 files changed, 38 insertions(+), 25 deletions(-) delete mode 100644 src/algorithms/math/compute-power/__test__/computePower.test.js rename src/algorithms/math/{compute-power => fast-powering}/README.md (95%) create mode 100644 src/algorithms/math/fast-powering/__test__/fastPowering.test.js rename src/algorithms/math/{compute-power/power.js => fast-powering/fastPowering.js} (54%) diff --git a/README.md b/README.md index 915b3fb3..527f30dc 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,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 @@ -163,6 +164,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 diff --git a/src/algorithms/math/compute-power/__test__/computePower.test.js b/src/algorithms/math/compute-power/__test__/computePower.test.js deleted file mode 100644 index 944d562b..00000000 --- a/src/algorithms/math/compute-power/__test__/computePower.test.js +++ /dev/null @@ -1,16 +0,0 @@ -import computePower from '../power'; - -describe('computePower', () => { - it('should compute Power', () => { - expect(computePower(1, 1)).toBe(1); - expect(computePower(2, 0)).toBe(1); - expect(computePower(3, 4)).toBe(81); - expect(computePower(190, 2)).toBe(36100); - expect(computePower(16, 16)).toBe(18446744073709552000); - expect(computePower(100, 9)).toBe(1000000000000000000); - expect(computePower(9, 16)).toBe(1853020188851841); - expect(computePower(11, 5)).toBe(161051); - expect(computePower(13, 11)).toBe(1792160394037); - expect(computePower(7, 21)).toBe(558545864083284000); - }); -}); diff --git a/src/algorithms/math/compute-power/README.md b/src/algorithms/math/fast-powering/README.md similarity index 95% rename from src/algorithms/math/compute-power/README.md rename to src/algorithms/math/fast-powering/README.md index a0219c06..d8364888 100644 --- a/src/algorithms/math/compute-power/README.md +++ b/src/algorithms/math/fast-powering/README.md @@ -1,4 +1,4 @@ -# Power(a,b) +# Fast Powering Algorithm This computes power of (a,b) eg: power(2,3) = 8 @@ -33,3 +33,7 @@ power(2,5) = 2 * power(2,2) * power(2,2) = 2 * 4 * 4 = 32 Complexity relation: T(n) = T(n/2) + 1 Time complexity of the algorithm: O(logn) + +## References + + diff --git a/src/algorithms/math/fast-powering/__test__/fastPowering.test.js b/src/algorithms/math/fast-powering/__test__/fastPowering.test.js new file mode 100644 index 00000000..0a5da756 --- /dev/null +++ b/src/algorithms/math/fast-powering/__test__/fastPowering.test.js @@ -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); + }); +}); diff --git a/src/algorithms/math/compute-power/power.js b/src/algorithms/math/fast-powering/fastPowering.js similarity index 54% rename from src/algorithms/math/compute-power/power.js rename to src/algorithms/math/fast-powering/fastPowering.js index 2fc3ee68..6841eb03 100644 --- a/src/algorithms/math/compute-power/power.js +++ b/src/algorithms/math/fast-powering/fastPowering.js @@ -1,11 +1,11 @@ /** - * @param {number1} number - * @param {number2} number - * @return {number1^number2} + * Recursive implementation to compute power. + * + * @param {number} number1 + * @param {number} number2 + * @return {number} */ - -// recursive implementation to compute power -export default function computePower(number1, number2) { +export default function fastPowering(number1, number2) { let val = 0; let res = 0; if (number2 === 0) { // if number2 is 0 @@ -13,10 +13,10 @@ export default function computePower(number1, number2) { } else if (number2 === 1) { // if number2 is 1 return number 1 as it is val = number1; } else if (number2 % 2 === 0) { // if number2 is even - res = computePower(number1, number2 / 2); + res = fastPowering(number1, number2 / 2); val = res * res; } else { // if number2 is odd - res = computePower(number1, Math.floor(number2 / 2)); + res = fastPowering(number1, Math.floor(number2 / 2)); val = res * res * number1; } return val; From 7dc60c96bfd13c52ed79d75cdfa99dd53d36e7b1 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Tue, 4 Sep 2018 18:27:38 +0300 Subject: [PATCH 03/12] Add Fast Powering algorithm. --- src/algorithms/math/fast-powering/README.md | 83 +++++++++++++------ .../math/fast-powering/fastPowering.js | 39 +++++---- 2 files changed, 79 insertions(+), 43 deletions(-) diff --git a/src/algorithms/math/fast-powering/README.md b/src/algorithms/math/fast-powering/README.md index d8364888..13745afa 100644 --- a/src/algorithms/math/fast-powering/README.md +++ b/src/algorithms/math/fast-powering/README.md @@ -1,39 +1,68 @@ # Fast Powering Algorithm -This computes power of (a,b) -eg: power(2,3) = 8 -power(10,0) = 1 +**The power of a number** says how many times to use the number in a +multiplication. -The algorithm uses divide and conquer approach to compute power. -Currently the algorithm work for two positive integers X and Y -Lets say there are two numbers X and Y. -At each step of the algorithm: - 1. if Y is even - then power(X, Y/2) * power(X, Y/2) is computed - 2. if Y is odd - then X * power(X, Y/2) * power(X, Y/2) is computed +It is written as a small number to the right and above the base number. -At each step since power(X,Y/2) is called twice, this is optimised by saving the result of power(X, Y/2) in a variable (lets say res). -And then res is multiplied by self. +![Power](https://www.mathsisfun.com/algebra/images/exponent-8-2.svg) -Illustration through example -power (2,5) - - 2 * power(2,2) * power(2,2) - power(2,2) - - power(2,1) * power(2,1) - power(2,1) - - return 2 +## Naive Algorithm Complexity -Going up the tree once the end values are computed - power(2,1) = 2 - power(2,2) = power(2,1) * power(2,1) = 2 * 2 = 4 -power(2,5) = 2 * power(2,2) * power(2,2) = 2 * 4 * 4 = 32 +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`). -Complexity relation: T(n) = T(n/2) + 1 +This operation will take `O(n)` time since we need to do multiplication operation +exactly `n` times. -Time complexity of the algorithm: O(logn) +## 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) diff --git a/src/algorithms/math/fast-powering/fastPowering.js b/src/algorithms/math/fast-powering/fastPowering.js index 6841eb03..4f4a6b35 100644 --- a/src/algorithms/math/fast-powering/fastPowering.js +++ b/src/algorithms/math/fast-powering/fastPowering.js @@ -1,23 +1,30 @@ /** + * Fast Powering Algorithm. * Recursive implementation to compute power. * - * @param {number} number1 - * @param {number} number2 + * 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(number1, number2) { - let val = 0; - let res = 0; - if (number2 === 0) { // if number2 is 0 - val = 1; - } else if (number2 === 1) { // if number2 is 1 return number 1 as it is - val = number1; - } else if (number2 % 2 === 0) { // if number2 is even - res = fastPowering(number1, number2 / 2); - val = res * res; - } else { // if number2 is odd - res = fastPowering(number1, Math.floor(number2 / 2)); - val = res * res * number1; +export default function fastPowering(base, power) { + if (power === 0) { + // Anything that is raised to the power of zero is 1. + return 1; } - return val; + + 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; } From f1a32d9f851845bd6b14c34dd270de16c03c8960 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Wed, 5 Sep 2018 11:38:22 +0300 Subject: [PATCH 04/12] Add info about the purpose of the repo to main README. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 527f30dc..2239695f 100644 --- a/README.md +++ b/README.md @@ -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 From d038c402dd60413cacd6d45bdbc0255b96e32597 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Wed, 5 Sep 2018 11:39:39 +0300 Subject: [PATCH 05/12] Add info about the purpose of the repo to main README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2239695f..bc722406 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ _Read this in other languages:_ [_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.* +only and it is **not** meant to be used for production.* ## Data Structures From 4989a6a3b018501df52031950716f6cdc6febf60 Mon Sep 17 00:00:00 2001 From: "Hanh D. TRAN" Date: Sat, 8 Sep 2018 17:55:51 +0900 Subject: [PATCH 06/12] Add LinkedList traversal and reverse implementations (#194) * Add LinkedList traverse function * Add LinkedList reverse traversal implementations * Update LinkedList traverse function * Update LinkedList reverse traversal and test cases * Update LinkedList traversal tests --- src/data-structures/linked-list/LinkedList.js | 62 +++++++++++++++++++ src/data-structures/linked-list/README.md | 10 +-- .../linked-list/__test__/LinkedList.test.js | 37 +++++++++++ 3 files changed, 104 insertions(+), 5 deletions(-) diff --git a/src/data-structures/linked-list/LinkedList.js b/src/data-structures/linked-list/LinkedList.js index dfb17aaf..15b54e25 100644 --- a/src/data-structures/linked-list/LinkedList.js +++ b/src/data-structures/linked-list/LinkedList.js @@ -207,4 +207,66 @@ export default class LinkedList { toString(callback) { return this.toArray().map(node => node.toString(callback)).toString(); } + + /** + * Traverse through all nodes of the list from head to tail + * @param {*} callback + * @return {LinkedListNode[]} + */ + traverse(callback = undefined) { + if (typeof callback !== 'function') { + throw new TypeError(`traverse method requires a callback function as an argument.\nArgument given: ${typeof callback}`); + } + + let currentNode = this.head; + const traversedNodes = []; + + while (currentNode) { + traversedNodes.push(callback(currentNode.value)); + currentNode = currentNode.next; + } + + return traversedNodes; + } + + /** + * The items in the list have been traversed in reverse order + */ + reverseTraversal(node, callback = undefined) { + if (typeof callback !== 'function') { + throw new TypeError(`reverseTraverse method requires a callback function as an argument.\nArgument given: ${typeof callback}`); + } + + if (!node) return []; + + return this.reverseTraversal(node.next, callback).concat(callback(node.value)); + } + + /** + * Reverse a singly linked list use to three variables + * @returns {ReservedLinkedList} + */ + 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 + currNode.next = prevNode; + + // Move forward prev and current nodes one step + prevNode = currNode; + currNode = nextNode; + } + + // Reset head, tail + this.tail = this.head; + this.head = prevNode; + + return this; + } } diff --git a/src/data-structures/linked-list/README.md b/src/data-structures/linked-list/README.md index 8f1a46c8..bb0ad476 100644 --- a/src/data-structures/linked-list/README.md +++ b/src/data-structures/linked-list/README.md @@ -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 diff --git a/src/data-structures/linked-list/__test__/LinkedList.test.js b/src/data-structures/linked-list/__test__/LinkedList.test.js index 795bb247..f88891cb 100644 --- a/src/data-structures/linked-list/__test__/LinkedList.test.js +++ b/src/data-structures/linked-list/__test__/LinkedList.test.js @@ -217,4 +217,41 @@ describe('LinkedList', () => { expect(node.value.customValue).toBe('test2'); expect(linkedList.find({ value: 2, customValue: 'test5' })).toBeNull(); }); + + it('should traverse through all nodes of the list from head to tail with callback', () => { + const linkedList = new LinkedList(); + + linkedList + .append(1) + .append(2) + .append(3); + + expect(linkedList.traverse(value => value * 2)).toEqual([2, 4, 6]); + expect(() => linkedList.traverse()).toThrow(); + }); + + 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(); + }); + + it('should reverse the singly linked list', () => { + const linkedList = new LinkedList(); + + linkedList + .append(1) + .append(2) + .append(3); + + expect(linkedList.toString()).toBe('1,2,3'); + expect(linkedList.reverse().toString()).toBe('3,2,1'); + }); }); From 2feec48ea69eac8fb927bc6e437977979abafc91 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Sat, 8 Sep 2018 12:16:15 +0300 Subject: [PATCH 07/12] Add more test cases for linked list reversion. --- src/data-structures/linked-list/LinkedList.js | 12 ++++++------ .../linked-list/__test__/LinkedList.test.js | 13 +++++++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/data-structures/linked-list/LinkedList.js b/src/data-structures/linked-list/LinkedList.js index 15b54e25..d98b6284 100644 --- a/src/data-structures/linked-list/LinkedList.js +++ b/src/data-structures/linked-list/LinkedList.js @@ -243,8 +243,8 @@ export default class LinkedList { } /** - * Reverse a singly linked list use to three variables - * @returns {ReservedLinkedList} + * Reverse a linked list. + * @returns {LinkedList} */ reverse() { let currNode = this.head; @@ -252,18 +252,18 @@ export default class LinkedList { let nextNode = null; while (currNode) { - // Store next node + // Store next node. nextNode = currNode.next; - // Change next node of the current + // Change next node of the current node so it would link to previous node. currNode.next = prevNode; - // Move forward prev and current nodes one step + // Move prevNode and currNode nodes one step forward. prevNode = currNode; currNode = nextNode; } - // Reset head, tail + // Reset head and tail. this.tail = this.head; this.head = prevNode; diff --git a/src/data-structures/linked-list/__test__/LinkedList.test.js b/src/data-structures/linked-list/__test__/LinkedList.test.js index f88891cb..32290145 100644 --- a/src/data-structures/linked-list/__test__/LinkedList.test.js +++ b/src/data-structures/linked-list/__test__/LinkedList.test.js @@ -243,15 +243,24 @@ describe('LinkedList', () => { expect(() => linkedList.reverseTraversal(linkedList.head)).toThrow(); }); - it('should reverse the singly linked list', () => { + 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.reverse().toString()).toBe('3,2,1'); + 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); }); }); From 80ecbe0b3ed34e51893e77b95606026cf381ffe4 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Sat, 8 Sep 2018 22:20:52 +0300 Subject: [PATCH 08/12] Move linked list traversals into separate section. --- README.md | 3 ++ .../linked-list/reverse-traversal/README.md | 19 ++++++++++ .../__test__/reverseTraversal.test.js | 36 +++++++++++++++++++ .../reverse-traversal/reverseTraversal.js | 24 +++++++++++++ .../linked-list/traversal/README.md | 19 ++++++++++ .../traversal/__test__/traversal.test.js | 22 ++++++++++++ .../linked-list/traversal/traversal.js | 18 ++++++++++ src/data-structures/linked-list/LinkedList.js | 34 ------------------ .../linked-list/__test__/LinkedList.test.js | 32 ++++------------- 9 files changed, 147 insertions(+), 60 deletions(-) create mode 100644 src/algorithms/linked-list/reverse-traversal/README.md create mode 100644 src/algorithms/linked-list/reverse-traversal/__test__/reverseTraversal.test.js create mode 100644 src/algorithms/linked-list/reverse-traversal/reverseTraversal.js create mode 100644 src/algorithms/linked-list/traversal/README.md create mode 100644 src/algorithms/linked-list/traversal/__test__/traversal.test.js create mode 100644 src/algorithms/linked-list/traversal/traversal.js diff --git a/README.md b/README.md index bc722406..334d48b0 100644 --- a/README.md +++ b/README.md @@ -109,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) diff --git a/src/algorithms/linked-list/reverse-traversal/README.md b/src/algorithms/linked-list/reverse-traversal/README.md new file mode 100644 index 00000000..89187e4f --- /dev/null +++ b/src/algorithms/linked-list/reverse-traversal/README.md @@ -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) diff --git a/src/algorithms/linked-list/reverse-traversal/__test__/reverseTraversal.test.js b/src/algorithms/linked-list/reverse-traversal/__test__/reverseTraversal.test.js new file mode 100644 index 00000000..63ddb24c --- /dev/null +++ b/src/algorithms/linked-list/reverse-traversal/__test__/reverseTraversal.test.js @@ -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(); +// }); diff --git a/src/algorithms/linked-list/reverse-traversal/reverseTraversal.js b/src/algorithms/linked-list/reverse-traversal/reverseTraversal.js new file mode 100644 index 00000000..d39260aa --- /dev/null +++ b/src/algorithms/linked-list/reverse-traversal/reverseTraversal.js @@ -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); +} diff --git a/src/algorithms/linked-list/traversal/README.md b/src/algorithms/linked-list/traversal/README.md new file mode 100644 index 00000000..25609518 --- /dev/null +++ b/src/algorithms/linked-list/traversal/README.md @@ -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) diff --git a/src/algorithms/linked-list/traversal/__test__/traversal.test.js b/src/algorithms/linked-list/traversal/__test__/traversal.test.js new file mode 100644 index 00000000..77ce79b6 --- /dev/null +++ b/src/algorithms/linked-list/traversal/__test__/traversal.test.js @@ -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]); + }); +}); diff --git a/src/algorithms/linked-list/traversal/traversal.js b/src/algorithms/linked-list/traversal/traversal.js new file mode 100644 index 00000000..cfbfe4fb --- /dev/null +++ b/src/algorithms/linked-list/traversal/traversal.js @@ -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; + } +} diff --git a/src/data-structures/linked-list/LinkedList.js b/src/data-structures/linked-list/LinkedList.js index d98b6284..e256b1fe 100644 --- a/src/data-structures/linked-list/LinkedList.js +++ b/src/data-structures/linked-list/LinkedList.js @@ -208,40 +208,6 @@ export default class LinkedList { return this.toArray().map(node => node.toString(callback)).toString(); } - /** - * Traverse through all nodes of the list from head to tail - * @param {*} callback - * @return {LinkedListNode[]} - */ - traverse(callback = undefined) { - if (typeof callback !== 'function') { - throw new TypeError(`traverse method requires a callback function as an argument.\nArgument given: ${typeof callback}`); - } - - let currentNode = this.head; - const traversedNodes = []; - - while (currentNode) { - traversedNodes.push(callback(currentNode.value)); - currentNode = currentNode.next; - } - - return traversedNodes; - } - - /** - * The items in the list have been traversed in reverse order - */ - reverseTraversal(node, callback = undefined) { - if (typeof callback !== 'function') { - throw new TypeError(`reverseTraverse method requires a callback function as an argument.\nArgument given: ${typeof callback}`); - } - - if (!node) return []; - - return this.reverseTraversal(node.next, callback).concat(callback(node.value)); - } - /** * Reverse a linked list. * @returns {LinkedList} diff --git a/src/data-structures/linked-list/__test__/LinkedList.test.js b/src/data-structures/linked-list/__test__/LinkedList.test.js index 32290145..3a4241c9 100644 --- a/src/data-structures/linked-list/__test__/LinkedList.test.js +++ b/src/data-structures/linked-list/__test__/LinkedList.test.js @@ -218,31 +218,6 @@ describe('LinkedList', () => { expect(linkedList.find({ value: 2, customValue: 'test5' })).toBeNull(); }); - it('should traverse through all nodes of the list from head to tail with callback', () => { - const linkedList = new LinkedList(); - - linkedList - .append(1) - .append(2) - .append(3); - - expect(linkedList.traverse(value => value * 2)).toEqual([2, 4, 6]); - expect(() => linkedList.traverse()).toThrow(); - }); - - 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(); - }); - it('should reverse linked list', () => { const linkedList = new LinkedList(); @@ -258,9 +233,14 @@ describe('LinkedList', () => { // 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); }); }); From 6f27113993cc53f988175ac6e3ca9368f3e1a1a9 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Sat, 8 Sep 2018 22:25:23 +0300 Subject: [PATCH 09/12] Add reverse() method for doubly linked list. --- .../doubly-linked-list/DoublyLinkedList.js | 28 +++++++++++++++++++ .../__test__/DoublyLinkedList.test.js | 26 +++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/data-structures/doubly-linked-list/DoublyLinkedList.js b/src/data-structures/doubly-linked-list/DoublyLinkedList.js index 4afb404b..e66e016d 100644 --- a/src/data-structures/doubly-linked-list/DoublyLinkedList.js +++ b/src/data-structures/doubly-linked-list/DoublyLinkedList.js @@ -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; + } } diff --git a/src/data-structures/doubly-linked-list/__test__/DoublyLinkedList.test.js b/src/data-structures/doubly-linked-list/__test__/DoublyLinkedList.test.js index 5e4e81ea..ca6be64f 100644 --- a/src/data-structures/doubly-linked-list/__test__/DoublyLinkedList.test.js +++ b/src/data-structures/doubly-linked-list/__test__/DoublyLinkedList.test.js @@ -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); + }); }); From 1a62078f26fb85719ce61ef1f282baaa3b80a6d0 Mon Sep 17 00:00:00 2001 From: Vinicius Date: Sat, 8 Sep 2018 16:35:24 -0300 Subject: [PATCH 10/12] Improve JSDocs in Stack.js (#203) The functions' comments were copied from Queue.js, but some words were not replaced. I also made some changes to the wording for clarification. --- src/data-structures/stack/Stack.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/data-structures/stack/Stack.js b/src/data-structures/stack/Stack.js index 762b428e..dd007d2c 100644 --- a/src/data-structures/stack/Stack.js +++ b/src/data-structures/stack/Stack.js @@ -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; } From bc8943dee2e53402919ff0bd1a70d762e4737ad8 Mon Sep 17 00:00:00 2001 From: ADITYA Date: Sat, 8 Sep 2018 15:51:03 -0400 Subject: [PATCH 11/12] Perform multiplication of any two integers positive or negative through bit manipulations (#201) --- src/algorithms/math/bits/README.md | 60 ++++++++++++++----- .../math/bits/__test__/isEven.test.js | 11 ++++ .../math/bits/__test__/multiply.test.js | 16 +++++ src/algorithms/math/bits/isEven.js | 7 +++ src/algorithms/math/bits/multiply.js | 29 +++++++++ 5 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 src/algorithms/math/bits/__test__/isEven.test.js create mode 100644 src/algorithms/math/bits/__test__/multiply.test.js create mode 100644 src/algorithms/math/bits/isEven.js create mode 100644 src/algorithms/math/bits/multiply.js diff --git a/src/algorithms/math/bits/README.md b/src/algorithms/math/bits/README.md index b432043f..31da17a4 100644 --- a/src/algorithms/math/bits/README.md +++ b/src/algorithms/math/bits/README.md @@ -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,35 @@ 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. + +``` +Number: 5 +isEven: false + +Number: 4 +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 +72,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 +101,29 @@ 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 : + +```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 +143,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 +190,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). diff --git a/src/algorithms/math/bits/__test__/isEven.test.js b/src/algorithms/math/bits/__test__/isEven.test.js new file mode 100644 index 00000000..a3f965e4 --- /dev/null +++ b/src/algorithms/math/bits/__test__/isEven.test.js @@ -0,0 +1,11 @@ +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); + }); +}); diff --git a/src/algorithms/math/bits/__test__/multiply.test.js b/src/algorithms/math/bits/__test__/multiply.test.js new file mode 100644 index 00000000..80269b13 --- /dev/null +++ b/src/algorithms/math/bits/__test__/multiply.test.js @@ -0,0 +1,16 @@ +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); + }); +}); diff --git a/src/algorithms/math/bits/isEven.js b/src/algorithms/math/bits/isEven.js new file mode 100644 index 00000000..bfc3bef3 --- /dev/null +++ b/src/algorithms/math/bits/isEven.js @@ -0,0 +1,7 @@ +/** + * @param {number} number + * @return bool + */ +export default function isEven(number) { + return (number & 1) === 0; +} diff --git a/src/algorithms/math/bits/multiply.js b/src/algorithms/math/bits/multiply.js new file mode 100644 index 00000000..4f2e8c76 --- /dev/null +++ b/src/algorithms/math/bits/multiply.js @@ -0,0 +1,29 @@ +import divideByTwo from './divideByTwo'; +import isEven from './isEven'; +import multiplyByTwo from './multiplyByTwo'; + +/** + * FUNCTION DEFINITION + * multiply(a, b) = 0 if a is zero or b is zero or if both a and b are zeros + * multiply(a, b) = multiply(2a, b/2) if b is even + * multiply(a, b) = multiply(2a, (b-1)/2) + a 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 + * + * COMPLEXITY + * O(log b) + * @param {number} a + * @param {number} b + * @return {number} a * b + */ +export default function multiply(a, b) { + if (b === 0 || a === 0) { + return 0; + } + + 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(); +} From de6a24e0d166a72bcc3f79b7e1920500a1df69f8 Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Sat, 8 Sep 2018 23:09:04 +0300 Subject: [PATCH 12/12] Minor code style fixes for bitwise multiplication. --- src/algorithms/math/bits/README.md | 17 ++++++----- .../math/bits/__test__/isEven.test.js | 8 +++++ .../math/bits/__test__/multiply.test.js | 2 ++ src/algorithms/math/bits/isEven.js | 2 +- src/algorithms/math/bits/multiply.js | 29 +++++++++++++------ 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/algorithms/math/bits/README.md b/src/algorithms/math/bits/README.md index 31da17a4..98b6c4dd 100644 --- a/src/algorithms/math/bits/README.md +++ b/src/algorithms/math/bits/README.md @@ -38,12 +38,14 @@ This method is a combination of "Clear Bit" and "Set Bit" methods. #### 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. -``` -Number: 5 +```text +Number: 5 = 0b0101 isEven: false -Number: 4 +Number: 4 = 0b0100 isEven: true ``` @@ -108,18 +110,19 @@ inverting all of the bits of the number and adding 1 to it. #### Multiply Two Signed Numbers This method multiplies two signed integer numbers using bitwise operators. -This method is based on the following : +This method is based on the following facts: ```text -a * b can be written in the below formats +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. +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. diff --git a/src/algorithms/math/bits/__test__/isEven.test.js b/src/algorithms/math/bits/__test__/isEven.test.js index a3f965e4..b21bbfb0 100644 --- a/src/algorithms/math/bits/__test__/isEven.test.js +++ b/src/algorithms/math/bits/__test__/isEven.test.js @@ -7,5 +7,13 @@ describe('isEven', () => { 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); }); }); diff --git a/src/algorithms/math/bits/__test__/multiply.test.js b/src/algorithms/math/bits/__test__/multiply.test.js index 80269b13..c2195704 100644 --- a/src/algorithms/math/bits/__test__/multiply.test.js +++ b/src/algorithms/math/bits/__test__/multiply.test.js @@ -12,5 +12,7 @@ describe('multiply', () => { 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); }); }); diff --git a/src/algorithms/math/bits/isEven.js b/src/algorithms/math/bits/isEven.js index bfc3bef3..843b4d28 100644 --- a/src/algorithms/math/bits/isEven.js +++ b/src/algorithms/math/bits/isEven.js @@ -1,6 +1,6 @@ /** * @param {number} number - * @return bool + * @return {boolean} */ export default function isEven(number) { return (number & 1) === 0; diff --git a/src/algorithms/math/bits/multiply.js b/src/algorithms/math/bits/multiply.js index 4f2e8c76..4dd33a9d 100644 --- a/src/algorithms/math/bits/multiply.js +++ b/src/algorithms/math/bits/multiply.js @@ -1,27 +1,38 @@ +import multiplyByTwo from './multiplyByTwo'; import divideByTwo from './divideByTwo'; import isEven from './isEven'; -import multiplyByTwo from './multiplyByTwo'; /** - * FUNCTION DEFINITION - * multiply(a, b) = 0 if a is zero or b is zero or if both a and b are zeros - * multiply(a, b) = multiply(2a, b/2) if b is even - * multiply(a, b) = multiply(2a, (b-1)/2) + a 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 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) * - * COMPLEXITY - * O(log b) * @param {number} a * @param {number} b - * @return {number} a * 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());