Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Data structures covered so far -
- [Graph](#graph)
- [Queue](#queue)
- [Linked List](#link-list)
- [Stack](#stack)

# Contribution
Your contribution is highly appreciated. You can contribute in several ways -
Expand Down Expand Up @@ -242,3 +243,37 @@ Usage as spread operator
```js
const items = [...list] // ['firstVal', 'Mid', 'xyz', 'secondVal']
```

# <a name="stack"></a> Stack (Linked List Implementation)

Import Stack data structure and create a list object.

```js
import { Stack } from '@js-labs/data-structures/lib/ds';
// const { Stack } = require('@js-labs/data-structures/lib/ds')
const stack = new Stack;
```

Get size of the stack and check if the stack is empty

```js
stack.size() //0
stack.isEmpty() //true
```

Push items in the stack

```js
stack.push('item1');
stack.size() //1

stack.push('item2');
stack.size() //2
```

Pop items from the stack

```js
stack.pop() //item2
stack.pop() //item1
```
3 changes: 2 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from './queue/queue';
export * from './binary-search-tree/binary-search-tree';
export * from './graph/graph';
export * from './graph/adj-list';
export * from './linked-list/linked-list';
export * from './linked-list/linked-list';
export * from './stack/stack';
65 changes: 50 additions & 15 deletions src/linked-list/linked-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ export class Node {

const _size = Symbol('size');
const _head = Symbol('head');
const _tail = Symbol('tail');

export class LinkedList {
constructor() {
this[_size] = 0;
this[_head] = null;
this[_tail] = null;
}

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

get tail() {
return this[_tail];
}

size() {
return this[_size];
}
Expand All @@ -51,33 +57,43 @@ export class LinkedList {
}

insert(item) {
if(this.isEmpty()) {
if (this.isEmpty()) {
this.insertFirst(item);
} else {
this.insertLast(item);
}
this[_size] += 1;

}

insertLast(item) {
let el = this[_head];
while(el && el.next !== null) {
while (el && el.next !== null) {
el = el.next;
}

el.next = new Node(item);
this[_tail] = el.next;
this[_size] += 1;
}

insertFirst(item) {
this[_head] = new Node(item);
if (this.isEmpty()) {
this[_head] = new Node(item);
this[_tail] = this[_head];
} else {
const currNode = this[_head];
this[_head] = new Node(item);
this[_head].next = currNode;
}
this[_size] += 1;
}

insertBefore(item, before) {
const {prev, curr, found} = this._search(before);
const { prev, curr, found } = this._search(before);

if(found) {
if (found) {
const node = new Node(item);
if(prev === null) {
if (prev === null) {
this[_head] = node;
} else {
prev.next = node;
Expand All @@ -87,31 +103,50 @@ export class LinkedList {
}
}
insertAfter(item, after) {
const {curr, next, found} = this._search(after);
const { curr, next, found } = this._search(after);

if(found) {
if (found) {
curr.next = new Node(item);
curr.next.next = next;
if (next === null) {
this[_tail] = curr.next;
}
this[_size] += 1;
}
}
remove(item) {
const {prev, next, found} = this._search(item);
if(found) {
if(prev === null) {
const { prev, next, found } = this._search(item);
if (found) {
if (prev === null) {
this[_head] = next;
} else {
prev.next = next;
if (next === null) {
this[_tail] = prev;
}
}
this[_size] -= 1;
}
}

removeFirst() {
if (!this.isEmpty()) {
const currNode = this[_head];
this[_head] = currNode.next;
currNode.next = null;
this[_size] -= 1;
if (this[_head] === null) {
this[_tail] = null;
}
return currNode;
}
}

_search(item) {
let prev = null, curr = this.head, next = null, found = false;
while(curr !== null && !found) {
while (curr !== null && !found) {
// TODO: Check for Object and Array as well
if(curr.key === item) {
if (curr.key === item) {
next = curr.next;
found = true;
break;
Expand All @@ -120,6 +155,6 @@ export class LinkedList {
curr = curr.next;
}

return {prev, curr, next, found};
return { prev, curr, next, found };
}
}
46 changes: 42 additions & 4 deletions src/linked-list/linked-list.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ describe('Linked List', () => {
expect(list.isEmpty()).toBeFalsy();
expect(list.head.key).toContain('firstVal');
expect(list.head.next).toBe(null);
expect(list.tail.key).toBe('firstVal');

list.insertFirst('secondFirst');
expect(list.size()).toBe(2);
expect(list.head.key).toContain('secondFirst');
expect(list.head.next.key).toContain('firstVal');
expect(list.tail.key).toBe('firstVal');
});

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

it('should insert item after specified item', () => {
expect(list.tail.key).toBe('firstVal');

list.insertAfter('secondVal', 'firstVal');
expect(list.size()).toBe(2);
expect(list.search('secondVal')).toBeTruthy();
expect(list.tail.key).toBe('secondVal');

list.insertAfter('thirdVal', 'xyz');
expect(list.size()).toBe(2);
expect(list.search('thirdVal')).toBeFalsy();
expect(list.tail.key).toBe('secondVal');

list.insertAfter('thirdVal', 'firstVal');
expect(list.size()).toBe(3);
Expand Down Expand Up @@ -93,18 +105,44 @@ describe('Linked List', () => {
list.insert('B');
list.insert('C');
expect(list.size()).toBe(3);
expect(list.tail.key).toBe('C');

list.remove('B');
expect(list.size()).toBe(2);
expect(list.search('B')).toBeFalsy();

list.remove('A');
list.remove('C');
expect(list.size()).toBe(1);
expect(list.search('A')).toBeFalsy();
expect(list.search('C')).toBeFalsy();
expect(list.tail.key).toBe('A');

list.remove('C');
list.remove('A');
expect(list.size()).toBe(0);
expect(list.search('C')).toBeFalsy();
expect(list.search('A')).toBeFalsy();
});

it('should remove item from the head', () => {
expect(list.size()).toBe(0);

list.insert('A');
list.insert('B');
list.insert('C');
expect(list.size()).toBe(3);
expect(list.tail.key).toBe('C');

let item = list.removeFirst();
expect(item.key).toBe('A');
expect(list.tail.key).toBe('C');
expect(list.size()).toBe(2);

item = list.removeFirst();
expect(item.key).toBe('B');
expect(list.tail.key).toBe('C');
expect(list.size()).toBe(1);

item = list.removeFirst();
expect(item.key).toBe('C');
expect(list.tail).toEqual(null);
expect(list.size()).toBe(0);
});
});
60 changes: 60 additions & 0 deletions src/stack/stack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const _stack = Symbol('stack');
import { LinkedList } from "../linked-list/linked-list";

/**
* Linked List implementation of Stack
*/
export class Stack {
constructor() {
this[_stack] = new LinkedList;
}

/**
* getter for _stack object
*/
get stack() {
return this[_stack];
}

/**
* Push an item on top of the stack.
* Ideally the insertFirst method of linked list is called to add the item in the first position in linked list
* @param {any} item
*/
push(item) {
this[_stack].insertFirst(item);
}

/**
* Pop the top item from the stack
* Ideally removeFirst method of stack is called
*/
pop() {
if (!this.isEmpty()) {
return this[_stack].removeFirst().key;
}
}

/**
* Returns the top item of the stack without popping it
*/
peek() {
if (!this.isEmpty()) {
return this[_stack].head.key;
}
}

/**
* Returns the size of the stack
*/
size() {
return this[_stack].size();
}

/**
* Checks if the stack is empty
*/
isEmpty() {
return this.size() === 0;
}
}
Loading