Skip to content

Commit c8bfe9f

Browse files
committedMar 28, 2018
Add Queue.
1 parent 55d6aa5 commit c8bfe9f

File tree

8 files changed

+4053
-19
lines changed

8 files changed

+4053
-19
lines changed
 

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- [Linked List](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/linked-list)
88
- [Hash Table](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/hash-table)
9+
- [Queue](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/queue)
910

1011
## Running Tests
1112

‎package-lock.json

+3,855
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+3
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,8 @@
2929
"eslint-plugin-jsx-a11y": "^6.0.3",
3030
"eslint-plugin-react": "^7.7.0",
3131
"jest": "^22.4.3"
32+
},
33+
"dependencies": {
34+
"npm": "^5.8.0"
3235
}
3336
}

‎src/data-structures/linked-list/LinkedList.js

+54-18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import LinkedListNode from './LinkedListNode';
33
export default class LinkedList {
44
constructor() {
55
this.head = null;
6+
this.tail = null;
7+
}
8+
9+
prepend({ value, key = null }) {
10+
const newNode = new LinkedListNode({ value, key, next: this.head });
11+
12+
// Make new node to be a head.
13+
this.head = newNode;
14+
15+
return newNode;
616
}
717

818
append({ value, key = null }) {
@@ -11,27 +21,14 @@ export default class LinkedList {
1121
// If there is no head yet let's make new node a head.
1222
if (!this.head) {
1323
this.head = newNode;
24+
this.tail = newNode;
1425

1526
return newNode;
1627
}
1728

18-
// Rewind to last node.
19-
let currentNode = this.head;
20-
while (currentNode.next !== null) {
21-
currentNode = currentNode.next;
22-
}
23-
2429
// Attach new node to the end of linked list.
25-
currentNode.next = newNode;
26-
27-
return newNode;
28-
}
29-
30-
prepend({ value, key = null }) {
31-
const newNode = new LinkedListNode({ value, key, next: this.head });
32-
33-
// Make new node to be a head.
34-
this.head = newNode;
30+
this.tail.next = newNode;
31+
this.tail = newNode;
3532

3633
return newNode;
3734
}
@@ -42,6 +39,7 @@ export default class LinkedList {
4239
// If there is no head yet let's make new node a head.
4340
if (!this.head) {
4441
this.head = newNode;
42+
this.tail = newNode;
4543

4644
return newNode;
4745
}
@@ -66,6 +64,7 @@ export default class LinkedList {
6664

6765
// Attach new node to the end of linked list.
6866
currentNode.next = newNode;
67+
this.tail = newNode;
6968

7069
return newNode;
7170
}
@@ -90,8 +89,14 @@ export default class LinkedList {
9089
if (currentNode.next.value === value) {
9190
deletedNode = currentNode.next;
9291
currentNode.next = currentNode.next.next;
92+
} else {
93+
currentNode = currentNode.next;
9394
}
94-
currentNode = currentNode.next;
95+
}
96+
97+
// Check if tail must be deleted.
98+
if (this.tail.value === value) {
99+
this.tail = currentNode;
95100
}
96101

97102
return deletedNode;
@@ -117,13 +122,44 @@ export default class LinkedList {
117122
if (currentNode.next.key === key) {
118123
deletedNode = currentNode.next;
119124
currentNode.next = currentNode.next.next;
125+
} else {
126+
currentNode = currentNode.next;
120127
}
121-
currentNode = currentNode.next;
128+
}
129+
130+
// Check if tail must be deleted.
131+
if (this.tail.key === key) {
132+
this.tail = currentNode;
122133
}
123134

124135
return deletedNode;
125136
}
126137

138+
deleteTail() {
139+
if (this.head === this.tail) {
140+
const deletedTail = this.tail;
141+
this.head = null;
142+
this.tail = null;
143+
144+
return deletedTail;
145+
}
146+
147+
const deletedTail = this.tail;
148+
149+
// Rewind to the last node and delete "next" link for the node before the last one.
150+
let currentNode = this.head;
151+
while (currentNode.next) {
152+
if (!currentNode.next.next) {
153+
currentNode.next = null;
154+
} else {
155+
currentNode = currentNode.next;
156+
}
157+
}
158+
159+
this.tail = currentNode;
160+
return deletedTail;
161+
}
162+
127163
findByKey(key) {
128164
let currentNode = this.head;
129165

‎src/data-structures/linked-list/__test__/LinkedList.test.js

+53-1
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,19 @@ describe('LinkedList', () => {
99
it('should append node to linked list', () => {
1010
const linkedList = new LinkedList();
1111

12+
expect(linkedList.head).toBeNull();
13+
expect(linkedList.tail).toBeNull();
14+
1215
const node1 = linkedList.append({ value: 1 });
1316
const node2 = linkedList.append({ value: 2, key: 'test' });
1417

1518
expect(node1.value).toBe(1);
1619
expect(node2.value).toBe(2);
1720
expect(node2.key).toBe('test');
1821

22+
expect(linkedList.head.toString()).toBe('1');
23+
expect(linkedList.tail.toString()).toBe('test:2');
24+
1925
expect(linkedList.toString()).toBe('1,test:2');
2026
});
2127

@@ -28,6 +34,9 @@ describe('LinkedList', () => {
2834
expect(node1.value).toBe(1);
2935
expect(node2.value).toBe(2);
3036

37+
expect(linkedList.head.toString()).toBe('2');
38+
expect(linkedList.tail.toString()).toBe('1');
39+
3140
expect(linkedList.toString()).toBe('2,1');
3241
});
3342

@@ -38,18 +47,61 @@ describe('LinkedList', () => {
3847
linkedList.append({ value: 2 });
3948
linkedList.append({ value: 3 });
4049
linkedList.append({ value: 3 });
50+
linkedList.append({ value: 3 });
4151
linkedList.append({ value: 4 });
4252
linkedList.append({ value: 5 });
4353

54+
expect(linkedList.head.toString()).toBe('1');
55+
expect(linkedList.tail.toString()).toBe('5');
56+
4457
const deletedNode = linkedList.deleteByValue(3);
4558
expect(deletedNode.value).toBe(3);
46-
expect(linkedList.toString()).toBe('1,2,3,4,5');
59+
expect(linkedList.toString()).toBe('1,2,4,5');
4760

4861
linkedList.deleteByValue(3);
4962
expect(linkedList.toString()).toBe('1,2,4,5');
5063

5164
linkedList.deleteByValue(1);
5265
expect(linkedList.toString()).toBe('2,4,5');
66+
67+
expect(linkedList.head.toString()).toBe('2');
68+
expect(linkedList.tail.toString()).toBe('5');
69+
70+
linkedList.deleteByValue(5);
71+
expect(linkedList.toString()).toBe('2,4');
72+
73+
expect(linkedList.head.toString()).toBe('2');
74+
expect(linkedList.tail.toString()).toBe('4');
75+
76+
linkedList.deleteByValue(4);
77+
expect(linkedList.toString()).toBe('2');
78+
79+
expect(linkedList.head.toString()).toBe('2');
80+
expect(linkedList.tail.toString()).toBe('2');
81+
});
82+
83+
it('should delete linked list tail', () => {
84+
const linkedList = new LinkedList();
85+
86+
linkedList.append({ value: 1 });
87+
linkedList.append({ value: 2 });
88+
89+
expect(linkedList.head.toString()).toBe('1');
90+
expect(linkedList.tail.toString()).toBe('2');
91+
92+
const deletedNode1 = linkedList.deleteTail();
93+
94+
expect(deletedNode1.value).toBe(2);
95+
expect(linkedList.toString()).toBe('1');
96+
expect(linkedList.head.toString()).toBe('1');
97+
expect(linkedList.tail.toString()).toBe('1');
98+
99+
const deletedNode2 = linkedList.deleteTail();
100+
101+
expect(deletedNode2.value).toBe(1);
102+
expect(linkedList.toString()).toBe('');
103+
expect(linkedList.head).toBeNull();
104+
expect(linkedList.tail).toBeNull();
53105
});
54106

55107
it('should delete node by key from linked list', () => {

‎src/data-structures/queue/Queue.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import LinkedList from '../linked-list/LinkedList';
2+
3+
export default class Queue {
4+
constructor() {
5+
this.linkedList = new LinkedList();
6+
}
7+
8+
isEmpty() {
9+
return !this.linkedList.tail;
10+
}
11+
12+
peek() {
13+
if (!this.linkedList.tail) {
14+
return null;
15+
}
16+
17+
return this.linkedList.tail.value;
18+
}
19+
20+
add(value) {
21+
this.linkedList.append({ value });
22+
}
23+
24+
remove() {
25+
const removedTail = this.linkedList.deleteTail();
26+
return removedTail ? removedTail.value : null;
27+
}
28+
}

‎src/data-structures/queue/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Queue
2+
3+
|Operation |Complexity |
4+
|---------------------------|-------------------|
5+
|Find |O() |
6+
|Insert/delete at beginning |O() |
7+
|Insert/delete in middle |O() |
8+
|Insert/delete at end |O() |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import Queue from '../Queue';
2+
3+
describe('Queue', () => {
4+
it('should create empty queue', () => {
5+
const queue = new Queue();
6+
expect(queue).not.toBeNull();
7+
expect(queue.linkedList).not.toBeNull();
8+
});
9+
10+
it('should add data to queue', () => {
11+
const queue = new Queue();
12+
13+
queue.add(1);
14+
queue.add(2);
15+
16+
expect(queue.linkedList.toString()).toBe('1,2');
17+
});
18+
19+
it('should peek data from queue', () => {
20+
const queue = new Queue();
21+
22+
expect(queue.peek()).toBeNull();
23+
24+
queue.add(1);
25+
queue.add(2);
26+
27+
expect(queue.peek()).toBe(2);
28+
expect(queue.peek()).toBe(2);
29+
});
30+
31+
it('should check if queue is empty', () => {
32+
const queue = new Queue();
33+
34+
expect(queue.isEmpty()).toBeTruthy();
35+
36+
queue.add(1);
37+
38+
expect(queue.isEmpty()).toBeFalsy();
39+
});
40+
41+
it('should remove from empty', () => {
42+
const queue = new Queue();
43+
44+
queue.add(1);
45+
queue.add(2);
46+
47+
expect(queue.remove()).toBe(2);
48+
expect(queue.remove()).toBe(1);
49+
expect(queue.isEmpty()).toBeTruthy();
50+
});
51+
});

0 commit comments

Comments
 (0)
Please sign in to comment.