Skip to content

Commit c609380

Browse files
authored
Merge pull request #35 from linux-nerd/feature/#20_singly_linked_list
#20 singly linked list
2 parents 5446284 + d864edf commit c609380

File tree

5 files changed

+290
-2
lines changed

5 files changed

+290
-2
lines changed

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Data structures covered so far -
4545
- [Binary Search Tree](#binary-search-tree)
4646
- [Graph](#graph)
4747
- [Queue](#queue)
48+
- [Linked List](#link-list)
4849

4950
# Contribution
5051
Your contribution is highly appreciated. You can contribute in several ways -
@@ -196,3 +197,48 @@ Clear the entire queue at once
196197
```js
197198
queue.clear() // this will empty the queue
198199
```
200+
201+
# <a name="link-list"></a> Linked List
202+
203+
Import LinkedList data structure and create a list object.
204+
205+
```js
206+
import { LinkedList } from 'es6-data-structures/lib/ds';
207+
const list = new LinkedList;
208+
```
209+
210+
Insert and Remove Items from the list
211+
212+
```js
213+
// inserts item at the end of the list
214+
list.insert('firstVal');
215+
list.insert('SecondVal');
216+
217+
list.insertAfter('Mid', 'firstVal'); // insert Mid after firstVal
218+
list.insertBefore('xyz', 'secondVal'); // insert xyz before secondVal
219+
220+
list.remove('firstVal') // removes firstVal from the list.
221+
```
222+
223+
Get size and search for an item and check if the list is empty
224+
225+
```js
226+
list.size() // 3
227+
list.search('xyz') // true
228+
list.search('abc') // false
229+
list.isEmpty() // false
230+
```
231+
232+
Iterate over list using for...of loop
233+
234+
```js
235+
for(const item of list) {
236+
console.log(item) // 'firstVal', 'Mid', 'xyz', 'secondVal'
237+
}
238+
```
239+
240+
Usage as spread operator
241+
242+
```js
243+
const items = [...list] // ['firstVal', 'Mid', 'xyz', 'secondVal']
244+
```

src/linked-list/linked-list.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
export class Node {
2+
constructor(key, next = null) {
3+
this._key = key;
4+
this._next = next;
5+
}
6+
7+
get key() {
8+
return this._key
9+
}
10+
get next() {
11+
return this._next;
12+
}
13+
set next(node) {
14+
this._next = node;
15+
}
16+
}
17+
18+
19+
const _size = Symbol('size');
20+
const _head = Symbol('head');
21+
22+
export class LinkedList {
23+
constructor() {
24+
this[_size] = 0;
25+
this[_head] = null;
26+
}
27+
28+
*[Symbol.iterator]() {
29+
let element = this.head;
30+
31+
while (element !== null) {
32+
yield element.key;
33+
element = element.next;
34+
}
35+
}
36+
37+
get head() {
38+
return this[_head];
39+
}
40+
41+
size() {
42+
return this[_size];
43+
}
44+
45+
isEmpty() {
46+
return this.size() === 0;
47+
}
48+
49+
search(item) {
50+
return this._search(item).found;
51+
}
52+
53+
insert(item) {
54+
if(this.isEmpty()) {
55+
this.insertFirst(item);
56+
} else {
57+
this.insertLast(item);
58+
}
59+
this[_size] += 1;
60+
}
61+
62+
insertLast(item) {
63+
let el = this[_head];
64+
while(el && el.next !== null) {
65+
el = el.next;
66+
}
67+
68+
el.next = new Node(item);
69+
}
70+
71+
insertFirst(item) {
72+
this[_head] = new Node(item);
73+
}
74+
75+
insertBefore(item, before) {
76+
const {prev, curr, found} = this._search(before);
77+
78+
if(found) {
79+
const node = new Node(item);
80+
if(prev === null) {
81+
this[_head] = node;
82+
} else {
83+
prev.next = node;
84+
}
85+
node.next = curr;
86+
this[_size] += 1;
87+
}
88+
}
89+
insertAfter(item, after) {
90+
const {curr, next, found} = this._search(after);
91+
92+
if(found) {
93+
curr.next = new Node(item);
94+
curr.next.next = next;
95+
this[_size] += 1;
96+
}
97+
}
98+
remove(item) {
99+
const {prev, next, found} = this._search(item);
100+
if(found) {
101+
if(prev === null) {
102+
this[_head] = next;
103+
} else {
104+
prev.next = next;
105+
}
106+
this[_size] -= 1;
107+
}
108+
}
109+
110+
_search(item) {
111+
let prev = null, curr = this.head, next = null, found = false;
112+
while(curr !== null && !found) {
113+
// TODO: Check for Object and Array as well
114+
if(curr.key === item) {
115+
next = curr.next;
116+
found = true;
117+
break;
118+
}
119+
prev = curr;
120+
curr = curr.next;
121+
}
122+
123+
return {prev, curr, next, found};
124+
}
125+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { LinkedList } from './linked-list';
2+
3+
describe('Linked List', () => {
4+
let list;
5+
beforeEach(() => {
6+
list = new LinkedList;
7+
});
8+
9+
it('should create a linked list object', () => {
10+
expect(list).toBeDefined();
11+
expect(list.size()).toBe(0);
12+
expect(list.head).toBe(null);
13+
expect(list.isEmpty()).toBeTruthy();
14+
});
15+
16+
it('should insert first item in the linked list', () => {
17+
spyOn(list, 'insertFirst').and.callThrough();
18+
list.insert('firstVal');
19+
20+
expect(list.insertFirst).toHaveBeenCalled();
21+
expect(list.insertFirst).toHaveBeenCalledWith('firstVal');
22+
expect(list.size()).toBe(1);
23+
expect(list.isEmpty()).toBeFalsy();
24+
expect(list.head.key).toContain('firstVal');
25+
expect(list.head.next).toBe(null);
26+
});
27+
28+
describe('When first item is inserted', () => {
29+
beforeEach(() => {
30+
list.insert('firstVal');
31+
});
32+
33+
it('should insert items at the end', () => {
34+
spyOn(list, 'insertLast').and.callThrough();
35+
expect(list.size()).toBe(1);
36+
list.insert('secondVal');
37+
38+
expect(list.insertLast).toHaveBeenCalled();
39+
expect(list.insertLast).toHaveBeenCalledWith('secondVal');
40+
expect(list.size()).toBe(2);
41+
expect(list.head.next.key).toBe('secondVal');
42+
expect(list.head.next.next).toBe(null);
43+
});
44+
45+
it('should insert item after specified item', () => {
46+
list.insertAfter('secondVal', 'firstVal');
47+
expect(list.size()).toBe(2);
48+
expect(list.search('secondVal')).toBeTruthy();
49+
50+
list.insertAfter('thirdVal', 'xyz');
51+
expect(list.size()).toBe(2);
52+
expect(list.search('thirdVal')).toBeFalsy();
53+
54+
list.insertAfter('thirdVal', 'firstVal');
55+
expect(list.size()).toBe(3);
56+
expect(list.search('thirdVal')).toBeTruthy();
57+
58+
expect([...list][1]).toContain('thirdVal');
59+
expect([...list][2]).toContain('secondVal');
60+
});
61+
62+
it('should insert item before specified item', () => {
63+
list.insertBefore('secondVal', 'firstVal');
64+
expect(list.size()).toBe(2);
65+
expect(list.search('secondVal')).toBeTruthy();
66+
67+
list.insertBefore('thirdVal', 'firstVal');
68+
expect(list.size()).toBe(3);
69+
expect(list.search('thirdVal')).toBeTruthy();
70+
71+
list.insertBefore('forthVal', 'xyz');
72+
expect(list.size()).toBe(3);
73+
expect(list.search('forthVal')).toBeFalsy();
74+
});
75+
});
76+
77+
it('should search for item', () => {
78+
expect(list.search('A')).toBeFalsy();
79+
80+
list.insert('A');
81+
list.insert('B');
82+
list.insert('C');
83+
84+
expect(list.search('B')).toBeTruthy();
85+
expect(list.search('C')).toBeTruthy();
86+
expect(list.search('D')).toBeFalsy();
87+
});
88+
89+
it('should remove items from list', () => {
90+
expect(list.size()).toBe(0);
91+
92+
list.insert('A');
93+
list.insert('B');
94+
list.insert('C');
95+
expect(list.size()).toBe(3);
96+
97+
list.remove('B');
98+
expect(list.size()).toBe(2);
99+
expect(list.search('B')).toBeFalsy();
100+
101+
list.remove('A');
102+
expect(list.size()).toBe(1);
103+
expect(list.search('A')).toBeFalsy();
104+
105+
list.remove('C');
106+
expect(list.size()).toBe(0);
107+
expect(list.search('C')).toBeFalsy();
108+
});
109+
110+
});

src/test-index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require('./binary-search-tree/binary-search-tree.spec');
22
require('./graph/graph.spec');
3-
require('./queue/queue.spec');
43
require('./graph/adj-list.spec');
4+
require('./queue/queue.spec');
5+
require('./linked-list/linked-list.spec');

typings/data-structures.d.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,14 @@ declare namespace DataStructures {
3434
enqueue(o): void;
3535
dequeue(): Error | void;
3636
size(): number;
37-
isEmpty(): void;
37+
isEmpty(): boolean;
3838
top(): any;
3939
clear(): void;
4040
}
41+
42+
class LinkedList {
43+
head(): any;
44+
size(): number;
45+
isEmpty(): boolean;
46+
}
4147
}

0 commit comments

Comments
 (0)