From 8da675452333c2a50d93729d8e1b0214c50eaaac Mon Sep 17 00:00:00 2001 From: Oleksii Trekhleb Date: Wed, 28 Mar 2018 17:01:46 +0300 Subject: [PATCH] Add Stack. --- README.md | 6 +- src/data-structures/linked-list/LinkedList.js | 17 ++++ .../linked-list/__test__/LinkedList.test.js | 90 ++++++++++++++----- src/data-structures/queue/Queue.js | 12 +-- src/data-structures/queue/README.md | 6 -- .../queue/__test__/Queue.test.js | 26 +++--- src/data-structures/stack/README.md | 1 + src/data-structures/stack/Stack.js | 28 ++++++ .../stack/__test__/Stack.test.js | 51 +++++++++++ 9 files changed, 189 insertions(+), 48 deletions(-) create mode 100644 src/data-structures/stack/README.md create mode 100644 src/data-structures/stack/Stack.js create mode 100644 src/data-structures/stack/__test__/Stack.test.js diff --git a/README.md b/README.md index 7486af980..143f33d3d 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ ## Data Structures -- [Linked List](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/linked-list) -- [Hash Table](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/hash-table) -- [Queue](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/queue) +1. [Linked List](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/linked-list) +2. [Queue](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/queue) +3. [Hash Table](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/hash-table) ## Running Tests diff --git a/src/data-structures/linked-list/LinkedList.js b/src/data-structures/linked-list/LinkedList.js index 3a3e9184f..f8968aaae 100644 --- a/src/data-structures/linked-list/LinkedList.js +++ b/src/data-structures/linked-list/LinkedList.js @@ -160,6 +160,23 @@ export default class LinkedList { return deletedTail; } + deleteHead() { + if (!this.head) { + return null; + } + + const deletedHead = this.head; + + if (this.head.next) { + this.head = this.head.next; + } else { + this.head = null; + this.tail = null; + } + + return deletedHead; + } + findByKey(key) { let currentNode = this.head; diff --git a/src/data-structures/linked-list/__test__/LinkedList.test.js b/src/data-structures/linked-list/__test__/LinkedList.test.js index bb58f49f8..2806e68ea 100644 --- a/src/data-structures/linked-list/__test__/LinkedList.test.js +++ b/src/data-structures/linked-list/__test__/LinkedList.test.js @@ -43,6 +43,8 @@ describe('LinkedList', () => { it('should delete node by value from linked list', () => { const linkedList = new LinkedList(); + expect(linkedList.deleteByValue(5)).toBeNull(); + linkedList.append({ value: 1 }); linkedList.append({ value: 2 }); linkedList.append({ value: 3 }); @@ -80,55 +82,103 @@ describe('LinkedList', () => { expect(linkedList.tail.toString()).toBe('2'); }); + it('should delete node by key from linked list', () => { + const linkedList = new LinkedList(); + + expect(linkedList.deleteByKey('key')).toBeNull(); + + linkedList.append({ value: 1, key: 'test1' }); + linkedList.append({ value: 2, key: 'test2' }); + linkedList.append({ value: 3, key: 'test3' }); + linkedList.append({ value: 4, key: 'test4' }); + + const deletedNode1 = linkedList.deleteByKey('test2'); + expect(deletedNode1.key).toBe('test2'); + expect(linkedList.toString()).toBe('test1:1,test3:3,test4:4'); + + const deletedNode2 = linkedList.deleteByKey('test1'); + expect(deletedNode2.key).toBe('test1'); + expect(linkedList.toString()).toBe('test3:3,test4:4'); + + const deletedNode3 = linkedList.deleteByKey('test4'); + expect(deletedNode3.key).toBe('test4'); + expect(linkedList.toString()).toBe('test3:3'); + }); + it('should delete linked list tail', () => { const linkedList = new LinkedList(); + linkedList.append({ value: 1 }); + linkedList.append({ value: 2 }); + linkedList.append({ value: 3 }); + + expect(linkedList.head.toString()).toBe('1'); + expect(linkedList.tail.toString()).toBe('3'); + + const deletedNode1 = linkedList.deleteTail(); + + expect(deletedNode1.value).toBe(3); + expect(linkedList.toString()).toBe('1,2'); + expect(linkedList.head.toString()).toBe('1'); + expect(linkedList.tail.toString()).toBe('2'); + + const deletedNode2 = linkedList.deleteTail(); + + expect(deletedNode2.value).toBe(2); + expect(linkedList.toString()).toBe('1'); + expect(linkedList.head.toString()).toBe('1'); + expect(linkedList.tail.toString()).toBe('1'); + + const deletedNode3 = linkedList.deleteTail(); + + expect(deletedNode3.value).toBe(1); + expect(linkedList.toString()).toBe(''); + expect(linkedList.head).toBeNull(); + expect(linkedList.tail).toBeNull(); + }); + + it('should delete linked list head', () => { + const linkedList = new LinkedList(); + + expect(linkedList.deleteHead()).toBeNull(); + linkedList.append({ value: 1 }); linkedList.append({ value: 2 }); expect(linkedList.head.toString()).toBe('1'); expect(linkedList.tail.toString()).toBe('2'); - const deletedNode1 = linkedList.deleteTail(); + const deletedNode1 = linkedList.deleteHead(); - expect(deletedNode1.value).toBe(2); - expect(linkedList.toString()).toBe('1'); - expect(linkedList.head.toString()).toBe('1'); - expect(linkedList.tail.toString()).toBe('1'); + expect(deletedNode1.value).toBe(1); + expect(linkedList.toString()).toBe('2'); + expect(linkedList.head.toString()).toBe('2'); + expect(linkedList.tail.toString()).toBe('2'); - const deletedNode2 = linkedList.deleteTail(); + const deletedNode2 = linkedList.deleteHead(); - expect(deletedNode2.value).toBe(1); + expect(deletedNode2.value).toBe(2); expect(linkedList.toString()).toBe(''); expect(linkedList.head).toBeNull(); expect(linkedList.tail).toBeNull(); }); - it('should delete node by key from linked list', () => { - const linkedList = new LinkedList(); - - linkedList.append({ value: 1, key: 'test1' }); - linkedList.append({ value: 2, key: 'test2' }); - linkedList.append({ value: 3, key: 'test3' }); - - const deletedNode = linkedList.deleteByKey('test2'); - expect(deletedNode.key).toBe('test2'); - expect(linkedList.toString()).toBe('test1:1,test3:3'); - }); - it('should append unique nodes', () => { const linkedList = new LinkedList(); linkedList.appendUnique({ value: 1, key: 'test1' }); linkedList.appendUnique({ value: 2, key: 'test2' }); linkedList.appendUnique({ value: 3, key: 'test2' }); + linkedList.appendUnique({ value: 5, key: 'test1' }); - expect(linkedList.toString()).toBe('test1:1,test2:3'); + expect(linkedList.toString()).toBe('test1:5,test2:3'); }); it('should find node by its key', () => { const linkedList = new LinkedList(); + expect(linkedList.findByKey('test')).toBeNull(); + linkedList.appendUnique({ value: 1, key: 'test1' }); linkedList.appendUnique({ value: 2, key: 'test2' }); linkedList.appendUnique({ value: 3, key: 'test3' }); diff --git a/src/data-structures/queue/Queue.js b/src/data-structures/queue/Queue.js index fa5db5410..b2d504581 100644 --- a/src/data-structures/queue/Queue.js +++ b/src/data-structures/queue/Queue.js @@ -10,19 +10,19 @@ export default class Queue { } peek() { - if (!this.linkedList.tail) { + if (!this.linkedList.head) { return null; } - return this.linkedList.tail.value; + return this.linkedList.head.value; } - add(value) { + enqueue(value) { this.linkedList.append({ value }); } - remove() { - const removedTail = this.linkedList.deleteTail(); - return removedTail ? removedTail.value : null; + dequeue() { + const removedHead = this.linkedList.deleteHead(); + return removedHead ? removedHead.value : null; } } diff --git a/src/data-structures/queue/README.md b/src/data-structures/queue/README.md index a9eca0911..8b596a2f3 100644 --- a/src/data-structures/queue/README.md +++ b/src/data-structures/queue/README.md @@ -1,8 +1,2 @@ # Queue -|Operation |Complexity | -|---------------------------|-------------------| -|Find |O() | -|Insert/delete at beginning |O() | -|Insert/delete in middle |O() | -|Insert/delete at end |O() | diff --git a/src/data-structures/queue/__test__/Queue.test.js b/src/data-structures/queue/__test__/Queue.test.js index 195a1045a..d55292d98 100644 --- a/src/data-structures/queue/__test__/Queue.test.js +++ b/src/data-structures/queue/__test__/Queue.test.js @@ -7,11 +7,11 @@ describe('Queue', () => { expect(queue.linkedList).not.toBeNull(); }); - it('should add data to queue', () => { + it('should enqueue data to queue', () => { const queue = new Queue(); - queue.add(1); - queue.add(2); + queue.enqueue(1); + queue.enqueue(2); expect(queue.linkedList.toString()).toBe('1,2'); }); @@ -21,11 +21,11 @@ describe('Queue', () => { expect(queue.peek()).toBeNull(); - queue.add(1); - queue.add(2); + queue.enqueue(1); + queue.enqueue(2); - expect(queue.peek()).toBe(2); - expect(queue.peek()).toBe(2); + expect(queue.peek()).toBe(1); + expect(queue.peek()).toBe(1); }); it('should check if queue is empty', () => { @@ -33,19 +33,19 @@ describe('Queue', () => { expect(queue.isEmpty()).toBeTruthy(); - queue.add(1); + queue.enqueue(1); expect(queue.isEmpty()).toBeFalsy(); }); - it('should remove from empty', () => { + it('should dequeue from queue in FIFO order', () => { const queue = new Queue(); - queue.add(1); - queue.add(2); + queue.enqueue(1); + queue.enqueue(2); - expect(queue.remove()).toBe(2); - expect(queue.remove()).toBe(1); + expect(queue.dequeue()).toBe(1); + expect(queue.dequeue()).toBe(2); expect(queue.isEmpty()).toBeTruthy(); }); }); diff --git a/src/data-structures/stack/README.md b/src/data-structures/stack/README.md new file mode 100644 index 000000000..d095a2b0b --- /dev/null +++ b/src/data-structures/stack/README.md @@ -0,0 +1 @@ +# Queue diff --git a/src/data-structures/stack/Stack.js b/src/data-structures/stack/Stack.js new file mode 100644 index 000000000..3c2b0c451 --- /dev/null +++ b/src/data-structures/stack/Stack.js @@ -0,0 +1,28 @@ +import LinkedList from '../linked-list/LinkedList'; + +export default class Stack { + constructor() { + this.linkedList = new LinkedList(); + } + + isEmpty() { + return !this.linkedList.tail; + } + + peek() { + if (!this.linkedList.tail) { + return null; + } + + return this.linkedList.tail.value; + } + + push(value) { + this.linkedList.append({ value }); + } + + pop() { + const removedTail = this.linkedList.deleteTail(); + return removedTail ? removedTail.value : null; + } +} diff --git a/src/data-structures/stack/__test__/Stack.test.js b/src/data-structures/stack/__test__/Stack.test.js new file mode 100644 index 000000000..cf8addcfc --- /dev/null +++ b/src/data-structures/stack/__test__/Stack.test.js @@ -0,0 +1,51 @@ +import Stack from '../Stack'; + +describe('Stack', () => { + it('should create empty stack', () => { + const stack = new Stack(); + expect(stack).not.toBeNull(); + expect(stack.linkedList).not.toBeNull(); + }); + + it('should stack data to stack', () => { + const stack = new Stack(); + + stack.push(1); + stack.push(2); + + expect(stack.linkedList.toString()).toBe('1,2'); + }); + + it('should peek data from stack', () => { + const stack = new Stack(); + + expect(stack.peek()).toBeNull(); + + stack.push(1); + stack.push(2); + + expect(stack.peek()).toBe(2); + expect(stack.peek()).toBe(2); + }); + + it('should check if stack is empty', () => { + const stack = new Stack(); + + expect(stack.isEmpty()).toBeTruthy(); + + stack.push(1); + + expect(stack.isEmpty()).toBeFalsy(); + }); + + it('should pop data from stack', () => { + const stack = new Stack(); + + stack.push(1); + stack.push(2); + + expect(stack.pop()).toBe(2); + expect(stack.pop()).toBe(1); + expect(stack.isEmpty()).toBeTruthy(); + }); +});