Skip to content

Commit 50ba7eb

Browse files
authored
Merge pull request #41 from linux-nerd/feature/#40_stack
Linked List implementation of Stacks
2 parents 8d86b8b + 4b69e88 commit 50ba7eb

File tree

7 files changed

+251
-20
lines changed

7 files changed

+251
-20
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Data structures covered so far -
4646
- [Graph](#graph)
4747
- [Queue](#queue)
4848
- [Linked List](#link-list)
49+
- [Stack](#stack)
4950

5051
# Contribution
5152
Your contribution is highly appreciated. You can contribute in several ways -
@@ -242,3 +243,37 @@ Usage as spread operator
242243
```js
243244
const items = [...list] // ['firstVal', 'Mid', 'xyz', 'secondVal']
244245
```
246+
247+
# <a name="stack"></a> Stack (Linked List Implementation)
248+
249+
Import Stack data structure and create a list object.
250+
251+
```js
252+
import { Stack } from '@js-labs/data-structures/lib/ds';
253+
// const { Stack } = require('@js-labs/data-structures/lib/ds')
254+
const stack = new Stack;
255+
```
256+
257+
Get size of the stack and check if the stack is empty
258+
259+
```js
260+
stack.size() //0
261+
stack.isEmpty() //true
262+
```
263+
264+
Push items in the stack
265+
266+
```js
267+
stack.push('item1');
268+
stack.size() //1
269+
270+
stack.push('item2');
271+
stack.size() //2
272+
```
273+
274+
Pop items from the stack
275+
276+
```js
277+
stack.pop() //item2
278+
stack.pop() //item1
279+
```

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export * from './queue/queue';
22
export * from './binary-search-tree/binary-search-tree';
33
export * from './graph/graph';
44
export * from './graph/adj-list';
5-
export * from './linked-list/linked-list';
5+
export * from './linked-list/linked-list';
6+
export * from './stack/stack';

src/linked-list/linked-list.js

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ export class Node {
1818

1919
const _size = Symbol('size');
2020
const _head = Symbol('head');
21+
const _tail = Symbol('tail');
2122

2223
export class LinkedList {
2324
constructor() {
2425
this[_size] = 0;
2526
this[_head] = null;
27+
this[_tail] = null;
2628
}
2729

2830
*[Symbol.iterator]() {
@@ -38,6 +40,10 @@ export class LinkedList {
3840
return this[_head];
3941
}
4042

43+
get tail() {
44+
return this[_tail];
45+
}
46+
4147
size() {
4248
return this[_size];
4349
}
@@ -51,33 +57,43 @@ export class LinkedList {
5157
}
5258

5359
insert(item) {
54-
if(this.isEmpty()) {
60+
if (this.isEmpty()) {
5561
this.insertFirst(item);
5662
} else {
5763
this.insertLast(item);
5864
}
59-
this[_size] += 1;
65+
6066
}
6167

6268
insertLast(item) {
6369
let el = this[_head];
64-
while(el && el.next !== null) {
70+
while (el && el.next !== null) {
6571
el = el.next;
6672
}
6773

6874
el.next = new Node(item);
75+
this[_tail] = el.next;
76+
this[_size] += 1;
6977
}
7078

7179
insertFirst(item) {
72-
this[_head] = new Node(item);
80+
if (this.isEmpty()) {
81+
this[_head] = new Node(item);
82+
this[_tail] = this[_head];
83+
} else {
84+
const currNode = this[_head];
85+
this[_head] = new Node(item);
86+
this[_head].next = currNode;
87+
}
88+
this[_size] += 1;
7389
}
7490

7591
insertBefore(item, before) {
76-
const {prev, curr, found} = this._search(before);
92+
const { prev, curr, found } = this._search(before);
7793

78-
if(found) {
94+
if (found) {
7995
const node = new Node(item);
80-
if(prev === null) {
96+
if (prev === null) {
8197
this[_head] = node;
8298
} else {
8399
prev.next = node;
@@ -87,31 +103,50 @@ export class LinkedList {
87103
}
88104
}
89105
insertAfter(item, after) {
90-
const {curr, next, found} = this._search(after);
106+
const { curr, next, found } = this._search(after);
91107

92-
if(found) {
108+
if (found) {
93109
curr.next = new Node(item);
94110
curr.next.next = next;
111+
if (next === null) {
112+
this[_tail] = curr.next;
113+
}
95114
this[_size] += 1;
96115
}
97116
}
98117
remove(item) {
99-
const {prev, next, found} = this._search(item);
100-
if(found) {
101-
if(prev === null) {
118+
const { prev, next, found } = this._search(item);
119+
if (found) {
120+
if (prev === null) {
102121
this[_head] = next;
103122
} else {
104123
prev.next = next;
124+
if (next === null) {
125+
this[_tail] = prev;
126+
}
105127
}
106128
this[_size] -= 1;
107129
}
108130
}
109131

132+
removeFirst() {
133+
if (!this.isEmpty()) {
134+
const currNode = this[_head];
135+
this[_head] = currNode.next;
136+
currNode.next = null;
137+
this[_size] -= 1;
138+
if (this[_head] === null) {
139+
this[_tail] = null;
140+
}
141+
return currNode;
142+
}
143+
}
144+
110145
_search(item) {
111146
let prev = null, curr = this.head, next = null, found = false;
112-
while(curr !== null && !found) {
147+
while (curr !== null && !found) {
113148
// TODO: Check for Object and Array as well
114-
if(curr.key === item) {
149+
if (curr.key === item) {
115150
next = curr.next;
116151
found = true;
117152
break;
@@ -120,6 +155,6 @@ export class LinkedList {
120155
curr = curr.next;
121156
}
122157

123-
return {prev, curr, next, found};
158+
return { prev, curr, next, found };
124159
}
125160
}

src/linked-list/linked-list.spec.js

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ describe('Linked List', () => {
2323
expect(list.isEmpty()).toBeFalsy();
2424
expect(list.head.key).toContain('firstVal');
2525
expect(list.head.next).toBe(null);
26+
expect(list.tail.key).toBe('firstVal');
27+
28+
list.insertFirst('secondFirst');
29+
expect(list.size()).toBe(2);
30+
expect(list.head.key).toContain('secondFirst');
31+
expect(list.head.next.key).toContain('firstVal');
32+
expect(list.tail.key).toBe('firstVal');
2633
});
2734

2835
describe('When first item is inserted', () => {
@@ -40,16 +47,21 @@ describe('Linked List', () => {
4047
expect(list.size()).toBe(2);
4148
expect(list.head.next.key).toBe('secondVal');
4249
expect(list.head.next.next).toBe(null);
50+
expect(list.tail.key).toBe('secondVal');
4351
});
4452

4553
it('should insert item after specified item', () => {
54+
expect(list.tail.key).toBe('firstVal');
55+
4656
list.insertAfter('secondVal', 'firstVal');
4757
expect(list.size()).toBe(2);
4858
expect(list.search('secondVal')).toBeTruthy();
59+
expect(list.tail.key).toBe('secondVal');
4960

5061
list.insertAfter('thirdVal', 'xyz');
5162
expect(list.size()).toBe(2);
5263
expect(list.search('thirdVal')).toBeFalsy();
64+
expect(list.tail.key).toBe('secondVal');
5365

5466
list.insertAfter('thirdVal', 'firstVal');
5567
expect(list.size()).toBe(3);
@@ -93,18 +105,44 @@ describe('Linked List', () => {
93105
list.insert('B');
94106
list.insert('C');
95107
expect(list.size()).toBe(3);
108+
expect(list.tail.key).toBe('C');
96109

97110
list.remove('B');
98111
expect(list.size()).toBe(2);
99112
expect(list.search('B')).toBeFalsy();
100113

101-
list.remove('A');
114+
list.remove('C');
102115
expect(list.size()).toBe(1);
103-
expect(list.search('A')).toBeFalsy();
116+
expect(list.search('C')).toBeFalsy();
117+
expect(list.tail.key).toBe('A');
104118

105-
list.remove('C');
119+
list.remove('A');
106120
expect(list.size()).toBe(0);
107-
expect(list.search('C')).toBeFalsy();
121+
expect(list.search('A')).toBeFalsy();
108122
});
109123

124+
it('should remove item from the head', () => {
125+
expect(list.size()).toBe(0);
126+
127+
list.insert('A');
128+
list.insert('B');
129+
list.insert('C');
130+
expect(list.size()).toBe(3);
131+
expect(list.tail.key).toBe('C');
132+
133+
let item = list.removeFirst();
134+
expect(item.key).toBe('A');
135+
expect(list.tail.key).toBe('C');
136+
expect(list.size()).toBe(2);
137+
138+
item = list.removeFirst();
139+
expect(item.key).toBe('B');
140+
expect(list.tail.key).toBe('C');
141+
expect(list.size()).toBe(1);
142+
143+
item = list.removeFirst();
144+
expect(item.key).toBe('C');
145+
expect(list.tail).toEqual(null);
146+
expect(list.size()).toBe(0);
147+
});
110148
});

src/stack/stack.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const _stack = Symbol('stack');
2+
import { LinkedList } from "../linked-list/linked-list";
3+
4+
/**
5+
* Linked List implementation of Stack
6+
*/
7+
export class Stack {
8+
constructor() {
9+
this[_stack] = new LinkedList;
10+
}
11+
12+
/**
13+
* getter for _stack object
14+
*/
15+
get stack() {
16+
return this[_stack];
17+
}
18+
19+
/**
20+
* Push an item on top of the stack.
21+
* Ideally the insertFirst method of linked list is called to add the item in the first position in linked list
22+
* @param {any} item
23+
*/
24+
push(item) {
25+
this[_stack].insertFirst(item);
26+
}
27+
28+
/**
29+
* Pop the top item from the stack
30+
* Ideally removeFirst method of stack is called
31+
*/
32+
pop() {
33+
if (!this.isEmpty()) {
34+
return this[_stack].removeFirst().key;
35+
}
36+
}
37+
38+
/**
39+
* Returns the top item of the stack without popping it
40+
*/
41+
peek() {
42+
if (!this.isEmpty()) {
43+
return this[_stack].head.key;
44+
}
45+
}
46+
47+
/**
48+
* Returns the size of the stack
49+
*/
50+
size() {
51+
return this[_stack].size();
52+
}
53+
54+
/**
55+
* Checks if the stack is empty
56+
*/
57+
isEmpty() {
58+
return this.size() === 0;
59+
}
60+
}

0 commit comments

Comments
 (0)