├── data-structures ├── model │ ├── objectType.ts │ ├── node.ts │ ├── doubly-node.ts │ └── comparator.ts ├── linked-list │ ├── doubly-linked-list.spec.ts │ ├── doubly-linked-list.ts │ ├── README.md │ ├── singly-linked-list.spec.ts │ └── singly-linked-list.ts ├── stack │ ├── stack.ts │ ├── stack.spec.ts │ └── README.md └── queue │ ├── queue.ts │ ├── queue.spec.ts │ └── README.md ├── assets └── singly-linked-list-reverse.jpg ├── .prettierrc ├── babel.config.js ├── tsconfig.json ├── .vscode └── launch.json ├── package.json ├── LICENSE ├── README.md └── .gitignore /data-structures/model/objectType.ts: -------------------------------------------------------------------------------- 1 | export interface ObjectType { 2 | [key: number]: T; 3 | } 4 | -------------------------------------------------------------------------------- /data-structures/model/node.ts: -------------------------------------------------------------------------------- 1 | export class Node { 2 | constructor(public val: T, public next?: Node) {} 3 | } 4 | -------------------------------------------------------------------------------- /assets/singly-linked-list-reverse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/trungvose/typescript-data-structures/HEAD/assets/singly-linked-list-reverse.jpg -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 100, 4 | "tabWidth": 2, 5 | "useTabs": false, 6 | "trailingComma": "none", 7 | "bracketSpacing": true, 8 | "semi": true 9 | } -------------------------------------------------------------------------------- /data-structures/model/doubly-node.ts: -------------------------------------------------------------------------------- 1 | import { Node } from './node'; 2 | 3 | export class DoublyNode extends Node { 4 | constructor(public val: T, public next?: DoublyNode, public prev?: DoublyNode) { 5 | super(val, next); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | '@babel/preset-env', 5 | { 6 | targets: { 7 | node: 'current' 8 | } 9 | } 10 | ], 11 | '@babel/preset-typescript' 12 | ] 13 | }; 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "strict": true, 5 | "esModuleInterop": true, 6 | "skipLibCheck": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "strictNullChecks": false, 9 | "noImplicitAny": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /data-structures/model/comparator.ts: -------------------------------------------------------------------------------- 1 | export class Comparator { 2 | constructor(private _compareFunction = primitiveCompare) {} 3 | 4 | equal(a: T, b: T): boolean { 5 | return this._compareFunction(a, b); 6 | } 7 | } 8 | 9 | export function primitiveCompare(a: T, b: T): boolean { 10 | return a === b; 11 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Jest Tests", 6 | "type": "node", 7 | "request": "launch", 8 | "runtimeArgs": [ 9 | "--inspect-brk", 10 | "${workspaceRoot}/node_modules/jest/bin/jest.js", 11 | "--runInBand" 12 | ], 13 | "console": "integratedTerminal", 14 | "internalConsoleOptions": "neverOpen", 15 | "port": 9229 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /data-structures/linked-list/doubly-linked-list.spec.ts: -------------------------------------------------------------------------------- 1 | import { DoublyLinkedList } from './doubly-linked-list'; 2 | 3 | describe('Doubly Linked List', () => { 4 | let linkedList: DoublyLinkedList; 5 | beforeEach(() => { 6 | linkedList = new DoublyLinkedList(); 7 | }); 8 | 9 | it('1. Empty linked list', () => { 10 | expect(linkedList.isEmpty).toBe(true); 11 | expect(linkedList.size).toBe(0); 12 | }); 13 | 14 | it('2. Push front', () => { 15 | prePushFront(linkedList); 16 | 17 | }); 18 | 19 | function prePushFront(list: DoublyLinkedList){ 20 | list.pushFront(1) 21 | list.pushFront(2) 22 | list.pushFront(3) 23 | expect(linkedList.size).toBe(3); 24 | expect(linkedList.isEmpty).toBe(false); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /data-structures/linked-list/doubly-linked-list.ts: -------------------------------------------------------------------------------- 1 | import { DoublyNode } from '../model/doubly-node'; 2 | 3 | export class DoublyLinkedList { 4 | private _head: DoublyNode; 5 | private _count: number; 6 | 7 | constructor() { 8 | this._count = 0; 9 | } 10 | 11 | get head(): DoublyNode { 12 | return this._head; 13 | } 14 | 15 | get size(): number { 16 | return this._count; 17 | } 18 | 19 | get isEmpty(): boolean { 20 | return this.size === 0; 21 | } 22 | 23 | /** 24 | * Adds an item to the front of the list 25 | * Time complexity: O(1) 26 | */ 27 | pushFront(value: T): DoublyLinkedList { 28 | this._count++; 29 | let node = new DoublyNode(value); 30 | if (this._head) { 31 | this._head.prev = node; 32 | } 33 | node.next = this._head; 34 | this._head = node; 35 | return this; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-data-structures", 3 | "version": "1.0.0", 4 | "description": "Fundamental data structures and algorithms a front end engineer should know, written all in TypeScript", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "jest" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/trungk18/javascript-data-structures.git" 12 | }, 13 | "author": "trungk18", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/trungk18/javascript-data-structures/issues" 17 | }, 18 | "homepage": "https://github.com/trungk18/javascript-data-structures#readme", 19 | "devDependencies": { 20 | "@babel/core": "^7.10.2", 21 | "@babel/preset-env": "^7.10.2", 22 | "@babel/preset-typescript": "^7.10.1", 23 | "babel-jest": "^26.0.1", 24 | "jest": "^26.0.1" 25 | }, 26 | "dependencies": { 27 | "@types/jest": "^25.2.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Trung Vo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /data-structures/stack/stack.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '../model/objectType'; 2 | 3 | export class Stack { 4 | private _stack: ObjectType; 5 | private _count: number; 6 | 7 | constructor() { 8 | this._stack = {}; 9 | this._count = 0; 10 | } 11 | 12 | get isEmpty(): boolean { 13 | return this.size === 0; 14 | } 15 | 16 | get size(): number { 17 | return this._count; 18 | } 19 | 20 | push(value: T) { 21 | this._stack[this._count] = value; 22 | this._count++; 23 | } 24 | 25 | pop(): T { 26 | if (this.isEmpty) { 27 | return undefined; 28 | } 29 | this._count--; 30 | const value = this._stack[this._count]; 31 | delete this._stack[this._count]; 32 | return value; 33 | } 34 | 35 | peek(): T { 36 | if (this.isEmpty) { 37 | return undefined; 38 | } 39 | return this._stack[this._count - 1]; 40 | } 41 | 42 | clear(): void { 43 | this._stack = {}; 44 | this._count = 0; 45 | } 46 | 47 | print(): string { 48 | if (this.isEmpty) { 49 | return ''; 50 | } 51 | let values = []; 52 | for (let i = 0; i < this._count; i++) { 53 | values.unshift((this._stack[i] as any).toString()); 54 | } 55 | return values.join(' -> '); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /data-structures/queue/queue.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType } from '../model/objectType'; 2 | export class Queue { 3 | private _queue: ObjectType; 4 | private _head: number; 5 | private _tail: number; 6 | 7 | constructor() { 8 | this._queue = {}; 9 | this._head = 0; 10 | this._tail = 0; 11 | } 12 | 13 | get isEmpty() { 14 | return this.size === 0; 15 | } 16 | 17 | get size() { 18 | return this._tail - this._head; 19 | } 20 | 21 | enqueue(value: T): void { 22 | this._queue[this._tail] = value; 23 | this._tail++; 24 | } 25 | 26 | dequeue(): T { 27 | if (this.isEmpty) { 28 | return undefined; 29 | } 30 | const value = this._queue[this._head]; 31 | delete this._queue[this._head]; 32 | this._head++; 33 | return value; 34 | } 35 | 36 | peek(): T { 37 | if (this.isEmpty) { 38 | return undefined; 39 | } 40 | return this._queue[this._head]; 41 | } 42 | 43 | clear(): void { 44 | this._queue = {}; 45 | this._head = 0; 46 | this._tail = 0; 47 | } 48 | 49 | print(): string { 50 | if (this.isEmpty) { 51 | return ''; 52 | } 53 | let values = []; 54 | for (let i = this._head; i < this._tail; i++) { 55 | values.unshift(this._queue[i].toString()); 56 | } 57 | return values.join(' -> '); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /data-structures/stack/stack.spec.ts: -------------------------------------------------------------------------------- 1 | import { Stack } from './stack'; 2 | 3 | describe('Stack', () => { 4 | let stack: Stack; 5 | beforeEach(() => { 6 | stack = new Stack(); 7 | }); 8 | 9 | it('1. Empty stack', () => { 10 | expect(stack.size).toBe(0); 11 | expect(stack.isEmpty).toBe(true); 12 | }); 13 | 14 | it('2. Push', () => { 15 | stack.push(1); 16 | expect(stack.size).toBe(1); 17 | stack.push(2); 18 | expect(stack.size).toBe(2); 19 | stack.push(3); 20 | expect(stack.size).toBe(3); 21 | }); 22 | 23 | it('3. Pop', () => { 24 | prePush(stack); 25 | expect(stack.pop()).toBe(3); 26 | expect(stack.pop()).toBe(2); 27 | expect(stack.pop()).toBe(1); 28 | expect(stack.pop()).toBe(undefined); 29 | expect(stack.isEmpty).toBe(true); 30 | expect(stack.size).toBe(0); 31 | }); 32 | 33 | it('4. Peek', () => { 34 | prePush(stack); 35 | expect(stack.peek()).toBe(3); 36 | }); 37 | 38 | it('5. Clear', () => { 39 | prePush(stack); 40 | stack.clear(); 41 | expect(stack.isEmpty).toBe(true); 42 | expect(stack.size).toBe(0); 43 | }); 44 | 45 | it('6. Print primitive type', () => { 46 | prePush(stack); 47 | expect(stack.print()).toBe('3 -> 2 -> 1'); 48 | }); 49 | 50 | function prePush(s: Stack) { 51 | s.push(1); 52 | s.push(2); 53 | s.push(3); 54 | } 55 | }); 56 | -------------------------------------------------------------------------------- /data-structures/queue/queue.spec.ts: -------------------------------------------------------------------------------- 1 | import { Queue } from './queue'; 2 | 3 | describe('Queue', () => { 4 | let queue: Queue; 5 | beforeEach(() => { 6 | queue = new Queue(); 7 | }); 8 | 9 | it('1. Empty queue', () => { 10 | expect(queue.size).toBe(0); 11 | expect(queue.isEmpty).toBe(true); 12 | }); 13 | 14 | it('2. Enqueue', () => { 15 | queue.enqueue(1); 16 | expect(queue.size).toBe(1); 17 | queue.enqueue(2); 18 | expect(queue.size).toBe(2); 19 | queue.enqueue(3); 20 | expect(queue.size).toBe(3); 21 | }); 22 | 23 | it('3. Dequeue', () => { 24 | preEnqueue(queue); 25 | expect(queue.dequeue()).toBe(1); 26 | expect(queue.dequeue()).toBe(2); 27 | expect(queue.dequeue()).toBe(3); 28 | expect(queue.dequeue()).toBe(undefined); 29 | expect(queue.isEmpty).toBe(true); 30 | expect(queue.size).toBe(0); 31 | }); 32 | 33 | it('4. Peek', () => { 34 | preEnqueue(queue); 35 | expect(queue.peek()).toBe(1); 36 | }); 37 | 38 | it('5. Clear', () => { 39 | preEnqueue(queue); 40 | queue.clear(); 41 | expect(queue.isEmpty).toBe(true); 42 | expect(queue.size).toBe(0); 43 | }); 44 | 45 | it('6. Print primitive type', () => { 46 | preEnqueue(queue); 47 | expect(queue.print()).toBe("3 -> 2 -> 1"); 48 | }); 49 | 50 | function preEnqueue(q: Queue) { 51 | q.enqueue(1); 52 | q.enqueue(2); 53 | q.enqueue(3); 54 | } 55 | }); 56 | 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TypeScript Data Structures 2 | 3 | Fundamental data structures and algorithms a front end engineer should know, written in TypeScript. 4 | 5 | ## Available data structures 6 | 7 | 1. [x] [Queue][queue] 8 | 2. [x] [Stack][stack] 9 | 3. [x] [Singly Linked List][linkedlist] 10 | 4. [ ] [Double Linked List (wip)][linkedlist] 11 | 5. [ ] Trees 12 | 6. [ ] Tries 13 | 7. [ ] HashSets 14 | 8. [ ] HashTables 15 | 9. [ ] Graph 16 | 17 | ## Algorithms 18 | 19 | 1. [ ] Quick Sort 20 | 2. [ ] Merge Sort 21 | 3. [ ] Insertion Sort 22 | 4. [ ] Binary Search 23 | 5. [ ] Depth First Search 24 | 6. [ ] Breadth First Search 25 | 26 | 27 | ## Test 28 | 29 | ```bash 30 | # install dependencies 31 | npm install 32 | 33 | # run all test suite 34 | npm run test 35 | ``` 36 | 37 | Please make sure all the tests has been passed before issuing a pull request :) 38 | 39 | ## References 40 | 41 | - [coding-interview-university][coding-interview-university] 42 | 43 | ## Contributing 44 | 45 | I know this collection of standards is not perfect and still needs to improve. If you have any ideas, just [open an issue][issues] or create a [pull request][pullreq] and tell me what you think. 46 | 47 | If you'd like to contribute, please fork the repository and make changes as you'd like. Pull requests are warmly welcome. 48 | 49 | [coding-interview-university]: https://github.com/jwasham/coding-interview-university 50 | [queue]: https://github.com/trungk18/typescript-data-structures/tree/master/data-structures/queue 51 | [stack]: https://github.com/trungk18/typescript-data-structures/tree/master/data-structures/stack 52 | [linkedlist]: https://github.com/trungk18/typescript-data-structures/tree/master/data-structures/linked-list 53 | [issues]: https://github.com/trungk18/typescript-data-structures/issues/new 54 | [pullreq]: https://github.com/trungk18/typescript-data-structures/compare 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /data-structures/stack/README.md: -------------------------------------------------------------------------------- 1 | # Stack 2 | 3 | In computer science, a **stack** is an abstract data type that serves 4 | as a collection of elements, with two principal operations: 5 | 6 | * **push**, which adds an element to the collection, and 7 | * **pop**, which removes the most recently added element that was not yet removed. 8 | 9 | The order in which elements come off a stack gives rise to its alternative name, LIFO (last in, first out). Additionally, a peek operation may give access to the top without modifying the stack. The name "stack" for this type of structure comes from the analogy to a set of physical items stacked on top of each other, which makes it easy to take an item off the top of the stack, while getting to an item deeper in the stack may require taking off multiple other items first. 10 | 11 | Simple representation of a stack runtime with push and pop operations. 12 | 13 | ![Stack](https://upload.wikimedia.org/wikipedia/commons/b/b4/Lifo_stack.png) 14 | 15 | ## Implementation 16 | 17 | Implement using built-in object as a map, with tail pointer. I came up with this approach to make sure the Big O notation for the `pop` operation. 18 | 19 | | Operation | Description | Big O | 20 | | ----------- | ------------------------------------------------------------------- | ----- | 21 | | push(value) | adds an element to the collection | O(1) | 22 | | pop() | removes the most recently added element | O(1) | 23 | | peek() | returns the most recently added element without modifying the stack | O(1) | 24 | | clear() | removes all the element in the stack | O(1) | 25 | | isEmpty | checks if the stack is empty | O(1) | 26 | | size | checks the size of the stack | O(1) | 27 | 28 | ## Alternative Implementation 29 | 30 | 1. [Trivial implementation using Array][0] - Using `arr.splice()` for pop, could take `O(n)`. That will not meet the requirement of `O(1)` for pop operation. 31 | 2. [Stack][1] 32 | 33 | ## References 34 | 35 | - [Wikipedia](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) 36 | 37 | [0]: https://github.com/davidshariff/computer-science/blob/master/Data%20Structures/Stack.js 38 | [1]: https://github.com/yangshun/lago/blob/master/lib/data-structures/Stack.js 39 | -------------------------------------------------------------------------------- /data-structures/linked-list/README.md: -------------------------------------------------------------------------------- 1 | # Linked List 2 | 3 | In computer science, a **linked list** is a linear collection of data elements, in which linear order is not given by their physical placement in memory. Instead, each element points to the next. It is a data structure consisting of a group of nodes which together represent a sequence. Under the simplest form, each node is composed of data and a reference (in other words, a link) to the next node in the sequence. This structure allows for efficient insertion or removal of elements from any position in the sequence during iteration. More complex variants add additional links, allowing efficient insertion or removal from arbitrary element references. A drawback of linked lists is that access time is linear (and difficult to pipeline). Faster access, such as random access, is not feasible. Arrays have better cache locality as compared to linked lists. 4 | 5 | ![Linked List](https://upload.wikimedia.org/wikipedia/commons/6/6d/Singly-linked-list.svg) 6 | 7 | ## Implementation 8 | 9 | ### Singly Linked List 10 | 11 | I implemented without the tail pointer. 12 | 13 | | Operation | Description | Big O | 14 | | ---------------- | -------------------------------------------------- | ----- | 15 | | pushFront(value) | Adds an item to the front of the list | O(1) | 16 | | popFront() | Remove front item and return its value | O(1) | 17 | | front() | Get value of the front item | O(1) | 18 | | pushBack(value) | Adds an item to the end of the list | O(n) | 19 | | popBack() | Remove front item and return its value | O(n) | 20 | | back() | Get value of the end item | O(n) | 21 | | reverse() | Reverses the list | O(n) | 22 | | has(value) | Return boolean if the list has a value | O(n) | 23 | | remove(value) | Removes the first item in the list with this value | O(n) | 24 | | isEmpty | Returns true if empty | O(1) | 25 | | size | Returns number of data elements in list | O(1) | 26 | 27 | This is my notebook when implementing the reverse operation. 28 | 29 | ![Singly Linked List Reverse][4] 30 | 31 | ## References 32 | 33 | - [Coursera][1] 34 | - [Wikipedia][2] 35 | - [coding-interview-university][3] 36 | 37 | [1]: https://www.coursera.org/lecture/data-structures/singly-linked-lists-kHhgK 38 | [2]: https://en.wikipedia.org/wiki/Linked_list 39 | [3]: https://github.com/jwasham/coding-interview-university#linked-lists 40 | [4]: https://raw.githubusercontent.com/trungk18/typescript-data-structures/master/assets/singly-linked-list-reverse.jpg -------------------------------------------------------------------------------- /data-structures/linked-list/singly-linked-list.spec.ts: -------------------------------------------------------------------------------- 1 | import { LinkedList } from './singly-linked-list'; 2 | 3 | describe('Singly Linked List', () => { 4 | let linkedList: LinkedList; 5 | beforeEach(() => { 6 | linkedList = new LinkedList(); 7 | }); 8 | 9 | it('1. Empty linked list', () => { 10 | expect(linkedList.size).toBe(0); 11 | expect(linkedList.isEmpty).toBe(true); 12 | }); 13 | 14 | it('2. Push front', () => { 15 | prePushFront(linkedList); 16 | expect(linkedList.size).toBe(3); 17 | expect(linkedList.isEmpty).toBe(false); 18 | }); 19 | 20 | it('3. Pop front', () => { 21 | prePushFront(linkedList); 22 | expect(linkedList.popFront()).toBe(2); 23 | expect(linkedList.popFront()).toBe(1); 24 | expect(linkedList.popFront()).toBe(0); 25 | expect(linkedList.size).toBe(0); 26 | expect(linkedList.isEmpty).toBe(true); 27 | }); 28 | 29 | it('4. Push back', () => { 30 | prePushBack(linkedList); 31 | expect(linkedList.size).toBe(3); 32 | expect(linkedList.isEmpty).toBe(false); 33 | }); 34 | 35 | it('4. Pop back', () => { 36 | prePushBack(linkedList); 37 | expect(linkedList.popBack()).toBe(0); 38 | expect(linkedList.popBack()).toBe(1); 39 | expect(linkedList.popBack()).toBe(2); 40 | expect(linkedList.size).toBe(0); 41 | expect(linkedList.isEmpty).toBe(true); 42 | }); 43 | 44 | it('5. Front', () => { 45 | prePushFront(linkedList); 46 | expect(linkedList.front()).toBe(2); 47 | linkedList.popFront(); 48 | expect(linkedList.front()).toBe(1); 49 | }); 50 | 51 | it('6. Back', () => { 52 | prePushFront(linkedList); 53 | expect(linkedList.back()).toBe(0); 54 | linkedList.popBack(); 55 | expect(linkedList.back()).toBe(1); 56 | }); 57 | 58 | it('7. Print', () => { 59 | prePushFront(linkedList); 60 | expect(linkedList.print()).toBe('2 -> 1 -> 0'); 61 | }); 62 | 63 | it('8. Reverse', () => { 64 | prePushFront(linkedList); 65 | expect(linkedList.print()).toBe('2 -> 1 -> 0'); 66 | expect(linkedList.reverse().print()).toBe('0 -> 1 -> 2'); 67 | }); 68 | 69 | it('9. Has', () => { 70 | prePushFront(linkedList); 71 | expect(linkedList.has(0)).toBe(true); 72 | expect(linkedList.has(1)).toBe(true); 73 | expect(linkedList.has(2)).toBe(true); 74 | expect(linkedList.has(3)).toBe(false); 75 | }); 76 | 77 | it('10. Remove', () => { 78 | prePushFront(linkedList); 79 | expect(linkedList.remove(0)).toBe(true); 80 | expect(linkedList.pushBack(3).print()).toBe('2 -> 1 -> 3'); 81 | expect(linkedList.remove(3)).toBe(true); 82 | linkedList.pushFront(4); 83 | linkedList.pushFront(5); 84 | expect(linkedList.print()).toBe('5 -> 4 -> 2 -> 1'); 85 | }); 86 | 87 | function prePushBack(linkedList: LinkedList) { 88 | linkedList.pushBack(2); 89 | linkedList.pushBack(1); 90 | linkedList.pushBack(0); 91 | } 92 | 93 | function prePushFront(linkedList: LinkedList) { 94 | linkedList.pushFront(0); 95 | linkedList.pushFront(1); 96 | linkedList.pushFront(2); 97 | } 98 | }); 99 | -------------------------------------------------------------------------------- /data-structures/queue/README.md: -------------------------------------------------------------------------------- 1 | # Queue 2 | 3 | In computer science, a **queue** is a collection of entities that are maintained in a sequence and can be modified by the addition of entities at one end of the sequence and the removal of entities from the other end of the sequence. By convention, the end of the sequence at which elements are added is called the back, **tail**, or rear of the queue, and the end at which elements are removed is called the **head** or front of the queue, analogously to the words used when people line up to wait for goods or services. 4 | 5 | The operation of adding an element to the rear of the queue is known as enqueue, and the operation of removing an element from the front is known as dequeue. Other operations may also be allowed, often including a peek or front operation that returns the value of the next element to be dequeued without dequeuing it. 6 | 7 | The operations of a queue make it a first-in-first-out (FIFO) data structure. In a FIFO data structure, the first element added to the queue will be the first one to be removed. This is equivalent to the requirement that once a new element is added, all elements that were added before have to be removed before the new element can be removed. A queue is an example of a linear data structure, or more abstractly a sequential collection 8 | 9 | Representation of a FIFO (first in, first out) queue 10 | 11 | ![Queue](https://upload.wikimedia.org/wikipedia/commons/5/52/Data_Queue.svg) 12 | 13 | ## Implementation 14 | 15 | Implement using built-in object as a map, with tail pointer. I came up with this approach to make sure the Big O notation for the dequeue operation. You can implement with the same approach using array instead of object. But to take one element out of an array could take `O(n)`. 16 | 17 | | Operation | Description | Big O | 18 | | -------------- | ------------------------------------------------------------------------- | ----- | 19 | | enqueue(value) | adds value at tail | O(1) | 20 | | dequeue() | returns value and removes least recently added element (head) | O(1) | 21 | | peek() | returns the value of the next element to be dequeued without dequeuing it | O(1) | 22 | | clear() | removes all the element in the queue | O(1) | 23 | | isEmpty | checks if the queue is empty | O(1) | 24 | | size | checks the size of the queue | O(1) | 25 | 26 | ## Alternative Implementation 27 | 28 | 1. [Trivial implementation using Array][0] - Using `arr.shift()` for dequeuing, take `O(n)` time. That will not meet the requirement of `O(1)` for dequeuing. 29 | 2. [Queue][1] 30 | 31 | ## References 32 | 33 | - [Wikipedia]() 34 | 35 | [0]: https://github.com/davidshariff/computer-science/blob/master/Data%20Structures/Queue.js 36 | [1]: https://github.com/yangshun/lago/blob/master/lib/data-structures/Queue.js 37 | -------------------------------------------------------------------------------- /data-structures/linked-list/singly-linked-list.ts: -------------------------------------------------------------------------------- 1 | import { Node } from '../model/node'; 2 | import { Comparator } from '../model/comparator'; 3 | 4 | export class LinkedList { 5 | private _head: Node; 6 | private _count: number; 7 | get head() { 8 | return this._head; 9 | } 10 | 11 | constructor(private _comparatorFunction = new Comparator()) { 12 | this._head = null; 13 | this._count = 0; 14 | } 15 | 16 | /** 17 | * Returns true if empty 18 | */ 19 | get isEmpty(): boolean { 20 | return this.size === 0; 21 | } 22 | 23 | /** 24 | * Returns number of data elements in list 25 | */ 26 | get size(): number { 27 | return this._count; 28 | } 29 | 30 | /** 31 | * Adds an item to the front of the list 32 | * Time complexity: O(1) 33 | */ 34 | pushFront(value: T): LinkedList { 35 | this._count++; 36 | let node = new Node(value); 37 | node.next = this._head; 38 | this._head = node; 39 | return this; 40 | } 41 | 42 | /** 43 | * Remove front item and return its value 44 | * Time complexity: O(1) 45 | */ 46 | popFront(): T { 47 | if (!this._head) { 48 | return undefined; 49 | } 50 | this._count--; 51 | let val = this._head.val; 52 | this._head = this._head.next; 53 | return val; 54 | } 55 | 56 | /** 57 | * Get value of the front item 58 | * Time complexity: O(1) 59 | */ 60 | front(): T { 61 | if (!this._head) { 62 | return undefined; 63 | } 64 | return this._head.val; 65 | } 66 | 67 | /** 68 | * Adds an item to the end of the list 69 | * Time complexity: O(n) 70 | */ 71 | pushBack(value: T): LinkedList { 72 | let newNode = new Node(value); 73 | this._count++; 74 | if (!this._head) { 75 | this._head = newNode; 76 | return this; 77 | } 78 | let curr = this._head; 79 | while (curr.next) { 80 | curr = curr.next; 81 | } 82 | curr.next = newNode; 83 | return this; 84 | } 85 | 86 | /** 87 | * Remove front item and return its value 88 | * Time complexity: O(n) 89 | */ 90 | popBack(): T { 91 | if (!this._head) { 92 | return undefined; 93 | } 94 | if (this.size === 1) { 95 | let val = this._head.val; 96 | this._head = null; 97 | this._count--; 98 | return val; 99 | } 100 | let curr = this._head; 101 | while (curr.next.next) { 102 | curr = curr.next; 103 | } 104 | let val = curr.next.val; 105 | curr.next = null; 106 | this._count--; 107 | return val; 108 | } 109 | 110 | /** 111 | * Get value of the end item 112 | * Time complexity: O(n) 113 | */ 114 | back(): T { 115 | if (!this._head) { 116 | return undefined; 117 | } 118 | let curr = this._head; 119 | while (curr.next) { 120 | curr = curr.next; 121 | } 122 | return curr.val; 123 | } 124 | 125 | /** 126 | * Reverses the list 127 | * Time complexity: O(n) 128 | */ 129 | reverse(): LinkedList { 130 | let curr = this._head; 131 | let prev = null; 132 | let next = null; 133 | while (curr) { 134 | next = curr.next; 135 | curr.next = prev; 136 | prev = curr; 137 | curr = next; 138 | } 139 | this._head = prev; 140 | return this; 141 | } 142 | 143 | /** 144 | * Return boolean if the list has a value 145 | * Time complexity: O(n) 146 | */ 147 | has(value: T): boolean { 148 | if (!this._head) { 149 | return undefined; 150 | } 151 | let curr = this._head; 152 | while (curr) { 153 | let hasValue = this._comparatorFunction.equal(value, curr.val); 154 | if (hasValue) { 155 | return true; 156 | } 157 | curr = curr.next; 158 | } 159 | return false; 160 | } 161 | 162 | /** 163 | * Removes the first item in the list with this value 164 | * Return true if delete success. 165 | * Time complexity: O(n) 166 | */ 167 | remove(value: T): boolean { 168 | if (!this.head) { 169 | return false; 170 | } 171 | if (this._comparatorFunction.equal(this.head.val, value)) { 172 | this._head = this._head.next; 173 | this._count--; 174 | return true; 175 | } 176 | let curr = this._head; 177 | while (curr.next) { 178 | if (this._comparatorFunction.equal(curr.next.val, value)) { 179 | curr.next = curr.next?.next; 180 | this._count--; 181 | return true; 182 | } 183 | curr = curr.next; 184 | } 185 | return false; 186 | } 187 | 188 | print(): string { 189 | let arr = []; 190 | let curr = this._head; 191 | while (curr) { 192 | arr.push(curr.val); 193 | curr = curr.next; 194 | } 195 | return arr.join(' -> '); 196 | } 197 | 198 | clear(): void { 199 | this._head = null; 200 | this._count = 0; 201 | } 202 | } 203 | --------------------------------------------------------------------------------