├── .editorconfig ├── .eslintrc ├── LICENSE ├── README.md ├── data-structures-in-javascript ├── README.md ├── array.es6.js ├── array.js ├── binary-search-tree.es6.js ├── binary-search-tree.js ├── doubly-linked-list.es6.js ├── doubly-linked-list.js ├── graph.es6.js ├── graph.js ├── hash-table.es6.js ├── hash-table.js ├── queue.es6.js ├── queue.js ├── set.es6.js ├── set.js ├── singly-linked-list.es6.js ├── singly-linked-list.js ├── stack.es6.js ├── stack.js ├── tree.es6.js ├── tree.js ├── trie.es6.js └── trie.js └── sorting-algorithms-in-javascript ├── README.md ├── bubble-sort-counters.es6.js ├── bubble-sort-counters.js ├── bubble-sort.es6.js ├── bubble-sort.js ├── insertion-sort-counters.es6.js ├── insertion-sort-counters.js ├── insertion-sort.es6.js ├── insertion-sort.js ├── merge-sort-counters.es6.js ├── merge-sort-counters.js ├── merge-sort.es6.js ├── merge-sort.js ├── quicksort-counters.es6.js ├── quicksort-counters.js ├── quicksort.es6.js ├── quicksort.js ├── selection-sort-counters.es6.js ├── selection-sort-counters.js ├── selection-sort.es6.js ├── selection-sort.js ├── shellsort-counters.es6.js ├── shellsort-counters.js ├── shellsort.es6.js └── shellsort.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | indent_style = space 7 | end_of_line = lf 8 | indent_size = 2 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "rules": { 4 | "indent": [2, 2, {"SwitchCase": 1}], 5 | "quotes": [2, "single"], 6 | "linebreak-style": [2, "unix"], 7 | "semi": [2, "always"], 8 | "no-console": 0 9 | }, 10 | "env": { 11 | "es6": true, 12 | "node": true, 13 | "browser": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Benoit VALLON 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Computer Science in JavaScript (ES5 and ES6) 2 | 3 | This repository contains code about various series of posts that I made [on my blog](http://blog.benoitvallon.com) about computer science (mostly data structures and sorting algorithms) reimplemented in JavaScript. 4 | 5 | ## What's in this repository? 6 | 7 | As of now, it contains 2 main sections: 8 | 9 | - [Data structures in JavaScript (ES5 and ES6)](https://github.com/benoitvallon/computer-science-in-javascript/tree/master/data-structures-in-javascript) 10 | - [Sorting algorithms in JavaScript (ES5 and ES6)](https://github.com/benoitvallon/computer-science-in-javascript/tree/master/sorting-algorithms-in-javascript) 11 | 12 | ## The #data-structures series 13 | 14 | The [#data-structures series](http://blog.benoitvallon.com/category/data-structures-in-javascript) is a collection of posts about reimplemented data structures in JavaScript. 15 | 16 | If you are not familiar with data structures, a quick introduction and the full list of reimplemented data structures can be found in the [introduction post of the series on data structures in JavaScript](http://blog.benoitvallon.com/data-structures-in-javascript/data-structures-in-javascript). 17 | 18 | If you feel comfortable with the concept of each data structure and only want to see the code, have a look at the summary post of the series. It removes all explanations and contains only the [JavaScript code for all data structures](http://blog.benoitvallon.com/data-structures-in-javascript/data-structures-in-javascript-all-the-code) discussed in the series. 19 | 20 | ### The data structures in the series 21 | 22 | - [x] Array 23 | - [x] Hash Table 24 | - [x] Set 25 | - [x] Singly Linked List 26 | - [x] Doubly Linked List 27 | - [x] Stack 28 | - [x] Queue 29 | - [x] Tree 30 | - [x] Binary Search Tree 31 | - [x] Trie 32 | - [x] Graph 33 | 34 | ## The #sorting-algorithms series 35 | 36 | The [#sorting-algorithms series](http://blog.benoitvallon.com/category/sorting-algorithms-in-javascript) is a collection of posts about reimplemented sorting algorithms in JavaScript. 37 | 38 | If you are not familiar with sorting algorithms, a quick introduction and the full list of reimplemented sorting algorithms can be found in the [introduction post of the series on sorting algorithms in JavaScript](http://blog.benoitvallon.com/sorting-algorithms-in-javascript/sorting-algorithms-in-javascript). 39 | 40 | If you feel comfortable with the concept of each sorting algorithms and only want to see the code, have a look at the summary post of the series. It removes all explanations and contains only the [JavaScript code for all sorting algorithms](http://blog.benoitvallon.com/sorting-algorithms-in-javascript/sorting-algorithms-in-javascript-all-the-code) discussed in the series. 41 | 42 | ### The sorting algorithms in the series 43 | 44 | - [x] Bubble sort 45 | - [x] Selection sort 46 | - [x] Insertion sort 47 | - [x] Shellsort 48 | - [x] Merge sort 49 | - [x] Quicksort 50 | 51 | ### A good way to compare all of them 52 | 53 | Unlike the [data structures](http://blog.benoitvallon.com/data-structures-in-javascript/data-structures-in-javascript/), all [sorting algorithms](http://blog.benoitvallon.com/sorting-algorithms-in-javascript/sorting-algorithms-in-javascript) have the same goal and they can all take the same input data. So, for every sorting algorithms of the series, we are going sort an `array` of 10 numbers from 1 to 10. 54 | 55 | By doing so we will be able to compare the different sorting algorithms more easily. Sorting algorithms are very sensitive to the input data so we will also try different input data to see how they affect the performances. 56 | 57 | ## Why this repository? 58 | 59 | At the beginning I started those series with 2 different repositories, one for the data structures and another one for the sorting algorithms. It was nice to keep things separately at first but grouping them into the same repository makes more sense to me now. 60 | 61 | All computer science concepts are located at the same place and it will allow me to add even more things in the future. Moreover, it won’t be mandatory that those things will be about data structures or sorting algorithms. There are other important things to know too. 62 | -------------------------------------------------------------------------------- /data-structures-in-javascript/README.md: -------------------------------------------------------------------------------- 1 | # Data Structures in JavaScript (ES5 and ES6) 2 | 3 | This repository is part of a series of posts about data structures reimplemented in JavaScript that I made [on my blog](http://blog.benoitvallon.com). 4 | 5 | # About the #data-structures series 6 | 7 | The [#data-structures series](http://blog.benoitvallon.com/category/data-structures-in-javascript) is a collection of posts about reimplemented data structures in JavaScript. 8 | 9 | If you are not familiar with data structures, a quick introduction and the full list of reimplemented data structures can be found in the [introduction post of the series on data structures in JavaScript](http://blog.benoitvallon.com/data-structures-in-javascript/data-structures-in-javascript). 10 | 11 | If you feel comfortable with the concept of each data structure and only want to see the code, have a look at the summary post of the series. It removes all explanations and contains only the [JavaScript code for all data structures](http://blog.benoitvallon.com/data-structures-in-javascript/data-structures-in-javascript-all-the-code) discussed in the series. 12 | 13 | # The data structures in the series 14 | 15 | - [x] Array 16 | - [x] Hash Table 17 | - [x] Set 18 | - [x] Singly Linked List 19 | - [x] Doubly Linked List 20 | - [x] Stack 21 | - [x] Queue 22 | - [x] Tree 23 | - [x] Binary Search Tree 24 | - [x] Trie 25 | - [x] Graph 26 | -------------------------------------------------------------------------------- /data-structures-in-javascript/array.es6.js: -------------------------------------------------------------------------------- 1 | class MyArray { 2 | constructor() { 3 | this.array = []; 4 | } 5 | 6 | add(data) { 7 | this.array.push(data); 8 | } 9 | 10 | remove(data) { 11 | this.array = this.array.filter(current => current !== data); 12 | } 13 | 14 | search(data) { 15 | const foundIndex = this.array.indexOf(data); 16 | if(~foundIndex) { 17 | return foundIndex; 18 | } 19 | 20 | return null; 21 | } 22 | 23 | getAtIndex(index) { 24 | return this.array[index]; 25 | } 26 | 27 | length() { 28 | return this.array.length; 29 | } 30 | 31 | print() { 32 | console.log(this.array.join(' ')); 33 | } 34 | } 35 | 36 | const array = new MyArray(); 37 | array.add(1); 38 | array.add(2); 39 | array.add(3); 40 | array.add(4); 41 | array.print(); // => 1 2 3 4 42 | console.log('search 3 gives index 2:', array.search(3)); // => 2 43 | console.log('getAtIndex 2 gives 3:', array.getAtIndex(2)); // => 3 44 | console.log('length is 4:', array.length()); // => 4 45 | array.remove(3); 46 | array.print(); // => 1 2 4 47 | array.add(5); 48 | array.add(5); 49 | array.print(); // => 1 2 4 5 5 50 | array.remove(5); 51 | array.print(); // => 1 2 4 52 | -------------------------------------------------------------------------------- /data-structures-in-javascript/array.js: -------------------------------------------------------------------------------- 1 | function MyArray() { 2 | this.array = []; 3 | } 4 | 5 | MyArray.prototype.add = function(data) { 6 | this.array.push(data); 7 | }; 8 | MyArray.prototype.remove = function(data) { 9 | this.array = this.array.filter(function(current) { 10 | return current !== data; 11 | }); 12 | }; 13 | MyArray.prototype.search = function(data) { 14 | var foundIndex = this.array.indexOf(data); 15 | if(~foundIndex) { 16 | return foundIndex; 17 | } 18 | 19 | return null; 20 | }; 21 | MyArray.prototype.getAtIndex = function(index) { 22 | return this.array[index]; 23 | }; 24 | MyArray.prototype.length = function() { 25 | return this.array.length; 26 | }; 27 | MyArray.prototype.print = function() { 28 | console.log(this.array.join(' ')); 29 | }; 30 | 31 | var array = new MyArray(); 32 | array.add(1); 33 | array.add(2); 34 | array.add(3); 35 | array.add(4); 36 | array.print(); // => 1 2 3 4 37 | console.log('search 3 gives index 2:', array.search(3)); // => 2 38 | console.log('getAtIndex 2 gives 3:', array.getAtIndex(2)); // => 3 39 | console.log('length is 4:', array.length()); // => 4 40 | array.remove(3); 41 | array.print(); // => 1 2 4 42 | array.add(5); 43 | array.add(5); 44 | array.print(); // => 1 2 4 5 5 45 | array.remove(5); 46 | array.print(); // => 1 2 4 47 | -------------------------------------------------------------------------------- /data-structures-in-javascript/binary-search-tree.es6.js: -------------------------------------------------------------------------------- 1 | function Node(data) { 2 | this.data = data; 3 | this.left = null; 4 | this.right = null; 5 | } 6 | 7 | class BinarySearchTree { 8 | constructor() { 9 | this.root = null; 10 | } 11 | 12 | add(data) { 13 | const node = new Node(data); 14 | if(!this.root) { 15 | this.root = node; 16 | } else { 17 | let current = this.root; 18 | while(current) { 19 | if(node.data < current.data) { 20 | if(!current.left) { 21 | current.left = node; 22 | break; 23 | } 24 | current = current.left; 25 | } else if (node.data > current.data) { 26 | if(!current.right) { 27 | current.right = node; 28 | break; 29 | } 30 | current = current.right; 31 | } else { 32 | break; 33 | } 34 | } 35 | } 36 | } 37 | 38 | remove(data) { 39 | const that = this; 40 | const removeNode = (node, data) => { 41 | if(!node) { 42 | return null; 43 | } 44 | if(data === node.data) { 45 | if(!node.left && !node.right) { 46 | return null; 47 | } 48 | if(!node.left) { 49 | return node.right; 50 | } 51 | if(!node.right) { 52 | return node.left; 53 | } 54 | // 2 children 55 | const temp = that.getMin(node.right); 56 | node.data = temp; 57 | node.right = removeNode(node.right, temp); 58 | return node; 59 | } else if(data < node.data) { 60 | node.left = removeNode(node.left, data); 61 | return node; 62 | } else { 63 | node.right = removeNode(node.right, data); 64 | return node; 65 | } 66 | }; 67 | this.root = removeNode(this.root, data); 68 | } 69 | 70 | contains(data) { 71 | let current = this.root; 72 | while(current) { 73 | if(data === current.data) { 74 | return true; 75 | } 76 | if(data < current.data) { 77 | current = current.left; 78 | } else { 79 | current = current.right; 80 | } 81 | } 82 | return false; 83 | } 84 | 85 | _preOrder(node, fn) { 86 | if(node) { 87 | if(fn) { 88 | fn(node); 89 | } 90 | this._preOrder(node.left, fn); 91 | this._preOrder(node.right, fn); 92 | } 93 | } 94 | 95 | _inOrder(node, fn) { 96 | if(node) { 97 | this._inOrder(node.left, fn); 98 | if(fn) { 99 | fn(node); 100 | } 101 | this._inOrder(node.right, fn); 102 | } 103 | } 104 | 105 | _postOrder(node, fn) { 106 | if(node) { 107 | this._postOrder(node.left, fn); 108 | this._postOrder(node.right, fn); 109 | if(fn) { 110 | fn(node); 111 | } 112 | } 113 | } 114 | 115 | traverseDFS(fn, method) { 116 | const current = this.root; 117 | if(method) { 118 | this[`_${method}`](current, fn); 119 | } else { 120 | this._preOrder(current, fn); 121 | } 122 | } 123 | 124 | traverseBFS(fn) { 125 | this.queue = []; 126 | this.queue.push(this.root); 127 | while(this.queue.length) { 128 | const node = this.queue.shift(); 129 | if(fn) { 130 | fn(node); 131 | } 132 | if(node.left) { 133 | this.queue.push(node.left); 134 | } 135 | if(node.right) { 136 | this.queue.push(node.right); 137 | } 138 | } 139 | } 140 | 141 | print() { 142 | if(!this.root) { 143 | return console.log('No root node found'); 144 | } 145 | const newline = new Node('|'); 146 | const queue = [this.root, newline]; 147 | let string = ''; 148 | while(queue.length) { 149 | const node = queue.shift(); 150 | string += `${node.data.toString()} `; 151 | if(node === newline && queue.length) { 152 | queue.push(newline); 153 | } 154 | if(node.left) { 155 | queue.push(node.left); 156 | } 157 | if(node.right) { 158 | queue.push(node.right); 159 | } 160 | } 161 | console.log(string.slice(0, -2).trim()); 162 | } 163 | 164 | printByLevel() { 165 | if(!this.root) { 166 | return console.log('No root node found'); 167 | } 168 | const newline = new Node('\n'); 169 | const queue = [this.root, newline]; 170 | let string = ''; 171 | while(queue.length) { 172 | const node = queue.shift(); 173 | string += node.data.toString() + (node.data !== '\n' ? ' ' : ''); 174 | if(node === newline && queue.length) { 175 | queue.push(newline); 176 | } 177 | if(node.left) { 178 | queue.push(node.left); 179 | } 180 | if(node.right) { 181 | queue.push(node.right); 182 | } 183 | } 184 | console.log(string.trim()); 185 | } 186 | 187 | getMin(node) { 188 | if(!node) { 189 | node = this.root; 190 | } 191 | while(node.left) { 192 | node = node.left; 193 | } 194 | return node.data; 195 | } 196 | 197 | getMax(node) { 198 | if(!node) { 199 | node = this.root; 200 | } 201 | while(node.right) { 202 | node = node.right; 203 | } 204 | return node.data; 205 | } 206 | 207 | _getHeight(node) { 208 | if(!node) { 209 | return -1; 210 | } 211 | const left = this._getHeight(node.left); 212 | const right = this._getHeight(node.right); 213 | return Math.max(left, right) + 1; 214 | } 215 | 216 | getHeight(node) { 217 | if(!node) { 218 | node = this.root; 219 | } 220 | return this._getHeight(node); 221 | } 222 | 223 | _isBalanced(node) { 224 | if(!node) { 225 | return true; 226 | } 227 | const heigthLeft = this._getHeight(node.left); 228 | const heigthRight = this._getHeight(node.right); 229 | const diff = Math.abs(heigthLeft - heigthRight); 230 | if(diff > 1) { 231 | return false; 232 | } else { 233 | return this._isBalanced(node.left) && this._isBalanced(node.right); 234 | } 235 | } 236 | 237 | isBalanced(node) { 238 | if(!node) { 239 | node = this.root; 240 | } 241 | return this._isBalanced(node); 242 | } 243 | 244 | _checkHeight(node) { 245 | if(!node) { 246 | return 0; 247 | } 248 | const left = this._checkHeight(node.left); 249 | if(left === -1) { 250 | return -1; 251 | } 252 | const right = this._checkHeight(node.right); 253 | if(right === -1) { 254 | return -1; 255 | } 256 | const diff = Math.abs(left - right); 257 | if(diff > 1) { 258 | return -1; 259 | } else { 260 | return Math.max(left, right) + 1; 261 | } 262 | } 263 | 264 | isBalancedOptimized(node) { 265 | if(!node) { 266 | node = this.root; 267 | } 268 | if(!node) { 269 | return true; 270 | } 271 | if(this._checkHeight(node) === -1) { 272 | return false; 273 | } else { 274 | return true; 275 | } 276 | } 277 | } 278 | 279 | const binarySearchTree = new BinarySearchTree(); 280 | binarySearchTree.add(5); 281 | binarySearchTree.add(3); 282 | binarySearchTree.add(7); 283 | binarySearchTree.add(2); 284 | binarySearchTree.add(4); 285 | binarySearchTree.add(4); 286 | binarySearchTree.add(6); 287 | binarySearchTree.add(8); 288 | binarySearchTree.print(); // => 5 | 3 7 | 2 4 6 8 289 | binarySearchTree.printByLevel(); // => 5 \n 3 7 \n 2 4 6 8 290 | console.log('--- DFS inOrder'); 291 | binarySearchTree.traverseDFS(node => { console.log(node.data); }, 'inOrder'); // => 2 3 4 5 6 7 8 292 | console.log('--- DFS preOrder'); 293 | binarySearchTree.traverseDFS(node => { console.log(node.data); }, 'preOrder'); // => 5 3 2 4 7 6 8 294 | console.log('--- DFS postOrder'); 295 | binarySearchTree.traverseDFS(node => { console.log(node.data); }, 'postOrder'); // => 2 4 3 6 8 7 5 296 | console.log('--- BFS'); 297 | binarySearchTree.traverseBFS(node => { console.log(node.data); }); // => 5 3 7 2 4 6 8 298 | console.log('min is 2:', binarySearchTree.getMin()); // => 2 299 | console.log('max is 8:', binarySearchTree.getMax()); // => 8 300 | console.log('tree contains 3 is true:', binarySearchTree.contains(3)); // => true 301 | console.log('tree contains 9 is false:', binarySearchTree.contains(9)); // => false 302 | console.log('tree height is 2:', binarySearchTree.getHeight()); // => 2 303 | console.log('tree is balanced is true:', binarySearchTree.isBalanced()); // => true 304 | binarySearchTree.remove(11); // remove non existing node 305 | binarySearchTree.print(); // => 5 | 3 7 | 2 4 6 8 306 | binarySearchTree.remove(5); // remove 5, 6 goes up 307 | binarySearchTree.print(); // => 6 | 3 7 | 2 4 8 308 | binarySearchTree.remove(7); // remove 7, 8 goes up 309 | binarySearchTree.print(); // => 6 | 3 8 | 2 4 310 | binarySearchTree.remove(8); // remove 8, the tree becomes unbalanced 311 | binarySearchTree.print(); // => 6 | 3 | 2 4 312 | console.log('tree is balanced is false:', binarySearchTree.isBalanced()); // => true 313 | binarySearchTree.remove(4); 314 | binarySearchTree.remove(2); 315 | binarySearchTree.remove(3); 316 | binarySearchTree.remove(6); 317 | binarySearchTree.print(); // => 'No root node found' 318 | binarySearchTree.printByLevel(); // => 'No root node found' 319 | console.log('tree height is -1:', binarySearchTree.getHeight()); // => -1 320 | console.log('tree is balanced is true:', binarySearchTree.isBalanced()); // => true 321 | console.log('---'); 322 | binarySearchTree.add(10); 323 | console.log('tree height is 0:', binarySearchTree.getHeight()); // => 0 324 | console.log('tree is balanced is true:', binarySearchTree.isBalanced()); // => true 325 | binarySearchTree.add(6); 326 | binarySearchTree.add(14); 327 | binarySearchTree.add(4); 328 | binarySearchTree.add(8); 329 | binarySearchTree.add(12); 330 | binarySearchTree.add(16); 331 | binarySearchTree.add(3); 332 | binarySearchTree.add(5); 333 | binarySearchTree.add(7); 334 | binarySearchTree.add(9); 335 | binarySearchTree.add(11); 336 | binarySearchTree.add(13); 337 | binarySearchTree.add(15); 338 | binarySearchTree.add(17); 339 | binarySearchTree.print(); // => 10 | 6 14 | 4 8 12 16 | 3 5 7 9 11 13 15 17 340 | binarySearchTree.remove(10); // remove 10, 11 goes up 341 | binarySearchTree.print(); // => 11 | 6 14 | 4 8 12 16 | 3 5 7 9 x 13 15 17 342 | binarySearchTree.remove(12); // remove 12; 13 goes up 343 | binarySearchTree.print(); // => 11 | 6 14 | 4 8 13 16 | 3 5 7 9 x x 15 17 344 | console.log('tree is balanced is true:', binarySearchTree.isBalanced()); // => true 345 | console.log('tree is balanced optimized is true:', binarySearchTree.isBalancedOptimized()); // => true 346 | binarySearchTree.remove(13); // remove 13, 13 has no children so nothing changes 347 | binarySearchTree.print(); // => 11 | 6 14 | 4 8 x 16 | 3 5 7 9 x x 15 17 348 | console.log('tree is balanced is false:', binarySearchTree.isBalanced()); // => false 349 | console.log('tree is balanced optimized is false:', binarySearchTree.isBalancedOptimized()); // => false 350 | -------------------------------------------------------------------------------- /data-structures-in-javascript/binary-search-tree.js: -------------------------------------------------------------------------------- 1 | function Node(data) { 2 | this.data = data; 3 | this.left = null; 4 | this.right = null; 5 | } 6 | 7 | function BinarySearchTree() { 8 | this.root = null; 9 | } 10 | 11 | BinarySearchTree.prototype.add = function(data) { 12 | var node = new Node(data); 13 | if(!this.root) { 14 | this.root = node; 15 | } else { 16 | var current = this.root; 17 | while(current) { 18 | if(node.data < current.data) { 19 | if(!current.left) { 20 | current.left = node; 21 | break; 22 | } 23 | current = current.left; 24 | } else if (node.data > current.data) { 25 | if(!current.right) { 26 | current.right = node; 27 | break; 28 | } 29 | current = current.right; 30 | } else { 31 | break; 32 | } 33 | } 34 | } 35 | }; 36 | BinarySearchTree.prototype.remove = function(data) { 37 | var that = this; 38 | var removeNode = function(node, data) { 39 | if(!node) { 40 | return null; 41 | } 42 | if(data === node.data) { 43 | if(!node.left && !node.right) { 44 | return null; 45 | } 46 | if(!node.left) { 47 | return node.right; 48 | } 49 | if(!node.right) { 50 | return node.left; 51 | } 52 | // 2 children 53 | var temp = that.getMin(node.right); 54 | node.data = temp; 55 | node.right = removeNode(node.right, temp); 56 | return node; 57 | } else if(data < node.data) { 58 | node.left = removeNode(node.left, data); 59 | return node; 60 | } else { 61 | node.right = removeNode(node.right, data); 62 | return node; 63 | } 64 | }; 65 | this.root = removeNode(this.root, data); 66 | }; 67 | BinarySearchTree.prototype.contains = function(data) { 68 | var current = this.root; 69 | while(current) { 70 | if(data === current.data) { 71 | return true; 72 | } 73 | if(data < current.data) { 74 | current = current.left; 75 | } else { 76 | current = current.right; 77 | } 78 | } 79 | return false; 80 | }; 81 | BinarySearchTree.prototype._preOrder = function(node, fn) { 82 | if(node) { 83 | if(fn) { 84 | fn(node); 85 | } 86 | this._preOrder(node.left, fn); 87 | this._preOrder(node.right, fn); 88 | } 89 | }; 90 | BinarySearchTree.prototype._inOrder = function(node, fn) { 91 | if(node) { 92 | this._inOrder(node.left, fn); 93 | if(fn) { 94 | fn(node); 95 | } 96 | this._inOrder(node.right, fn); 97 | } 98 | }; 99 | BinarySearchTree.prototype._postOrder = function(node, fn) { 100 | if(node) { 101 | this._postOrder(node.left, fn); 102 | this._postOrder(node.right, fn); 103 | if(fn) { 104 | fn(node); 105 | } 106 | } 107 | }; 108 | BinarySearchTree.prototype.traverseDFS = function(fn, method) { 109 | var current = this.root; 110 | if(method) { 111 | this['_' + method](current, fn); 112 | } else { 113 | this._preOrder(current, fn); 114 | } 115 | }; 116 | BinarySearchTree.prototype.traverseBFS = function(fn) { 117 | this.queue = []; 118 | this.queue.push(this.root); 119 | while(this.queue.length) { 120 | var node = this.queue.shift(); 121 | if(fn) { 122 | fn(node); 123 | } 124 | if(node.left) { 125 | this.queue.push(node.left); 126 | } 127 | if(node.right) { 128 | this.queue.push(node.right); 129 | } 130 | } 131 | }; 132 | BinarySearchTree.prototype.print = function() { 133 | if(!this.root) { 134 | return console.log('No root node found'); 135 | } 136 | var newline = new Node('|'); 137 | var queue = [this.root, newline]; 138 | var string = ''; 139 | while(queue.length) { 140 | var node = queue.shift(); 141 | string += node.data.toString() + ' '; 142 | if(node === newline && queue.length) { 143 | queue.push(newline); 144 | } 145 | if(node.left) { 146 | queue.push(node.left); 147 | } 148 | if(node.right) { 149 | queue.push(node.right); 150 | } 151 | } 152 | console.log(string.slice(0, -2).trim()); 153 | }; 154 | BinarySearchTree.prototype.printByLevel = function() { 155 | if(!this.root) { 156 | return console.log('No root node found'); 157 | } 158 | var newline = new Node('\n'); 159 | var queue = [this.root, newline]; 160 | var string = ''; 161 | while(queue.length) { 162 | var node = queue.shift(); 163 | string += node.data.toString() + (node.data !== '\n' ? ' ' : ''); 164 | if(node === newline && queue.length) { 165 | queue.push(newline); 166 | } 167 | if(node.left) { 168 | queue.push(node.left); 169 | } 170 | if(node.right) { 171 | queue.push(node.right); 172 | } 173 | } 174 | console.log(string.trim()); 175 | }; 176 | BinarySearchTree.prototype.getMin = function(node) { 177 | if(!node) { 178 | node = this.root; 179 | } 180 | while(node.left) { 181 | node = node.left; 182 | } 183 | return node.data; 184 | }; 185 | BinarySearchTree.prototype.getMax = function(node) { 186 | if(!node) { 187 | node = this.root; 188 | } 189 | while(node.right) { 190 | node = node.right; 191 | } 192 | return node.data; 193 | }; 194 | BinarySearchTree.prototype._getHeight = function(node) { 195 | if(!node) { 196 | return -1; 197 | } 198 | var left = this._getHeight(node.left); 199 | var right = this._getHeight(node.right); 200 | return Math.max(left, right) + 1; 201 | }; 202 | BinarySearchTree.prototype.getHeight = function(node) { 203 | if(!node) { 204 | node = this.root; 205 | } 206 | return this._getHeight(node); 207 | }; 208 | BinarySearchTree.prototype._isBalanced = function(node) { 209 | if(!node) { 210 | return true; 211 | } 212 | var heigthLeft = this._getHeight(node.left); 213 | var heigthRight = this._getHeight(node.right); 214 | var diff = Math.abs(heigthLeft - heigthRight); 215 | if(diff > 1) { 216 | return false; 217 | } else { 218 | return this._isBalanced(node.left) && this._isBalanced(node.right); 219 | } 220 | }; 221 | BinarySearchTree.prototype.isBalanced = function(node) { 222 | if(!node) { 223 | node = this.root; 224 | } 225 | return this._isBalanced(node); 226 | }; 227 | BinarySearchTree.prototype._checkHeight = function(node) { 228 | if(!node) { 229 | return 0; 230 | } 231 | var left = this._checkHeight(node.left); 232 | if(left === -1) { 233 | return -1; 234 | } 235 | var right = this._checkHeight(node.right); 236 | if(right === -1) { 237 | return -1; 238 | } 239 | var diff = Math.abs(left - right); 240 | if(diff > 1) { 241 | return -1; 242 | } else { 243 | return Math.max(left, right) + 1; 244 | } 245 | }; 246 | BinarySearchTree.prototype.isBalancedOptimized = function(node) { 247 | if(!node) { 248 | node = this.root; 249 | } 250 | if(!node) { 251 | return true; 252 | } 253 | if(this._checkHeight(node) === -1) { 254 | return false; 255 | } else { 256 | return true; 257 | } 258 | }; 259 | 260 | var binarySearchTree = new BinarySearchTree(); 261 | binarySearchTree.add(5); 262 | binarySearchTree.add(3); 263 | binarySearchTree.add(7); 264 | binarySearchTree.add(2); 265 | binarySearchTree.add(4); 266 | binarySearchTree.add(4); 267 | binarySearchTree.add(6); 268 | binarySearchTree.add(8); 269 | binarySearchTree.print(); // => 5 | 3 7 | 2 4 6 8 270 | binarySearchTree.printByLevel(); // => 5 \n 3 7 \n 2 4 6 8 271 | console.log('--- DFS inOrder'); 272 | binarySearchTree.traverseDFS(function(node) { console.log(node.data); }, 'inOrder'); // => 2 3 4 5 6 7 8 273 | console.log('--- DFS preOrder'); 274 | binarySearchTree.traverseDFS(function(node) { console.log(node.data); }, 'preOrder'); // => 5 3 2 4 7 6 8 275 | console.log('--- DFS postOrder'); 276 | binarySearchTree.traverseDFS(function(node) { console.log(node.data); }, 'postOrder'); // => 2 4 3 6 8 7 5 277 | console.log('--- BFS'); 278 | binarySearchTree.traverseBFS(function(node) { console.log(node.data); }); // => 5 3 7 2 4 6 8 279 | console.log('min is 2:', binarySearchTree.getMin()); // => 2 280 | console.log('max is 8:', binarySearchTree.getMax()); // => 8 281 | console.log('tree contains 3 is true:', binarySearchTree.contains(3)); // => true 282 | console.log('tree contains 9 is false:', binarySearchTree.contains(9)); // => false 283 | console.log('tree height is 2:', binarySearchTree.getHeight()); // => 2 284 | console.log('tree is balanced is true:', binarySearchTree.isBalanced()); // => true 285 | binarySearchTree.remove(11); // remove non existing node 286 | binarySearchTree.print(); // => 5 | 3 7 | 2 4 6 8 287 | binarySearchTree.remove(5); // remove 5, 6 goes up 288 | binarySearchTree.print(); // => 6 | 3 7 | 2 4 8 289 | binarySearchTree.remove(7); // remove 7, 8 goes up 290 | binarySearchTree.print(); // => 6 | 3 8 | 2 4 291 | binarySearchTree.remove(8); // remove 8, the tree becomes unbalanced 292 | binarySearchTree.print(); // => 6 | 3 | 2 4 293 | console.log('tree is balanced is false:', binarySearchTree.isBalanced()); // => true 294 | binarySearchTree.remove(4); 295 | binarySearchTree.remove(2); 296 | binarySearchTree.remove(3); 297 | binarySearchTree.remove(6); 298 | binarySearchTree.print(); // => 'No root node found' 299 | binarySearchTree.printByLevel(); // => 'No root node found' 300 | console.log('tree height is -1:', binarySearchTree.getHeight()); // => -1 301 | console.log('tree is balanced is true:', binarySearchTree.isBalanced()); // => true 302 | console.log('---'); 303 | binarySearchTree.add(10); 304 | console.log('tree height is 0:', binarySearchTree.getHeight()); // => 0 305 | console.log('tree is balanced is true:', binarySearchTree.isBalanced()); // => true 306 | binarySearchTree.add(6); 307 | binarySearchTree.add(14); 308 | binarySearchTree.add(4); 309 | binarySearchTree.add(8); 310 | binarySearchTree.add(12); 311 | binarySearchTree.add(16); 312 | binarySearchTree.add(3); 313 | binarySearchTree.add(5); 314 | binarySearchTree.add(7); 315 | binarySearchTree.add(9); 316 | binarySearchTree.add(11); 317 | binarySearchTree.add(13); 318 | binarySearchTree.add(15); 319 | binarySearchTree.add(17); 320 | binarySearchTree.print(); // => 10 | 6 14 | 4 8 12 16 | 3 5 7 9 11 13 15 17 321 | binarySearchTree.remove(10); // remove 10, 11 goes up 322 | binarySearchTree.print(); // => 11 | 6 14 | 4 8 12 16 | 3 5 7 9 x 13 15 17 323 | binarySearchTree.remove(12); // remove 12; 13 goes up 324 | binarySearchTree.print(); // => 11 | 6 14 | 4 8 13 16 | 3 5 7 9 x x 15 17 325 | console.log('tree is balanced is true:', binarySearchTree.isBalanced()); // => true 326 | console.log('tree is balanced optimized is true:', binarySearchTree.isBalancedOptimized()); // => true 327 | binarySearchTree.remove(13); // remove 13, 13 has no children so nothing changes 328 | binarySearchTree.print(); // => 11 | 6 14 | 4 8 x 16 | 3 5 7 9 x x 15 17 329 | console.log('tree is balanced is false:', binarySearchTree.isBalanced()); // => false 330 | console.log('tree is balanced optimized is false:', binarySearchTree.isBalancedOptimized()); // => false 331 | -------------------------------------------------------------------------------- /data-structures-in-javascript/doubly-linked-list.es6.js: -------------------------------------------------------------------------------- 1 | function Node(data) { 2 | this.data = data; 3 | this.previous = null; 4 | this.next = null; 5 | } 6 | 7 | class DoublyLinkedList { 8 | constructor() { 9 | this.head = null; 10 | this.tail = null; 11 | this.numberOfValues = 0; 12 | } 13 | 14 | add(data) { 15 | const node = new Node(data); 16 | if(!this.head) { 17 | this.head = node; 18 | this.tail = node; 19 | } else { 20 | node.previous = this.tail; 21 | this.tail.next = node; 22 | this.tail = node; 23 | } 24 | this.numberOfValues++; 25 | } 26 | 27 | remove(data) { 28 | let current = this.head; 29 | while(current) { 30 | if(current.data === data) { 31 | if(current === this.head && current === this.tail) { 32 | this.head = null; 33 | this.tail = null; 34 | } else if(current === this.head) { 35 | this.head = this.head.next; 36 | this.head.previous = null; 37 | } else if(current === this.tail) { 38 | this.tail = this.tail.previous; 39 | this.tail.next = null; 40 | } else { 41 | current.previous.next = current.next; 42 | current.next.previous = current.previous; 43 | } 44 | this.numberOfValues--; 45 | } 46 | current = current.next; 47 | } 48 | } 49 | 50 | insertAfter(data, toNodeData) { 51 | let current = this.head; 52 | while(current) { 53 | if(current.data === toNodeData) { 54 | const node = new Node(data); 55 | if(current === this.tail) { 56 | this.add(data); 57 | } else { 58 | current.next.previous = node; 59 | node.previous = current; 60 | node.next = current.next; 61 | current.next = node; 62 | this.numberOfValues++; 63 | } 64 | } 65 | current = current.next; 66 | } 67 | } 68 | 69 | traverse(fn) { 70 | let current = this.head; 71 | while(current) { 72 | if(fn) { 73 | fn(current); 74 | } 75 | current = current.next; 76 | } 77 | } 78 | 79 | traverseReverse(fn) { 80 | let current = this.tail; 81 | while(current) { 82 | if(fn) { 83 | fn(current); 84 | } 85 | current = current.previous; 86 | } 87 | } 88 | 89 | length() { 90 | return this.numberOfValues; 91 | } 92 | 93 | print() { 94 | let string = ''; 95 | let current = this.head; 96 | while(current) { 97 | string += `${current.data} `; 98 | current = current.next; 99 | } 100 | console.log(string.trim()); 101 | } 102 | } 103 | 104 | const doublyLinkedList = new DoublyLinkedList(); 105 | doublyLinkedList.print(); // => '' 106 | doublyLinkedList.add(1); 107 | doublyLinkedList.add(2); 108 | doublyLinkedList.add(3); 109 | doublyLinkedList.add(4); 110 | doublyLinkedList.print(); // => 1 2 3 4 111 | console.log('length is 4:', doublyLinkedList.length()); // => 4 112 | doublyLinkedList.remove(3); // remove value 113 | doublyLinkedList.print(); // => 1 2 4 114 | doublyLinkedList.remove(9); // remove non existing value 115 | doublyLinkedList.print(); // => 1 2 4 116 | doublyLinkedList.remove(1); // remove head 117 | doublyLinkedList.print(); // => 2 4 118 | doublyLinkedList.remove(4); // remove tail 119 | doublyLinkedList.print(); // => 2 120 | console.log('length is 1:', doublyLinkedList.length()); // => 1 121 | doublyLinkedList.remove(2); // remove tail, the list should be empty 122 | doublyLinkedList.print(); // => '' 123 | console.log('length is 0:', doublyLinkedList.length()); // => 0 124 | doublyLinkedList.add(2); 125 | doublyLinkedList.add(6); 126 | doublyLinkedList.print(); // => 2 6 127 | doublyLinkedList.insertAfter(3, 2); 128 | doublyLinkedList.print(); // => 2 3 6 129 | doublyLinkedList.traverseReverse(node => { console.log(node.data); }); 130 | doublyLinkedList.insertAfter(4, 3); 131 | doublyLinkedList.print(); // => 2 3 4 6 132 | doublyLinkedList.insertAfter(5, 9); // insertAfter a non existing node 133 | doublyLinkedList.print(); // => 2 3 4 6 134 | doublyLinkedList.insertAfter(5, 4); 135 | doublyLinkedList.insertAfter(7, 6); // insertAfter the tail 136 | doublyLinkedList.print(); // => 2 3 4 5 6 7 137 | doublyLinkedList.add(8); // add node with normal method 138 | doublyLinkedList.print(); // => 2 3 4 5 6 7 8 139 | console.log('length is 7:', doublyLinkedList.length()); // => 7 140 | doublyLinkedList.traverse(node => { node.data = node.data + 10; }); 141 | doublyLinkedList.print(); // => 12 13 14 15 16 17 18 142 | doublyLinkedList.traverse(node => { console.log(node.data); }); // => 12 13 14 15 16 17 18 143 | console.log('length is 7:', doublyLinkedList.length()); // => 7 144 | doublyLinkedList.traverseReverse(node => { console.log(node.data); }); // => 18 17 16 15 14 13 12 145 | doublyLinkedList.print(); // => 12 13 14 15 16 17 18 146 | console.log('length is 7:', doublyLinkedList.length()); // => 7 147 | -------------------------------------------------------------------------------- /data-structures-in-javascript/doubly-linked-list.js: -------------------------------------------------------------------------------- 1 | function Node(data) { 2 | this.data = data; 3 | this.previous = null; 4 | this.next = null; 5 | } 6 | 7 | function DoublyLinkedList() { 8 | this.head = null; 9 | this.tail = null; 10 | this.numberOfValues = 0; 11 | } 12 | 13 | DoublyLinkedList.prototype.add = function (data) { 14 | var node = new Node(data); 15 | if(!this.head) { 16 | this.head = node; 17 | this.tail = node; 18 | } else { 19 | node.previous = this.tail; 20 | this.tail.next = node; 21 | this.tail = node; 22 | } 23 | this.numberOfValues++; 24 | }; 25 | DoublyLinkedList.prototype.remove = function(data) { 26 | var current = this.head; 27 | while(current) { 28 | if(current.data === data) { 29 | if(current === this.head && current === this.tail) { 30 | this.head = null; 31 | this.tail = null; 32 | } else if(current === this.head) { 33 | this.head = this.head.next; 34 | this.head.previous = null; 35 | } else if(current === this.tail) { 36 | this.tail = this.tail.previous; 37 | this.tail.next = null; 38 | } else { 39 | current.previous.next = current.next; 40 | current.next.previous = current.previous; 41 | } 42 | this.numberOfValues--; 43 | } 44 | current = current.next; 45 | } 46 | }; 47 | DoublyLinkedList.prototype.insertAfter = function(data, toNodeData) { 48 | var current = this.head; 49 | while(current) { 50 | if(current.data === toNodeData) { 51 | var node = new Node(data); 52 | if(current === this.tail) { 53 | this.add(data); 54 | } else { 55 | current.next.previous = node; 56 | node.previous = current; 57 | node.next = current.next; 58 | current.next = node; 59 | this.numberOfValues++; 60 | } 61 | } 62 | current = current.next; 63 | } 64 | }; 65 | DoublyLinkedList.prototype.traverse = function(fn) { 66 | var current = this.head; 67 | while(current) { 68 | if(fn) { 69 | fn(current); 70 | } 71 | current = current.next; 72 | } 73 | }; 74 | DoublyLinkedList.prototype.traverseReverse = function(fn) { 75 | var current = this.tail; 76 | while(current) { 77 | if(fn) { 78 | fn(current); 79 | } 80 | current = current.previous; 81 | } 82 | }; 83 | DoublyLinkedList.prototype.length = function() { 84 | return this.numberOfValues; 85 | }; 86 | DoublyLinkedList.prototype.print = function() { 87 | var string = ''; 88 | var current = this.head; 89 | while(current) { 90 | string += current.data + ' '; 91 | current = current.next; 92 | } 93 | console.log(string.trim()); 94 | }; 95 | 96 | var doublyLinkedList = new DoublyLinkedList(); 97 | doublyLinkedList.print(); // => '' 98 | doublyLinkedList.add(1); 99 | doublyLinkedList.add(2); 100 | doublyLinkedList.add(3); 101 | doublyLinkedList.add(4); 102 | doublyLinkedList.print(); // => 1 2 3 4 103 | console.log('length is 4:', doublyLinkedList.length()); // => 4 104 | doublyLinkedList.remove(3); // remove value 105 | doublyLinkedList.print(); // => 1 2 4 106 | doublyLinkedList.remove(9); // remove non existing value 107 | doublyLinkedList.print(); // => 1 2 4 108 | doublyLinkedList.remove(1); // remove head 109 | doublyLinkedList.print(); // => 2 4 110 | doublyLinkedList.remove(4); // remove tail 111 | doublyLinkedList.print(); // => 2 112 | console.log('length is 1:', doublyLinkedList.length()); // => 1 113 | doublyLinkedList.remove(2); // remove tail, the list should be empty 114 | doublyLinkedList.print(); // => '' 115 | console.log('length is 0:', doublyLinkedList.length()); // => 0 116 | doublyLinkedList.add(2); 117 | doublyLinkedList.add(6); 118 | doublyLinkedList.print(); // => 2 6 119 | doublyLinkedList.insertAfter(3, 2); 120 | doublyLinkedList.print(); // => 2 3 6 121 | doublyLinkedList.traverseReverse(function(node) { console.log(node.data); }); 122 | doublyLinkedList.insertAfter(4, 3); 123 | doublyLinkedList.print(); // => 2 3 4 6 124 | doublyLinkedList.insertAfter(5, 9); // insertAfter a non existing node 125 | doublyLinkedList.print(); // => 2 3 4 6 126 | doublyLinkedList.insertAfter(5, 4); 127 | doublyLinkedList.insertAfter(7, 6); // insertAfter the tail 128 | doublyLinkedList.print(); // => 2 3 4 5 6 7 129 | doublyLinkedList.add(8); // add node with normal method 130 | doublyLinkedList.print(); // => 2 3 4 5 6 7 8 131 | console.log('length is 7:', doublyLinkedList.length()); // => 7 132 | doublyLinkedList.traverse(function(node) { node.data = node.data + 10; }); 133 | doublyLinkedList.print(); // => 12 13 14 15 16 17 18 134 | doublyLinkedList.traverse(function(node) { console.log(node.data); }); // => 12 13 14 15 16 17 18 135 | console.log('length is 7:', doublyLinkedList.length()); // => 7 136 | doublyLinkedList.traverseReverse(function(node) { console.log(node.data); }); // => 18 17 16 15 14 13 12 137 | doublyLinkedList.print(); // => 12 13 14 15 16 17 18 138 | console.log('length is 7:', doublyLinkedList.length()); // => 7 139 | -------------------------------------------------------------------------------- /data-structures-in-javascript/graph.es6.js: -------------------------------------------------------------------------------- 1 | class Graph { 2 | constructor() { 3 | this.vertices = []; 4 | this.edges = []; 5 | this.numberOfEdges = 0; 6 | } 7 | 8 | addVertex(vertex) { 9 | this.vertices.push(vertex); 10 | this.edges[vertex] = []; 11 | } 12 | 13 | removeVertex(vertex) { 14 | const index = this.vertices.indexOf(vertex); 15 | if(~index) { 16 | this.vertices.splice(index, 1); 17 | } 18 | while(this.edges[vertex].length) { 19 | const adjacentVertex = this.edges[vertex].pop(); 20 | this.removeEdge(adjacentVertex, vertex); 21 | } 22 | } 23 | 24 | addEdge(vertex1, vertex2) { 25 | this.edges[vertex1].push(vertex2); 26 | this.edges[vertex2].push(vertex1); 27 | this.numberOfEdges++; 28 | } 29 | 30 | removeEdge(vertex1, vertex2) { 31 | const index1 = this.edges[vertex1] ? this.edges[vertex1].indexOf(vertex2) : -1; 32 | const index2 = this.edges[vertex2] ? this.edges[vertex2].indexOf(vertex1) : -1; 33 | if(~index1) { 34 | this.edges[vertex1].splice(index1, 1); 35 | this.numberOfEdges--; 36 | } 37 | if(~index2) { 38 | this.edges[vertex2].splice(index2, 1); 39 | } 40 | } 41 | 42 | size() { 43 | return this.vertices.length; 44 | } 45 | 46 | relations() { 47 | return this.numberOfEdges; 48 | } 49 | 50 | traverseDFS(vertex, fn) { 51 | if(!~this.vertices.indexOf(vertex)) { 52 | return console.log('Vertex not found'); 53 | } 54 | const visited = []; 55 | this._traverseDFS(vertex, visited, fn); 56 | } 57 | 58 | _traverseDFS(vertex, visited, fn) { 59 | visited[vertex] = true; 60 | if(this.edges[vertex] !== undefined) { 61 | fn(vertex); 62 | } 63 | for(let i = 0; i < this.edges[vertex].length; i++) { 64 | if(!visited[this.edges[vertex][i]]) { 65 | this._traverseDFS(this.edges[vertex][i], visited, fn); 66 | } 67 | } 68 | } 69 | 70 | traverseBFS(vertex, fn) { 71 | if(!~this.vertices.indexOf(vertex)) { 72 | return console.log('Vertex not found'); 73 | } 74 | const queue = []; 75 | queue.push(vertex); 76 | const visited = []; 77 | visited[vertex] = true; 78 | 79 | while(queue.length) { 80 | vertex = queue.shift(); 81 | fn(vertex); 82 | for(let i = 0; i < this.edges[vertex].length; i++) { 83 | if(!visited[this.edges[vertex][i]]) { 84 | visited[this.edges[vertex][i]] = true; 85 | queue.push(this.edges[vertex][i]); 86 | } 87 | } 88 | } 89 | } 90 | 91 | pathFromTo(vertexSource, vertexDestination) { 92 | if(!~this.vertices.indexOf(vertexSource)) { 93 | return console.log('Vertex not found'); 94 | } 95 | const queue = []; 96 | queue.push(vertexSource); 97 | const visited = []; 98 | visited[vertexSource] = true; 99 | const paths = []; 100 | 101 | while(queue.length) { 102 | const vertex = queue.shift(); 103 | for(let i = 0; i < this.edges[vertex].length; i++) { 104 | if(!visited[this.edges[vertex][i]]) { 105 | visited[this.edges[vertex][i]] = true; 106 | queue.push(this.edges[vertex][i]); 107 | // save paths between vertices 108 | paths[this.edges[vertex][i]] = vertex; 109 | } 110 | } 111 | } 112 | if(!visited[vertexDestination]) { 113 | return undefined; 114 | } 115 | 116 | const path = []; 117 | for(var j = vertexDestination; j != vertexSource; j = paths[j]) { 118 | path.push(j); 119 | } 120 | path.push(j); 121 | return path.reverse().join('-'); 122 | } 123 | 124 | print() { 125 | console.log(this.vertices.map(function(vertex) { 126 | return (`${vertex} -> ${this.edges[vertex].join(', ')}`).trim(); 127 | }, this).join(' | ')); 128 | } 129 | } 130 | 131 | const graph = new Graph(); 132 | graph.addVertex(1); 133 | graph.addVertex(2); 134 | graph.addVertex(3); 135 | graph.addVertex(4); 136 | graph.addVertex(5); 137 | graph.addVertex(6); 138 | graph.print(); // 1 -> | 2 -> | 3 -> | 4 -> | 5 -> | 6 -> 139 | graph.addEdge(1, 2); 140 | graph.addEdge(1, 5); 141 | graph.addEdge(2, 3); 142 | graph.addEdge(2, 5); 143 | graph.addEdge(3, 4); 144 | graph.addEdge(4, 5); 145 | graph.addEdge(4, 6); 146 | graph.print(); // 1 -> 2, 5 | 2 -> 1, 3, 5 | 3 -> 2, 4 | 4 -> 3, 5, 6 | 5 -> 1, 2, 4 | 6 -> 4 147 | console.log('graph size (number of vertices):', graph.size()); // => 6 148 | console.log('graph relations (number of edges):', graph.relations()); // => 7 149 | graph.traverseDFS(1, vertex => { console.log(vertex); }); // => 1 2 3 4 5 6 150 | console.log('---'); 151 | graph.traverseBFS(1, vertex => { console.log(vertex); }); // => 1 2 5 3 4 6 152 | graph.traverseDFS(0, vertex => { console.log(vertex); }); // => 'Vertex not found' 153 | graph.traverseBFS(0, vertex => { console.log(vertex); }); // => 'Vertex not found' 154 | console.log('path from 6 to 1:', graph.pathFromTo(6, 1)); // => 6-4-5-1 155 | console.log('path from 3 to 5:', graph.pathFromTo(3, 5)); // => 3-2-5 156 | graph.removeEdge(1, 2); 157 | graph.removeEdge(4, 5); 158 | graph.removeEdge(10, 11); 159 | console.log('graph relations (number of edges):', graph.relations()); // => 5 160 | console.log('path from 6 to 1:', graph.pathFromTo(6, 1)); // => 6-4-3-2-5-1 161 | graph.addEdge(1, 2); 162 | graph.addEdge(4, 5); 163 | console.log('graph relations (number of edges):', graph.relations()); // => 7 164 | console.log('path from 6 to 1:', graph.pathFromTo(6, 1)); // => 6-4-5-1 165 | graph.removeVertex(5); 166 | console.log('graph size (number of vertices):', graph.size()); // => 5 167 | console.log('graph relations (number of edges):', graph.relations()); // => 4 168 | console.log('path from 6 to 1:', graph.pathFromTo(6, 1)); // => 6-4-3-2-1 169 | -------------------------------------------------------------------------------- /data-structures-in-javascript/graph.js: -------------------------------------------------------------------------------- 1 | function Graph() { 2 | this.vertices = []; 3 | this.edges = []; 4 | this.numberOfEdges = 0; 5 | } 6 | 7 | Graph.prototype.addVertex = function(vertex) { 8 | this.vertices.push(vertex); 9 | this.edges[vertex] = []; 10 | }; 11 | Graph.prototype.removeVertex = function(vertex) { 12 | var index = this.vertices.indexOf(vertex); 13 | if(~index) { 14 | this.vertices.splice(index, 1); 15 | } 16 | while(this.edges[vertex].length) { 17 | var adjacentVertex = this.edges[vertex].pop(); 18 | this.removeEdge(adjacentVertex, vertex); 19 | } 20 | }; 21 | Graph.prototype.addEdge = function(vertex1, vertex2) { 22 | this.edges[vertex1].push(vertex2); 23 | this.edges[vertex2].push(vertex1); 24 | this.numberOfEdges++; 25 | }; 26 | Graph.prototype.removeEdge = function(vertex1, vertex2) { 27 | var index1 = this.edges[vertex1] ? this.edges[vertex1].indexOf(vertex2) : -1; 28 | var index2 = this.edges[vertex2] ? this.edges[vertex2].indexOf(vertex1) : -1; 29 | if(~index1) { 30 | this.edges[vertex1].splice(index1, 1); 31 | this.numberOfEdges--; 32 | } 33 | if(~index2) { 34 | this.edges[vertex2].splice(index2, 1); 35 | } 36 | }; 37 | Graph.prototype.size = function() { 38 | return this.vertices.length; 39 | }; 40 | Graph.prototype.relations = function() { 41 | return this.numberOfEdges; 42 | }; 43 | Graph.prototype.traverseDFS = function(vertex, fn) { 44 | if(!~this.vertices.indexOf(vertex)) { 45 | return console.log('Vertex not found'); 46 | } 47 | var visited = []; 48 | this._traverseDFS(vertex, visited, fn); 49 | }; 50 | Graph.prototype._traverseDFS = function(vertex, visited, fn) { 51 | visited[vertex] = true; 52 | if(this.edges[vertex] !== undefined) { 53 | fn(vertex); 54 | } 55 | for(var i = 0; i < this.edges[vertex].length; i++) { 56 | if(!visited[this.edges[vertex][i]]) { 57 | this._traverseDFS(this.edges[vertex][i], visited, fn); 58 | } 59 | } 60 | }; 61 | Graph.prototype.traverseBFS = function(vertex, fn) { 62 | if(!~this.vertices.indexOf(vertex)) { 63 | return console.log('Vertex not found'); 64 | } 65 | var queue = []; 66 | queue.push(vertex); 67 | var visited = []; 68 | visited[vertex] = true; 69 | 70 | while(queue.length) { 71 | vertex = queue.shift(); 72 | fn(vertex); 73 | for(var i = 0; i < this.edges[vertex].length; i++) { 74 | if(!visited[this.edges[vertex][i]]) { 75 | visited[this.edges[vertex][i]] = true; 76 | queue.push(this.edges[vertex][i]); 77 | } 78 | } 79 | } 80 | }; 81 | Graph.prototype.pathFromTo = function(vertexSource, vertexDestination) { 82 | if(!~this.vertices.indexOf(vertexSource)) { 83 | return console.log('Vertex not found'); 84 | } 85 | var queue = []; 86 | queue.push(vertexSource); 87 | var visited = []; 88 | visited[vertexSource] = true; 89 | var paths = []; 90 | 91 | while(queue.length) { 92 | var vertex = queue.shift(); 93 | for(var i = 0; i < this.edges[vertex].length; i++) { 94 | if(!visited[this.edges[vertex][i]]) { 95 | visited[this.edges[vertex][i]] = true; 96 | queue.push(this.edges[vertex][i]); 97 | // save paths between vertices 98 | paths[this.edges[vertex][i]] = vertex; 99 | } 100 | } 101 | } 102 | if(!visited[vertexDestination]) { 103 | return undefined; 104 | } 105 | 106 | var path = []; 107 | for(var j = vertexDestination; j != vertexSource; j = paths[j]) { 108 | path.push(j); 109 | } 110 | path.push(j); 111 | return path.reverse().join('-'); 112 | }; 113 | Graph.prototype.print = function() { 114 | console.log(this.vertices.map(function(vertex) { 115 | return (vertex + ' -> ' + this.edges[vertex].join(', ')).trim(); 116 | }, this).join(' | ')); 117 | }; 118 | 119 | var graph = new Graph(); 120 | graph.addVertex(1); 121 | graph.addVertex(2); 122 | graph.addVertex(3); 123 | graph.addVertex(4); 124 | graph.addVertex(5); 125 | graph.addVertex(6); 126 | graph.print(); // 1 -> | 2 -> | 3 -> | 4 -> | 5 -> | 6 -> 127 | graph.addEdge(1, 2); 128 | graph.addEdge(1, 5); 129 | graph.addEdge(2, 3); 130 | graph.addEdge(2, 5); 131 | graph.addEdge(3, 4); 132 | graph.addEdge(4, 5); 133 | graph.addEdge(4, 6); 134 | graph.print(); // 1 -> 2, 5 | 2 -> 1, 3, 5 | 3 -> 2, 4 | 4 -> 3, 5, 6 | 5 -> 1, 2, 4 | 6 -> 4 135 | console.log('graph size (number of vertices):', graph.size()); // => 6 136 | console.log('graph relations (number of edges):', graph.relations()); // => 7 137 | graph.traverseDFS(1, function(vertex) { console.log(vertex); }); // => 1 2 3 4 5 6 138 | console.log('---'); 139 | graph.traverseBFS(1, function(vertex) { console.log(vertex); }); // => 1 2 5 3 4 6 140 | graph.traverseDFS(0, function(vertex) { console.log(vertex); }); // => 'Vertex not found' 141 | graph.traverseBFS(0, function(vertex) { console.log(vertex); }); // => 'Vertex not found' 142 | console.log('path from 6 to 1:', graph.pathFromTo(6, 1)); // => 6-4-5-1 143 | console.log('path from 3 to 5:', graph.pathFromTo(3, 5)); // => 3-2-5 144 | graph.removeEdge(1, 2); 145 | graph.removeEdge(4, 5); 146 | graph.removeEdge(10, 11); 147 | console.log('graph relations (number of edges):', graph.relations()); // => 5 148 | console.log('path from 6 to 1:', graph.pathFromTo(6, 1)); // => 6-4-3-2-5-1 149 | graph.addEdge(1, 2); 150 | graph.addEdge(4, 5); 151 | console.log('graph relations (number of edges):', graph.relations()); // => 7 152 | console.log('path from 6 to 1:', graph.pathFromTo(6, 1)); // => 6-4-5-1 153 | graph.removeVertex(5); 154 | console.log('graph size (number of vertices):', graph.size()); // => 5 155 | console.log('graph relations (number of edges):', graph.relations()); // => 4 156 | console.log('path from 6 to 1:', graph.pathFromTo(6, 1)); // => 6-4-3-2-1 157 | -------------------------------------------------------------------------------- /data-structures-in-javascript/hash-table.es6.js: -------------------------------------------------------------------------------- 1 | class HashTable { 2 | constructor(size) { 3 | this.values = {}; 4 | this.numberOfValues = 0; 5 | this.size = size; 6 | } 7 | 8 | add(key, value) { 9 | const hash = this.calculateHash(key); 10 | if(!this.values.hasOwnProperty(hash)) { 11 | this.values[hash] = {}; 12 | } 13 | if(!this.values[hash].hasOwnProperty(key)) { 14 | this.numberOfValues++; 15 | } 16 | this.values[hash][key] = value; 17 | } 18 | 19 | remove(key) { 20 | const hash = this.calculateHash(key); 21 | if(this.values.hasOwnProperty(hash) && this.values[hash].hasOwnProperty(key)) { 22 | delete this.values[hash][key]; 23 | this.numberOfValues--; 24 | } 25 | } 26 | 27 | calculateHash(key) { 28 | return key.toString().length % this.size; 29 | } 30 | 31 | search(key) { 32 | const hash = this.calculateHash(key); 33 | if(this.values.hasOwnProperty(hash) && this.values[hash].hasOwnProperty(key)) { 34 | return this.values[hash][key]; 35 | } else { 36 | return null; 37 | } 38 | } 39 | 40 | length() { 41 | return this.numberOfValues; 42 | } 43 | 44 | print() { 45 | let string = ''; 46 | for(const value in this.values) { 47 | for(const key in this.values[value]) { 48 | string += `${this.values[value][key]} `; 49 | } 50 | } 51 | console.log(string.trim()); 52 | } 53 | } 54 | 55 | const hashTable = new HashTable(3); 56 | hashTable.add('first', 1); 57 | hashTable.add('second', 2); 58 | hashTable.add('third', 3); 59 | hashTable.add('fourth', 4); 60 | hashTable.add('fifth', 5); 61 | hashTable.print(); // => 2 4 1 3 5 62 | console.log('length gives 5:', hashTable.length()); // => 5 63 | console.log('search second gives 2:', hashTable.search('second')); // => 2 64 | hashTable.remove('fourth'); 65 | hashTable.remove('first'); 66 | hashTable.print(); // => 2 3 5 67 | console.log('length gives 3:', hashTable.length()); // => 3 68 | -------------------------------------------------------------------------------- /data-structures-in-javascript/hash-table.js: -------------------------------------------------------------------------------- 1 | function HashTable(size) { 2 | this.values = {}; 3 | this.numberOfValues = 0; 4 | this.size = size; 5 | } 6 | 7 | HashTable.prototype.add = function(key, value) { 8 | var hash = this.calculateHash(key); 9 | if(!this.values.hasOwnProperty(hash)) { 10 | this.values[hash] = {}; 11 | } 12 | if(!this.values[hash].hasOwnProperty(key)) { 13 | this.numberOfValues++; 14 | } 15 | this.values[hash][key] = value; 16 | }; 17 | HashTable.prototype.remove = function(key) { 18 | var hash = this.calculateHash(key); 19 | if(this.values.hasOwnProperty(hash) && this.values[hash].hasOwnProperty(key)) { 20 | delete this.values[hash][key]; 21 | this.numberOfValues--; 22 | } 23 | }; 24 | HashTable.prototype.calculateHash = function(key) { 25 | return key.toString().length % this.size; 26 | }; 27 | HashTable.prototype.search = function(key) { 28 | var hash = this.calculateHash(key); 29 | if(this.values.hasOwnProperty(hash) && this.values[hash].hasOwnProperty(key)) { 30 | return this.values[hash][key]; 31 | } else { 32 | return null; 33 | } 34 | }; 35 | HashTable.prototype.length = function() { 36 | return this.numberOfValues; 37 | }; 38 | HashTable.prototype.print = function() { 39 | var string = ''; 40 | for(var value in this.values) { 41 | for(var key in this.values[value]) { 42 | string += this.values[value][key] + ' '; 43 | } 44 | } 45 | console.log(string.trim()); 46 | }; 47 | 48 | var hashTable = new HashTable(3); 49 | hashTable.add('first', 1); 50 | hashTable.add('second', 2); 51 | hashTable.add('third', 3); 52 | hashTable.add('fourth', 4); 53 | hashTable.add('fifth', 5); 54 | hashTable.print(); // => 2 4 1 3 5 55 | console.log('length gives 5:', hashTable.length()); // => 5 56 | console.log('search second gives 2:', hashTable.search('second')); // => 2 57 | hashTable.remove('fourth'); 58 | hashTable.remove('first'); 59 | hashTable.print(); // => 2 3 5 60 | console.log('length gives 3:', hashTable.length()); // => 3 61 | -------------------------------------------------------------------------------- /data-structures-in-javascript/queue.es6.js: -------------------------------------------------------------------------------- 1 | class Queue { 2 | constructor() { 3 | this.queue = []; 4 | } 5 | 6 | enqueue(value) { 7 | this.queue.push(value); 8 | } 9 | 10 | dequeue() { 11 | return this.queue.shift(); 12 | } 13 | 14 | peek() { 15 | return this.queue[0]; 16 | } 17 | 18 | length() { 19 | return this.queue.length; 20 | } 21 | 22 | print() { 23 | console.log(this.queue.join(' ')); 24 | } 25 | } 26 | 27 | const queue = new Queue(); 28 | queue.enqueue(1); 29 | queue.enqueue(2); 30 | queue.enqueue(3); 31 | queue.print(); // => 1 2 3 32 | console.log('length is 3:', queue.length()); // => 3 33 | console.log('peek is 1:', queue.peek()); // => 3 34 | console.log('dequeue is 1:', queue.dequeue()); // => 1 35 | queue.print(); // => 2 3 36 | console.log('dequeue is 2:', queue.dequeue()); // => 2 37 | console.log('length is 1:', queue.length()); // => 1 38 | console.log('dequeue is 3:', queue.dequeue()); // => 3 39 | queue.print(); // => '' 40 | console.log('peek is undefined:', queue.peek()); // => undefined 41 | console.log('dequeue is undefined:', queue.dequeue()); // => undefined 42 | -------------------------------------------------------------------------------- /data-structures-in-javascript/queue.js: -------------------------------------------------------------------------------- 1 | function Queue() { 2 | this.queue = []; 3 | } 4 | 5 | Queue.prototype.enqueue = function(value) { 6 | this.queue.push(value); 7 | }; 8 | Queue.prototype.dequeue = function() { 9 | return this.queue.shift(); 10 | }; 11 | Queue.prototype.peek = function() { 12 | return this.queue[0]; 13 | }; 14 | Queue.prototype.length = function() { 15 | return this.queue.length; 16 | }; 17 | Queue.prototype.print = function() { 18 | console.log(this.queue.join(' ')); 19 | }; 20 | 21 | var queue = new Queue(); 22 | queue.enqueue(1); 23 | queue.enqueue(2); 24 | queue.enqueue(3); 25 | queue.print(); // => 1 2 3 26 | console.log('length is 3:', queue.length()); // => 3 27 | console.log('peek is 1:', queue.peek()); // => 3 28 | console.log('dequeue is 1:', queue.dequeue()); // => 1 29 | queue.print(); // => 2 3 30 | console.log('dequeue is 2:', queue.dequeue()); // => 2 31 | console.log('length is 1:', queue.length()); // => 1 32 | console.log('dequeue is 3:', queue.dequeue()); // => 3 33 | queue.print(); // => '' 34 | console.log('peek is undefined:', queue.peek()); // => undefined 35 | console.log('dequeue is undefined:', queue.dequeue()); // => undefined 36 | -------------------------------------------------------------------------------- /data-structures-in-javascript/set.es6.js: -------------------------------------------------------------------------------- 1 | class Set { 2 | constructor() { 3 | this.values = []; 4 | this.numberOfValues = 0; 5 | } 6 | 7 | add(value) { 8 | if(!~this.values.indexOf(value)) { 9 | this.values.push(value); 10 | this.numberOfValues++; 11 | } 12 | } 13 | 14 | remove(value) { 15 | const index = this.values.indexOf(value); 16 | if(~index) { 17 | this.values.splice(index, 1); 18 | this.numberOfValues--; 19 | } 20 | } 21 | 22 | contains(value) { 23 | return this.values.indexOf(value) !== -1; 24 | } 25 | 26 | union(set) { 27 | const newSet = new Set(); 28 | set.values.forEach(value => { 29 | newSet.add(value); 30 | }); 31 | this.values.forEach(value => { 32 | newSet.add(value); 33 | }); 34 | return newSet; 35 | } 36 | 37 | intersect(set) { 38 | const newSet = new Set(); 39 | this.values.forEach(value => { 40 | if(set.contains(value)) { 41 | newSet.add(value); 42 | } 43 | }); 44 | return newSet; 45 | } 46 | 47 | difference(set) { 48 | const newSet = new Set(); 49 | this.values.forEach(value => { 50 | if(!set.contains(value)) { 51 | newSet.add(value); 52 | } 53 | }); 54 | return newSet; 55 | } 56 | 57 | isSubset(set) { 58 | return set.values.every(function(value) { 59 | return this.contains(value); 60 | }, this); 61 | } 62 | 63 | length() { 64 | return this.numberOfValues; 65 | } 66 | 67 | print() { 68 | console.log(this.values.join(' ')); 69 | } 70 | } 71 | 72 | const set = new Set(); 73 | set.add(1); 74 | set.add(2); 75 | set.add(3); 76 | set.add(4); 77 | set.print(); // => 1 2 3 4 78 | set.remove(3); 79 | set.print(); // => 1 2 4 80 | console.log('contains 4 is true:', set.contains(4)); // => true 81 | console.log('contains 3 is false:', set.contains(3)); // => false 82 | console.log('---'); 83 | const set1 = new Set(); 84 | set1.add(1); 85 | set1.add(2); 86 | const set2 = new Set(); 87 | set2.add(2); 88 | set2.add(3); 89 | const set3 = set2.union(set1); 90 | set3.print(); // => 1 2 3 91 | const set4 = set2.intersect(set1); 92 | set4.print(); // => 2 93 | const set5 = set.difference(set3); // 1 2 4 diff 1 2 3 94 | set5.print(); // => 4 95 | const set6 = set3.difference(set); // 1 2 3 diff 1 2 4 96 | set6.print(); // => 3 97 | console.log('set1 subset of set is true:', set.isSubset(set1)); // => true 98 | console.log('set2 subset of set is false:', set.isSubset(set2)); // => false 99 | console.log('set1 length gives 2:', set1.length()); // => 2 100 | console.log('set3 length gives 3:', set3.length()); // => 3 101 | -------------------------------------------------------------------------------- /data-structures-in-javascript/set.js: -------------------------------------------------------------------------------- 1 | function Set() { 2 | this.values = []; 3 | this.numberOfValues = 0; 4 | } 5 | 6 | Set.prototype.add = function(value) { 7 | if(!~this.values.indexOf(value)) { 8 | this.values.push(value); 9 | this.numberOfValues++; 10 | } 11 | }; 12 | Set.prototype.remove = function(value) { 13 | var index = this.values.indexOf(value); 14 | if(~index) { 15 | this.values.splice(index, 1); 16 | this.numberOfValues--; 17 | } 18 | }; 19 | Set.prototype.contains = function(value) { 20 | return this.values.indexOf(value) !== -1; 21 | }; 22 | Set.prototype.union = function(set) { 23 | var newSet = new Set(); 24 | set.values.forEach(function(value) { 25 | newSet.add(value); 26 | }); 27 | this.values.forEach(function(value) { 28 | newSet.add(value); 29 | }); 30 | return newSet; 31 | }; 32 | Set.prototype.intersect = function(set) { 33 | var newSet = new Set(); 34 | this.values.forEach(function(value) { 35 | if(set.contains(value)) { 36 | newSet.add(value); 37 | } 38 | }); 39 | return newSet; 40 | }; 41 | Set.prototype.difference = function(set) { 42 | var newSet = new Set(); 43 | this.values.forEach(function(value) { 44 | if(!set.contains(value)) { 45 | newSet.add(value); 46 | } 47 | }); 48 | return newSet; 49 | }; 50 | Set.prototype.isSubset = function(set) { 51 | return set.values.every(function(value) { 52 | return this.contains(value); 53 | }, this); 54 | }; 55 | Set.prototype.length = function() { 56 | return this.numberOfValues; 57 | }; 58 | Set.prototype.print = function() { 59 | console.log(this.values.join(' ')); 60 | }; 61 | 62 | var set = new Set(); 63 | set.add(1); 64 | set.add(2); 65 | set.add(3); 66 | set.add(4); 67 | set.print(); // => 1 2 3 4 68 | set.remove(3); 69 | set.print(); // => 1 2 4 70 | console.log('contains 4 is true:', set.contains(4)); // => true 71 | console.log('contains 3 is false:', set.contains(3)); // => false 72 | console.log('---'); 73 | var set1 = new Set(); 74 | set1.add(1); 75 | set1.add(2); 76 | var set2 = new Set(); 77 | set2.add(2); 78 | set2.add(3); 79 | var set3 = set2.union(set1); 80 | set3.print(); // => 1 2 3 81 | var set4 = set2.intersect(set1); 82 | set4.print(); // => 2 83 | var set5 = set.difference(set3); // 1 2 4 diff 1 2 3 84 | set5.print(); // => 4 85 | var set6 = set3.difference(set); // 1 2 3 diff 1 2 4 86 | set6.print(); // => 3 87 | console.log('set1 subset of set is true:', set.isSubset(set1)); // => true 88 | console.log('set2 subset of set is false:', set.isSubset(set2)); // => false 89 | console.log('set1 length gives 2:', set1.length()); // => 2 90 | console.log('set3 length gives 3:', set3.length()); // => 3 91 | -------------------------------------------------------------------------------- /data-structures-in-javascript/singly-linked-list.es6.js: -------------------------------------------------------------------------------- 1 | function Node(data) { 2 | this.data = data; 3 | this.next = null; 4 | } 5 | 6 | class SinglyLinkedList { 7 | constructor() { 8 | this.head = null; 9 | this.tail = null; 10 | this.numberOfValues = 0; 11 | } 12 | 13 | add(data) { 14 | const node = new Node(data); 15 | if(!this.head) { 16 | this.head = node; 17 | this.tail = node; 18 | } else { 19 | this.tail.next = node; 20 | this.tail = node; 21 | } 22 | this.numberOfValues++; 23 | } 24 | 25 | remove(data) { 26 | let previous = this.head; 27 | let current = this.head; 28 | while(current) { 29 | if(current.data === data) { 30 | if(current === this.head) { 31 | this.head = this.head.next; 32 | } 33 | if(current === this.tail) { 34 | this.tail = previous; 35 | } 36 | previous.next = current.next; 37 | this.numberOfValues--; 38 | } else { 39 | previous = current; 40 | } 41 | current = current.next; 42 | } 43 | } 44 | 45 | insertAfter(data, toNodeData) { 46 | let current = this.head; 47 | while(current) { 48 | if(current.data === toNodeData) { 49 | const node = new Node(data); 50 | if(current === this.tail) { 51 | this.tail.next = node; 52 | this.tail = node; 53 | } else { 54 | node.next = current.next; 55 | current.next = node; 56 | } 57 | this.numberOfValues++; 58 | } 59 | current = current.next; 60 | } 61 | } 62 | 63 | traverse(fn) { 64 | let current = this.head; 65 | while(current) { 66 | if(fn) { 67 | fn(current); 68 | } 69 | current = current.next; 70 | } 71 | } 72 | 73 | length() { 74 | return this.numberOfValues; 75 | } 76 | 77 | print() { 78 | let string = ''; 79 | let current = this.head; 80 | while(current) { 81 | string += `${current.data} `; 82 | current = current.next; 83 | } 84 | console.log(string.trim()); 85 | } 86 | } 87 | 88 | const singlyLinkedList = new SinglyLinkedList(); 89 | singlyLinkedList.print(); // => '' 90 | singlyLinkedList.add(1); 91 | singlyLinkedList.add(2); 92 | singlyLinkedList.add(3); 93 | singlyLinkedList.add(4); 94 | singlyLinkedList.print(); // => 1 2 3 4 95 | console.log('length is 4:', singlyLinkedList.length()); // => 4 96 | singlyLinkedList.remove(3); // remove value 97 | singlyLinkedList.print(); // => 1 2 4 98 | singlyLinkedList.remove(9); // remove non existing value 99 | singlyLinkedList.print(); // => 1 2 4 100 | singlyLinkedList.remove(1); // remove head 101 | singlyLinkedList.print(); // => 2 4 102 | singlyLinkedList.remove(4); // remove tail 103 | singlyLinkedList.print(); // => 2 104 | console.log('length is 1:', singlyLinkedList.length()); // => 1 105 | singlyLinkedList.add(6); 106 | singlyLinkedList.print(); // => 2 6 107 | singlyLinkedList.insertAfter(3, 2); 108 | singlyLinkedList.print(); // => 2 3 6 109 | singlyLinkedList.insertAfter(4, 3); 110 | singlyLinkedList.print(); // => 2 3 4 6 111 | singlyLinkedList.insertAfter(5, 9); // insertAfter a non existing node 112 | singlyLinkedList.print(); // => 2 3 4 6 113 | singlyLinkedList.insertAfter(5, 4); 114 | singlyLinkedList.insertAfter(7, 6); // insertAfter the tail 115 | singlyLinkedList.print(); // => 2 3 4 5 6 7 116 | singlyLinkedList.add(8); // add node with normal method 117 | singlyLinkedList.print(); // => 2 3 4 5 6 7 8 118 | console.log('length is 7:', singlyLinkedList.length()); // => 7 119 | singlyLinkedList.traverse(node => { node.data = node.data + 10; }); 120 | singlyLinkedList.print(); // => 12 13 14 15 16 17 18 121 | singlyLinkedList.traverse(node => { console.log(node.data); }); // => 12 13 14 15 16 17 18 122 | console.log('length is 7:', singlyLinkedList.length()); // => 7 123 | -------------------------------------------------------------------------------- /data-structures-in-javascript/singly-linked-list.js: -------------------------------------------------------------------------------- 1 | function Node(data) { 2 | this.data = data; 3 | this.next = null; 4 | } 5 | 6 | function SinglyLinkedList() { 7 | this.head = null; 8 | this.tail = null; 9 | this.numberOfValues = 0; 10 | } 11 | 12 | SinglyLinkedList.prototype.add = function(data) { 13 | var node = new Node(data); 14 | if(!this.head) { 15 | this.head = node; 16 | this.tail = node; 17 | } else { 18 | this.tail.next = node; 19 | this.tail = node; 20 | } 21 | this.numberOfValues++; 22 | }; 23 | SinglyLinkedList.prototype.remove = function(data) { 24 | var previous = this.head; 25 | var current = this.head; 26 | while(current) { 27 | if(current.data === data) { 28 | if(current === this.head) { 29 | this.head = this.head.next; 30 | } 31 | if(current === this.tail) { 32 | this.tail = previous; 33 | } 34 | previous.next = current.next; 35 | this.numberOfValues--; 36 | } else { 37 | previous = current; 38 | } 39 | current = current.next; 40 | } 41 | }; 42 | SinglyLinkedList.prototype.insertAfter = function(data, toNodeData) { 43 | var current = this.head; 44 | while(current) { 45 | if(current.data === toNodeData) { 46 | var node = new Node(data); 47 | if(current === this.tail) { 48 | this.tail.next = node; 49 | this.tail = node; 50 | } else { 51 | node.next = current.next; 52 | current.next = node; 53 | } 54 | this.numberOfValues++; 55 | } 56 | current = current.next; 57 | } 58 | }; 59 | SinglyLinkedList.prototype.traverse = function(fn) { 60 | var current = this.head; 61 | while(current) { 62 | if(fn) { 63 | fn(current); 64 | } 65 | current = current.next; 66 | } 67 | }; 68 | SinglyLinkedList.prototype.length = function() { 69 | return this.numberOfValues; 70 | }; 71 | SinglyLinkedList.prototype.print = function() { 72 | var string = ''; 73 | var current = this.head; 74 | while(current) { 75 | string += current.data + ' '; 76 | current = current.next; 77 | } 78 | console.log(string.trim()); 79 | }; 80 | 81 | var singlyLinkedList = new SinglyLinkedList(); 82 | singlyLinkedList.print(); // => '' 83 | singlyLinkedList.add(1); 84 | singlyLinkedList.add(2); 85 | singlyLinkedList.add(3); 86 | singlyLinkedList.add(4); 87 | singlyLinkedList.print(); // => 1 2 3 4 88 | console.log('length is 4:', singlyLinkedList.length()); // => 4 89 | singlyLinkedList.remove(3); // remove value 90 | singlyLinkedList.print(); // => 1 2 4 91 | singlyLinkedList.remove(9); // remove non existing value 92 | singlyLinkedList.print(); // => 1 2 4 93 | singlyLinkedList.remove(1); // remove head 94 | singlyLinkedList.print(); // => 2 4 95 | singlyLinkedList.remove(4); // remove tail 96 | singlyLinkedList.print(); // => 2 97 | console.log('length is 1:', singlyLinkedList.length()); // => 1 98 | singlyLinkedList.add(6); 99 | singlyLinkedList.print(); // => 2 6 100 | singlyLinkedList.insertAfter(3, 2); 101 | singlyLinkedList.print(); // => 2 3 6 102 | singlyLinkedList.insertAfter(4, 3); 103 | singlyLinkedList.print(); // => 2 3 4 6 104 | singlyLinkedList.insertAfter(5, 9); // insertAfter a non existing node 105 | singlyLinkedList.print(); // => 2 3 4 6 106 | singlyLinkedList.insertAfter(5, 4); 107 | singlyLinkedList.insertAfter(7, 6); // insertAfter the tail 108 | singlyLinkedList.print(); // => 2 3 4 5 6 7 109 | singlyLinkedList.add(8); // add node with normal method 110 | singlyLinkedList.print(); // => 2 3 4 5 6 7 8 111 | console.log('length is 7:', singlyLinkedList.length()); // => 7 112 | singlyLinkedList.traverse(function(node) { node.data = node.data + 10; }); 113 | singlyLinkedList.print(); // => 12 13 14 15 16 17 18 114 | singlyLinkedList.traverse(function(node) { console.log(node.data); }); // => 12 13 14 15 16 17 18 115 | console.log('length is 7:', singlyLinkedList.length()); // => 7 116 | -------------------------------------------------------------------------------- /data-structures-in-javascript/stack.es6.js: -------------------------------------------------------------------------------- 1 | class Stack { 2 | constructor() { 3 | this.stack = []; 4 | } 5 | 6 | push(value) { 7 | this.stack.push(value); 8 | } 9 | 10 | pop() { 11 | return this.stack.pop(); 12 | } 13 | 14 | peek() { 15 | return this.stack[this.stack.length - 1]; 16 | } 17 | 18 | length() { 19 | return this.stack.length; 20 | } 21 | 22 | print() { 23 | console.log(this.stack.join(' ')); 24 | } 25 | } 26 | 27 | const stack = new Stack(); 28 | stack.push(1); 29 | stack.push(2); 30 | stack.push(3); 31 | stack.print(); // => 1 2 3 32 | console.log('length is 3:', stack.length()); // => 3 33 | console.log('peek is 3:', stack.peek()); // => 3 34 | console.log('pop is 3:', stack.pop()); // => 3 35 | stack.print(); // => 1 2 36 | console.log('pop is 2:', stack.pop()); // => 2 37 | console.log('length is 1:', stack.length()); // => 1 38 | console.log('pop is 1:', stack.pop()); // => 1 39 | stack.print(); // => '' 40 | console.log('peek is undefined:', stack.peek()); // => undefined 41 | console.log('pop is undefined:', stack.pop()); // => undefined 42 | -------------------------------------------------------------------------------- /data-structures-in-javascript/stack.js: -------------------------------------------------------------------------------- 1 | function Stack() { 2 | this.stack = []; 3 | } 4 | 5 | Stack.prototype.push = function(value) { 6 | this.stack.push(value); 7 | }; 8 | Stack.prototype.pop = function() { 9 | return this.stack.pop(); 10 | }; 11 | Stack.prototype.peek = function() { 12 | return this.stack[this.stack.length - 1]; 13 | }; 14 | Stack.prototype.length = function() { 15 | return this.stack.length; 16 | }; 17 | Stack.prototype.print = function() { 18 | console.log(this.stack.join(' ')); 19 | }; 20 | 21 | var stack = new Stack(); 22 | stack.push(1); 23 | stack.push(2); 24 | stack.push(3); 25 | stack.print(); // => 1 2 3 26 | console.log('length is 3:', stack.length()); // => 3 27 | console.log('peek is 3:', stack.peek()); // => 3 28 | console.log('pop is 3:', stack.pop()); // => 3 29 | stack.print(); // => 1 2 30 | console.log('pop is 2:', stack.pop()); // => 2 31 | console.log('length is 1:', stack.length()); // => 1 32 | console.log('pop is 1:', stack.pop()); // => 1 33 | stack.print(); // => '' 34 | console.log('peek is undefined:', stack.peek()); // => undefined 35 | console.log('pop is undefined:', stack.pop()); // => undefined 36 | -------------------------------------------------------------------------------- /data-structures-in-javascript/tree.es6.js: -------------------------------------------------------------------------------- 1 | function Node(data) { 2 | this.data = data; 3 | this.children = []; 4 | } 5 | 6 | class Tree { 7 | constructor() { 8 | this.root = null; 9 | } 10 | 11 | add(data, toNodeData) { 12 | const node = new Node(data); 13 | const parent = toNodeData ? this.findBFS(toNodeData) : null; 14 | if(parent) { 15 | parent.children.push(node); 16 | } else { 17 | if(!this.root) { 18 | this.root = node; 19 | } else { 20 | return 'Root node is already assigned'; 21 | } 22 | } 23 | } 24 | 25 | remove(data) { 26 | if(this.root.data === data) { 27 | this.root = null; 28 | } 29 | 30 | const queue = [this.root]; 31 | while(queue.length) { 32 | const node = queue.shift(); 33 | for (let [index, child] of node.children.entries()) { 34 | if(child.data === data) { 35 | node.children.splice(index, 1); 36 | } else { 37 | queue.push(child); 38 | } 39 | } 40 | } 41 | } 42 | 43 | contains(data) { 44 | return !!this.findBFS(data); 45 | } 46 | 47 | findBFS(data) { 48 | const queue = [this.root]; 49 | while(queue.length) { 50 | const node = queue.shift(); 51 | if(node.data === data) { 52 | return node; 53 | } 54 | for(const child of node.children) { 55 | queue.push(child); 56 | } 57 | } 58 | return null; 59 | } 60 | 61 | _preOrder(node, fn) { 62 | if(node) { 63 | if(fn) { 64 | fn(node); 65 | } 66 | for(const child of node.children) { 67 | this._preOrder(child, fn); 68 | } 69 | } 70 | } 71 | 72 | _postOrder(node, fn) { 73 | if(node) { 74 | for(const child of node.children) { 75 | this._postOrder(child, fn); 76 | } 77 | if(fn) { 78 | fn(node); 79 | } 80 | } 81 | } 82 | 83 | traverseDFS(fn, method) { 84 | const current = this.root; 85 | if(method) { 86 | this[`_${method}`](current, fn); 87 | } else { 88 | this._preOrder(current, fn); 89 | } 90 | } 91 | 92 | traverseBFS(fn) { 93 | const queue = [this.root]; 94 | while(queue.length) { 95 | const node = queue.shift(); 96 | if(fn) { 97 | fn(node); 98 | } 99 | for(const child of node.children) { 100 | queue.push(child); 101 | } 102 | } 103 | } 104 | 105 | print() { 106 | if(!this.root) { 107 | return console.log('No root node found'); 108 | } 109 | const newline = new Node('|'); 110 | const queue = [this.root, newline]; 111 | let string = ''; 112 | while(queue.length) { 113 | const node = queue.shift(); 114 | string += `${node.data.toString()} `; 115 | if(node === newline && queue.length) { 116 | queue.push(newline); 117 | } 118 | for(const child of node.children) { 119 | queue.push(child); 120 | } 121 | } 122 | console.log(string.slice(0, -2).trim()); 123 | } 124 | 125 | printByLevel() { 126 | if(!this.root) { 127 | return console.log('No root node found'); 128 | } 129 | const newline = new Node('\n'); 130 | const queue = [this.root, newline]; 131 | let string = ''; 132 | while(queue.length) { 133 | const node = queue.shift(); 134 | string += node.data.toString() + (node.data !== '\n' ? ' ' : ''); 135 | if(node === newline && queue.length) { 136 | queue.push(newline); 137 | } 138 | for(const child of node.children) { 139 | queue.push(child); 140 | } 141 | } 142 | console.log(string.trim()); 143 | } 144 | } 145 | 146 | const tree = new Tree(); 147 | tree.add('ceo'); 148 | tree.add('cto', 'ceo'); 149 | tree.add('dev1', 'cto'); 150 | tree.add('dev2', 'cto'); 151 | tree.add('dev3', 'cto'); 152 | tree.add('cfo', 'ceo'); 153 | tree.add('accountant', 'cfo'); 154 | tree.add('cmo', 'ceo'); 155 | tree.print(); // => ceo | cto cfo cmo | dev1 dev2 dev3 accountant 156 | tree.printByLevel(); // => ceo \n cto cfo cmo \n dev1 dev2 dev3 accountant 157 | console.log('tree contains dev1 is true:', tree.contains('dev1')); // => true 158 | console.log('tree contains dev4 is false:', tree.contains('dev4')); // => false 159 | console.log('--- BFS'); 160 | tree.traverseBFS(node => { console.log(node.data); }); // => ceo cto cfo cmo dev1 dev2 dev3 accountant 161 | console.log('--- DFS preOrder'); 162 | tree.traverseDFS(node => { console.log(node.data); }, 'preOrder'); // => ceo cto dev1 dev2 dev3 cfo accountant cmo 163 | console.log('--- DFS postOrder'); 164 | tree.traverseDFS(node => { console.log(node.data); }, 'postOrder'); // => dev1 dev2 dev3 cto accountant cfo cmo ceo 165 | tree.remove('cmo'); 166 | tree.print(); // => ceo | cto cfo | dev1 dev2 dev3 accountant 167 | tree.remove('cfo'); 168 | tree.print(); // => ceo | cto | dev1 dev2 dev3 169 | -------------------------------------------------------------------------------- /data-structures-in-javascript/tree.js: -------------------------------------------------------------------------------- 1 | function Node(data) { 2 | this.data = data; 3 | this.children = []; 4 | } 5 | 6 | function Tree() { 7 | this.root = null; 8 | } 9 | 10 | Tree.prototype.add = function(data, toNodeData) { 11 | var node = new Node(data); 12 | var parent = toNodeData ? this.findBFS(toNodeData) : null; 13 | if(parent) { 14 | parent.children.push(node); 15 | } else { 16 | if(!this.root) { 17 | this.root = node; 18 | } else { 19 | return 'Root node is already assigned'; 20 | } 21 | } 22 | }; 23 | Tree.prototype.remove = function(data) { 24 | if(this.root.data === data) { 25 | this.root = null; 26 | } 27 | 28 | var queue = [this.root]; 29 | while(queue.length) { 30 | var node = queue.shift(); 31 | for(var i = 0; i < node.children.length; i++) { 32 | if(node.children[i].data === data) { 33 | node.children.splice(i, 1); 34 | } else { 35 | queue.push(node.children[i]); 36 | } 37 | } 38 | } 39 | }; 40 | Tree.prototype.contains = function(data) { 41 | return this.findBFS(data) ? true : false; 42 | }; 43 | Tree.prototype.findBFS = function(data) { 44 | var queue = [this.root]; 45 | while(queue.length) { 46 | var node = queue.shift(); 47 | if(node.data === data) { 48 | return node; 49 | } 50 | for(var i = 0; i < node.children.length; i++) { 51 | queue.push(node.children[i]); 52 | } 53 | } 54 | return null; 55 | }; 56 | Tree.prototype._preOrder = function(node, fn) { 57 | if(node) { 58 | if(fn) { 59 | fn(node); 60 | } 61 | for(var i = 0; i < node.children.length; i++) { 62 | this._preOrder(node.children[i], fn); 63 | } 64 | } 65 | }; 66 | Tree.prototype._postOrder = function(node, fn) { 67 | if(node) { 68 | for(var i = 0; i < node.children.length; i++) { 69 | this._postOrder(node.children[i], fn); 70 | } 71 | if(fn) { 72 | fn(node); 73 | } 74 | } 75 | }; 76 | Tree.prototype.traverseDFS = function(fn, method) { 77 | var current = this.root; 78 | if(method) { 79 | this['_' + method](current, fn); 80 | } else { 81 | this._preOrder(current, fn); 82 | } 83 | }; 84 | Tree.prototype.traverseBFS = function(fn) { 85 | var queue = [this.root]; 86 | while(queue.length) { 87 | var node = queue.shift(); 88 | if(fn) { 89 | fn(node); 90 | } 91 | for(var i = 0; i < node.children.length; i++) { 92 | queue.push(node.children[i]); 93 | } 94 | } 95 | }; 96 | Tree.prototype.print = function() { 97 | if(!this.root) { 98 | return console.log('No root node found'); 99 | } 100 | var newline = new Node('|'); 101 | var queue = [this.root, newline]; 102 | var string = ''; 103 | while(queue.length) { 104 | var node = queue.shift(); 105 | string += node.data.toString() + ' '; 106 | if(node === newline && queue.length) { 107 | queue.push(newline); 108 | } 109 | for(var i = 0; i < node.children.length; i++) { 110 | queue.push(node.children[i]); 111 | } 112 | } 113 | console.log(string.slice(0, -2).trim()); 114 | }; 115 | Tree.prototype.printByLevel = function() { 116 | if(!this.root) { 117 | return console.log('No root node found'); 118 | } 119 | var newline = new Node('\n'); 120 | var queue = [this.root, newline]; 121 | var string = ''; 122 | while(queue.length) { 123 | var node = queue.shift(); 124 | string += node.data.toString() + (node.data !== '\n' ? ' ' : ''); 125 | if(node === newline && queue.length) { 126 | queue.push(newline); 127 | } 128 | for(var i = 0; i < node.children.length; i++) { 129 | queue.push(node.children[i]); 130 | } 131 | } 132 | console.log(string.trim()); 133 | }; 134 | 135 | var tree = new Tree(); 136 | tree.add('ceo'); 137 | tree.add('cto', 'ceo'); 138 | tree.add('dev1', 'cto'); 139 | tree.add('dev2', 'cto'); 140 | tree.add('dev3', 'cto'); 141 | tree.add('cfo', 'ceo'); 142 | tree.add('accountant', 'cfo'); 143 | tree.add('cmo', 'ceo'); 144 | tree.print(); // => ceo | cto cfo cmo | dev1 dev2 dev3 accountant 145 | tree.printByLevel(); // => ceo \n cto cfo cmo \n dev1 dev2 dev3 accountant 146 | console.log('tree contains dev1 is true:', tree.contains('dev1')); // => true 147 | console.log('tree contains dev4 is false:', tree.contains('dev4')); // => false 148 | console.log('--- BFS'); 149 | tree.traverseBFS(function(node) { console.log(node.data); }); // => ceo cto cfo cmo dev1 dev2 dev3 accountant 150 | console.log('--- DFS preOrder'); 151 | tree.traverseDFS(function(node) { console.log(node.data); }, 'preOrder'); // => ceo cto dev1 dev2 dev3 cfo accountant cmo 152 | console.log('--- DFS postOrder'); 153 | tree.traverseDFS(function(node) { console.log(node.data); }, 'postOrder'); // => dev1 dev2 dev3 cto accountant cfo cmo ceo 154 | tree.remove('cmo'); 155 | tree.print(); // => ceo | cto cfo | dev1 dev2 dev3 accountant 156 | tree.remove('cfo'); 157 | tree.print(); // => ceo | cto | dev1 dev2 dev3 158 | -------------------------------------------------------------------------------- /data-structures-in-javascript/trie.es6.js: -------------------------------------------------------------------------------- 1 | function Node(data) { 2 | this.data = data; 3 | this.isWord = false; 4 | this.prefixes = 0; 5 | this.children = {}; 6 | } 7 | 8 | class Trie { 9 | constructor() { 10 | this.root = new Node(''); 11 | } 12 | 13 | add(word) { 14 | if(!this.root) { 15 | return null; 16 | } 17 | this._addNode(this.root, word); 18 | } 19 | 20 | _addNode(node, word) { 21 | if(!node || !word) { 22 | return null; 23 | } 24 | node.prefixes++; 25 | const letter = word.charAt(0); 26 | let child = node.children[letter]; 27 | if(!child) { 28 | child = new Node(letter); 29 | node.children[letter] = child; 30 | } 31 | const remainder = word.substring(1); 32 | if(!remainder) { 33 | child.isWord = true; 34 | } 35 | this._addNode(child, remainder); 36 | } 37 | 38 | remove(word) { 39 | if(!this.root) { 40 | return; 41 | } 42 | if(this.contains(word)) { 43 | this._removeNode(this.root, word); 44 | } 45 | } 46 | 47 | _removeNode(node, word) { 48 | if(!node || !word) { 49 | return; 50 | } 51 | node.prefixes--; 52 | const letter = word.charAt(0); 53 | 54 | const child = node.children[letter]; 55 | if(child) { 56 | const remainder = word.substring(1); 57 | if(remainder) { 58 | if(child.prefixes === 1) { 59 | delete node.children[letter]; 60 | } else { 61 | this._removeNode(child, remainder); 62 | } 63 | } else { 64 | if(child.prefixes === 0) { 65 | delete node.children[letter]; 66 | } else { 67 | child.isWord = false; 68 | } 69 | } 70 | } 71 | } 72 | 73 | contains(word) { 74 | if(!this.root) { 75 | return false; 76 | } 77 | return this._contains(this.root, word); 78 | } 79 | 80 | _contains(node, word) { 81 | if(!node || !word) { 82 | return false; 83 | } 84 | const letter = word.charAt(0); 85 | const child = node.children[letter]; 86 | if(child) { 87 | const remainder = word.substring(1); 88 | if(!remainder && child.isWord) { 89 | return true; 90 | } else { 91 | return this._contains(child, remainder); 92 | } 93 | } else { 94 | return false; 95 | } 96 | } 97 | 98 | countWords() { 99 | if(!this.root) { 100 | return console.log('No root node found'); 101 | } 102 | const queue = [this.root]; 103 | let counter = 0; 104 | while(queue.length) { 105 | const node = queue.shift(); 106 | if(node.isWord) { 107 | counter++; 108 | } 109 | for(const child in node.children) { 110 | if(node.children.hasOwnProperty(child)) { 111 | queue.push(node.children[child]); 112 | } 113 | } 114 | } 115 | return counter; 116 | } 117 | 118 | getWords() { 119 | const words = []; 120 | const word = ''; 121 | this._getWords(this.root, words, word); 122 | return words; 123 | } 124 | 125 | _getWords(node, words, word) { 126 | for(const child in node.children) { 127 | if(node.children.hasOwnProperty(child)) { 128 | word += child; 129 | if (node.children[child].isWord) { 130 | words.push(word); 131 | } 132 | this._getWords(node.children[child], words, word); 133 | word = word.substring(0, word.length - 1); 134 | } 135 | } 136 | } 137 | 138 | print() { 139 | if(!this.root) { 140 | return console.log('No root node found'); 141 | } 142 | const newline = new Node('|'); 143 | const queue = [this.root, newline]; 144 | let string = ''; 145 | while(queue.length) { 146 | const node = queue.shift(); 147 | string += `${node.data.toString()} `; 148 | if(node === newline && queue.length) { 149 | queue.push(newline); 150 | } 151 | for(const child in node.children) { 152 | if(node.children.hasOwnProperty(child)) { 153 | queue.push(node.children[child]); 154 | } 155 | } 156 | } 157 | console.log(string.slice(0, -2).trim()); 158 | } 159 | 160 | printByLevel() { 161 | if(!this.root) { 162 | return console.log('No root node found'); 163 | } 164 | const newline = new Node('\n'); 165 | const queue = [this.root, newline]; 166 | let string = ''; 167 | while(queue.length) { 168 | const node = queue.shift(); 169 | string += node.data.toString() + (node.data !== '\n' ? ' ' : ''); 170 | if(node === newline && queue.length) { 171 | queue.push(newline); 172 | } 173 | for(const child in node.children) { 174 | if(node.children.hasOwnProperty(child)) { 175 | queue.push(node.children[child]); 176 | } 177 | } 178 | } 179 | console.log(string.trim()); 180 | } 181 | } 182 | 183 | const trie = new Trie(); 184 | trie.add('one'); 185 | trie.add('two'); 186 | trie.add('fifth'); 187 | trie.add('fifty'); 188 | trie.print(); // => | o t f | n w i | e o f | t | h y 189 | trie.printByLevel(); // => o t f \n n w i \n e o f \n t \n h y 190 | console.log('words are: one, two, fifth, fifty:', trie.getWords()); // => [ 'one', 'two', 'fifth', 'fifty' ] 191 | console.log('trie count words is 4:', trie.countWords()); // => 4 192 | console.log('trie contains one is true:', trie.contains('one')); // => true 193 | console.log('trie contains on is false:', trie.contains('on')); // => false 194 | trie.remove('one'); 195 | console.log('trie contains one is false:', trie.contains('one')); // => false 196 | console.log('trie count words is 3:', trie.countWords()); // => 3 197 | console.log('words are two, fifth, fifty:', trie.getWords()); // => [ 'two', 'fifth', 'fifty' ] 198 | -------------------------------------------------------------------------------- /data-structures-in-javascript/trie.js: -------------------------------------------------------------------------------- 1 | function Node(data) { 2 | this.data = data; 3 | this.isWord = false; 4 | this.prefixes = 0; 5 | this.children = {}; 6 | } 7 | 8 | function Trie() { 9 | this.root = new Node(''); 10 | } 11 | 12 | Trie.prototype.add = function(word) { 13 | if(!this.root) { 14 | return null; 15 | } 16 | this._addNode(this.root, word); 17 | }; 18 | Trie.prototype._addNode = function(node, word) { 19 | if(!node || !word) { 20 | return null; 21 | } 22 | node.prefixes++; 23 | var letter = word.charAt(0); 24 | var child = node.children[letter]; 25 | if(!child) { 26 | child = new Node(letter); 27 | node.children[letter] = child; 28 | } 29 | var remainder = word.substring(1); 30 | if(!remainder) { 31 | child.isWord = true; 32 | } 33 | this._addNode(child, remainder); 34 | }; 35 | Trie.prototype.remove = function(word) { 36 | if(!this.root) { 37 | return; 38 | } 39 | if(this.contains(word)) { 40 | this._removeNode(this.root, word); 41 | } 42 | }; 43 | Trie.prototype._removeNode = function(node, word) { 44 | if(!node || !word) { 45 | return; 46 | } 47 | node.prefixes--; 48 | var letter = word.charAt(0); 49 | 50 | var child = node.children[letter]; 51 | if(child) { 52 | var remainder = word.substring(1); 53 | if(remainder) { 54 | if(child.prefixes === 1) { 55 | delete node.children[letter]; 56 | } else { 57 | this._removeNode(child, remainder); 58 | } 59 | } else { 60 | if(child.prefixes === 0) { 61 | delete node.children[letter]; 62 | } else { 63 | child.isWord = false; 64 | } 65 | } 66 | } 67 | }; 68 | Trie.prototype.contains = function(word) { 69 | if(!this.root) { 70 | return false; 71 | } 72 | return this._contains(this.root, word); 73 | }; 74 | Trie.prototype._contains = function(node, word) { 75 | if(!node || !word) { 76 | return false; 77 | } 78 | var letter = word.charAt(0); 79 | var child = node.children[letter]; 80 | if(child) { 81 | var remainder = word.substring(1); 82 | if(!remainder && child.isWord) { 83 | return true; 84 | } else { 85 | return this._contains(child, remainder); 86 | } 87 | } else { 88 | return false; 89 | } 90 | }; 91 | Trie.prototype.countWords = function() { 92 | if(!this.root) { 93 | return console.log('No root node found'); 94 | } 95 | var queue = [this.root]; 96 | var counter = 0; 97 | while(queue.length) { 98 | var node = queue.shift(); 99 | if(node.isWord) { 100 | counter++; 101 | } 102 | for(var child in node.children) { 103 | if(node.children.hasOwnProperty(child)) { 104 | queue.push(node.children[child]); 105 | } 106 | } 107 | } 108 | return counter; 109 | }; 110 | Trie.prototype.getWords = function() { 111 | var words = []; 112 | var word = ''; 113 | this._getWords(this.root, words, word); 114 | return words; 115 | }; 116 | Trie.prototype._getWords = function(node, words, word) { 117 | for(var child in node.children) { 118 | if(node.children.hasOwnProperty(child)) { 119 | word += child; 120 | if (node.children[child].isWord) { 121 | words.push(word); 122 | } 123 | this._getWords(node.children[child], words, word); 124 | word = word.substring(0, word.length - 1); 125 | } 126 | } 127 | }; 128 | Trie.prototype.print = function() { 129 | if(!this.root) { 130 | return console.log('No root node found'); 131 | } 132 | var newline = new Node('|'); 133 | var queue = [this.root, newline]; 134 | var string = ''; 135 | while(queue.length) { 136 | var node = queue.shift(); 137 | string += node.data.toString() + ' '; 138 | if(node === newline && queue.length) { 139 | queue.push(newline); 140 | } 141 | for(var child in node.children) { 142 | if(node.children.hasOwnProperty(child)) { 143 | queue.push(node.children[child]); 144 | } 145 | } 146 | } 147 | console.log(string.slice(0, -2).trim()); 148 | }; 149 | Trie.prototype.printByLevel = function() { 150 | if(!this.root) { 151 | return console.log('No root node found'); 152 | } 153 | var newline = new Node('\n'); 154 | var queue = [this.root, newline]; 155 | var string = ''; 156 | while(queue.length) { 157 | var node = queue.shift(); 158 | string += node.data.toString() + (node.data !== '\n' ? ' ' : ''); 159 | if(node === newline && queue.length) { 160 | queue.push(newline); 161 | } 162 | for(var child in node.children) { 163 | if(node.children.hasOwnProperty(child)) { 164 | queue.push(node.children[child]); 165 | } 166 | } 167 | } 168 | console.log(string.trim()); 169 | }; 170 | 171 | var trie = new Trie(); 172 | trie.add('one'); 173 | trie.add('two'); 174 | trie.add('fifth'); 175 | trie.add('fifty'); 176 | trie.print(); // => | o t f | n w i | e o f | t | h y 177 | trie.printByLevel(); // => o t f \n n w i \n e o f \n t \n h y 178 | console.log('words are: one, two, fifth, fifty:', trie.getWords()); // => [ 'one', 'two', 'fifth', 'fifty' ] 179 | console.log('trie count words is 4:', trie.countWords()); // => 4 180 | console.log('trie contains one is true:', trie.contains('one')); // => true 181 | console.log('trie contains on is false:', trie.contains('on')); // => false 182 | trie.remove('one'); 183 | console.log('trie contains one is false:', trie.contains('one')); // => false 184 | console.log('trie count words is 3:', trie.countWords()); // => 3 185 | console.log('words are two, fifth, fifty:', trie.getWords()); // => [ 'two', 'fifth', 'fifty' ] 186 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/README.md: -------------------------------------------------------------------------------- 1 | # Sorting algorithms in JavaScript (ES5 and ES6) 2 | 3 | This repository is part of a series of posts about sorting algorithms reimplemented in JavaScript that I made [on my blog](http://blog.benoitvallon.com). 4 | 5 | # About the #sorting-algorithms series 6 | 7 | The [#sorting-algorithms series](http://blog.benoitvallon.com/category/sorting-algorithms-in-javascript) is a collection of posts about reimplemented sorting algorithms in JavaScript. 8 | 9 | If you are not familiar with sorting algorithms, a quick introduction and the full list of reimplemented sorting algorithms can be found in the [introduction post of the series on sorting algorithms in JavaScript](http://blog.benoitvallon.com/sorting-algorithms-in-javascript/sorting-algorithms-in-javascript). 10 | 11 | If you feel comfortable with the concept of each sorting algorithms and only want to see the code, have a look at the summary post of the series. It removes all explanations and contains only the [JavaScript code for all sorting algorithms](http://blog.benoitvallon.com/sorting-algorithms-in-javascript/sorting-algorithms-in-javascript-all-the-code) discussed in the series. 12 | 13 | # The sorting algorithms in the series 14 | 15 | - [x] Bubble sort 16 | - [x] Selection sort 17 | - [x] Insertion sort 18 | - [x] Shellsort 19 | - [x] Merge sort 20 | - [x] Quicksort 21 | 22 | # A good way to compare all of them 23 | 24 | Unlike the [data structures](http://blog.benoitvallon.com/data-structures-in-javascript/data-structures-in-javascript/), all [sorting algorithms](http://blog.benoitvallon.com/sorting-algorithms-in-javascript/sorting-algorithms-in-javascript) have the same goal and they can all take the same input data. So, for every sorting algorithms of the series, we are going sort an `array` of 10 numbers from 1 to 10. 25 | 26 | By doing so we will be able to compare the different sorting algorithms more easily. Sorting algorithms are very sensitive to the input data so we will also try different input data to see how they affect the performances. 27 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/bubble-sort-counters.es6.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | const arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | const arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | const arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | // be careful: this is a very basic implementation which is nice to understand the deep principle of bubble sort (going through all comparisons) but it can be greatly improved for performances 7 | function bubbleSortBasic(array) { 8 | let countOuter = 0; 9 | let countInner = 0; 10 | let countSwap = 0; 11 | 12 | for(let i = 0; i < array.length; i++) { 13 | countOuter++; 14 | for(let j = 1; j < array.length; j++) { 15 | countInner++; 16 | if(array[j - 1] > array[j]) { 17 | countSwap++; 18 | [array[j - 1], array[j]] = [array[j], array[j - 1]]; 19 | } 20 | } 21 | } 22 | 23 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 24 | return array; 25 | } 26 | 27 | bubbleSortBasic(arrayRandom.slice()); // => outer: 10 inner: 90 swap: 21 28 | bubbleSortBasic(arrayOrdered.slice()); // => outer: 10 inner: 90 swap: 0 29 | bubbleSortBasic(arrayReversed.slice()); // => outer: 10 inner: 90 swap: 45 30 | 31 | // correct implementation: this is the usual implementation of the bubble sort algorithm. Some loops execution are avoided if not they are not needed 32 | function bubbleSort(array) { 33 | let countOuter = 0; 34 | let countInner = 0; 35 | let countSwap = 0; 36 | 37 | let swapped; 38 | do { 39 | countOuter++; 40 | swapped = false; 41 | for(let i = 0; i < array.length; i++) { 42 | countInner++; 43 | if(array[i] && array[i + 1] && array[i] > array[i + 1]) { 44 | countSwap++; 45 | [array[i], array[i + 1]] = [array[i + 1], array[i]]; 46 | swapped = true; 47 | } 48 | } 49 | } while(swapped); 50 | 51 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 52 | return array; 53 | } 54 | 55 | bubbleSort(arrayRandom.slice()); // => outer: 9 inner: 90 swap: 21 56 | bubbleSort(arrayOrdered.slice()); // => outer: 1 inner: 10 swap: 0 57 | bubbleSort(arrayReversed.slice()); // => outer: 10 inner: 100 swap: 45 58 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/bubble-sort-counters.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | var arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | var arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | var arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | // swap function helper 7 | function swap(array, i, j) { 8 | var temp = array[i]; 9 | array[i] = array[j]; 10 | array[j] = temp; 11 | } 12 | 13 | // be careful: this is a very basic implementation which is nice to understand the deep principle of bubble sort (going through all comparisons) but it can be greatly improved for performances 14 | function bubbleSortBasic(array) { 15 | var countOuter = 0; 16 | var countInner = 0; 17 | var countSwap = 0; 18 | 19 | for(var i = 0; i < array.length; i++) { 20 | countOuter++; 21 | for(var j = 1; j < array.length; j++) { 22 | countInner++; 23 | if(array[j - 1] > array[j]) { 24 | countSwap++; 25 | swap(array, j - 1, j); 26 | } 27 | } 28 | } 29 | 30 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 31 | return array; 32 | } 33 | 34 | bubbleSortBasic(arrayRandom.slice()); // => outer: 10 inner: 90 swap: 21 35 | bubbleSortBasic(arrayOrdered.slice()); // => outer: 10 inner: 90 swap: 0 36 | bubbleSortBasic(arrayReversed.slice()); // => outer: 10 inner: 90 swap: 45 37 | 38 | // correct implementation: this is the usual implementation of the bubble sort algorithm. Some loops execution are avoided if not they are not needed 39 | function bubbleSort(array) { 40 | var countOuter = 0; 41 | var countInner = 0; 42 | var countSwap = 0; 43 | 44 | var swapped; 45 | do { 46 | countOuter++; 47 | swapped = false; 48 | for(var i = 0; i < array.length; i++) { 49 | countInner++; 50 | if(array[i] && array[i + 1] && array[i] > array[i + 1]) { 51 | countSwap++; 52 | swap(array, i, i + 1); 53 | swapped = true; 54 | } 55 | } 56 | } while(swapped); 57 | 58 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 59 | return array; 60 | } 61 | 62 | bubbleSort(arrayRandom.slice()); // => outer: 9 inner: 90 swap: 21 63 | bubbleSort(arrayOrdered.slice()); // => outer: 1 inner: 10 swap: 0 64 | bubbleSort(arrayReversed.slice()); // => outer: 10 inner: 100 swap: 45 65 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/bubble-sort.es6.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | const array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | // be careful: this is a very basic implementation which is nice to understand the deep principle of bubble sort (going through all comparisons) but it can be greatly improved for performances 5 | function bubbleSortBasic(array) { 6 | for(let i = 0; i < array.length; i++) { 7 | for(let j = 1; j < array.length; j++) { 8 | if(array[j - 1] > array[j]) { 9 | [array[j - 1], array[j]] = [array[j], array[j - 1]]; 10 | } 11 | } 12 | } 13 | return array; 14 | } 15 | 16 | console.log(bubbleSortBasic(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 17 | 18 | // correct implementation: this is the usual implementation of the bubble sort algorithm. Some loops execution are avoided if not they are not needed 19 | function bubbleSort(array) { 20 | let swapped; 21 | do { 22 | swapped = false; 23 | for(let i = 0; i < array.length; i++) { 24 | if(array[i] && array[i + 1] && array[i] > array[i + 1]) { 25 | [array[i], array[i + 1]] = [array[i + 1], array[i]]; 26 | swapped = true; 27 | } 28 | } 29 | } while(swapped); 30 | return array; 31 | } 32 | 33 | console.log(bubbleSort(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 34 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/bubble-sort.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | var array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | // swap function helper 5 | function swap(array, i, j) { 6 | var temp = array[i]; 7 | array[i] = array[j]; 8 | array[j] = temp; 9 | } 10 | 11 | // be careful: this is a very basic implementation which is nice to understand the deep principle of bubble sort (going through all comparisons) but it can be greatly improved for performances 12 | function bubbleSortBasic(array) { 13 | for(var i = 0; i < array.length; i++) { 14 | for(var j = 1; j < array.length; j++) { 15 | if(array[j - 1] > array[j]) { 16 | swap(array, j - 1, j); 17 | } 18 | } 19 | } 20 | return array; 21 | } 22 | 23 | console.log(bubbleSortBasic(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 24 | 25 | // correct implementation: this is the usual implementation of the bubble sort algorithm. Some loops execution are avoided if not they are not needed 26 | function bubbleSort(array) { 27 | var swapped; 28 | do { 29 | swapped = false; 30 | for(var i = 0; i < array.length; i++) { 31 | if(array[i] && array[i + 1] && array[i] > array[i + 1]) { 32 | swap(array, i, i + 1); 33 | swapped = true; 34 | } 35 | } 36 | } while(swapped); 37 | return array; 38 | } 39 | 40 | console.log(bubbleSort(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 41 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/insertion-sort-counters.es6.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | const arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | const arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | const arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | function insertionSort(array) { 7 | let countOuter = 0; 8 | let countInner = 0; 9 | let countSwap = 0; 10 | 11 | for(let i = 0; i < array.length; i++) { 12 | countOuter++; 13 | let temp = array[i]; 14 | let j = i - 1; 15 | while (j >= 0 && array[j] > temp) { 16 | countInner++; 17 | countSwap++; 18 | array[j + 1] = array[j]; 19 | j--; 20 | } 21 | array[j + 1] = temp; 22 | } 23 | 24 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 25 | return array; 26 | } 27 | 28 | insertionSort(arrayRandom.slice()); // => outer: 10 inner: 21 swap: 21 29 | insertionSort(arrayOrdered.slice()); // => outer: 10 inner: 0 swap: 0 30 | insertionSort(arrayReversed.slice()); // => outer: 10 inner: 45 swap: 45 31 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/insertion-sort-counters.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | var arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | var arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | var arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | function insertionSort(array) { 7 | var countOuter = 0; 8 | var countInner = 0; 9 | var countSwap = 0; 10 | 11 | for(var i = 0; i < array.length; i++) { 12 | countOuter++; 13 | var temp = array[i]; 14 | var j = i - 1; 15 | while (j >= 0 && array[j] > temp) { 16 | countInner++; 17 | countSwap++; 18 | array[j + 1] = array[j]; 19 | j--; 20 | } 21 | array[j + 1] = temp; 22 | } 23 | 24 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 25 | return array; 26 | } 27 | 28 | insertionSort(arrayRandom.slice()); // => outer: 10 inner: 21 swap: 21 29 | insertionSort(arrayOrdered.slice()); // => outer: 10 inner: 0 swap: 0 30 | insertionSort(arrayReversed.slice()); // => outer: 10 inner: 45 swap: 45 31 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/insertion-sort.es6.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | const array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | function insertionSort(array) { 5 | for(let i = 0; i < array.length; i++) { 6 | let temp = array[i]; 7 | let j = i - 1; 8 | while (j >= 0 && array[j] > temp) { 9 | array[j + 1] = array[j]; 10 | j--; 11 | } 12 | array[j + 1] = temp; 13 | } 14 | return array; 15 | } 16 | 17 | console.log(insertionSort(array)); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 18 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/insertion-sort.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | var array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | function insertionSort(array) { 5 | for(var i = 0; i < array.length; i++) { 6 | var temp = array[i]; 7 | var j = i - 1; 8 | while (j >= 0 && array[j] > temp) { 9 | array[j + 1] = array[j]; 10 | j--; 11 | } 12 | array[j + 1] = temp; 13 | } 14 | return array; 15 | } 16 | 17 | console.log(insertionSort(array)); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 18 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/merge-sort-counters.es6.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | const arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | const arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | const arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | let countOuter = 0; 7 | let countInner = 0; 8 | let countSwap = 0; 9 | 10 | function resetCounters() { 11 | countOuter = 0; 12 | countInner = 0; 13 | countSwap = 0; 14 | } 15 | 16 | // top-down implementation 17 | function mergeSortTopDown(array) { 18 | countOuter++; 19 | if(array.length < 2) { 20 | return array; 21 | } 22 | 23 | const middle = Math.floor(array.length / 2); 24 | const left = array.slice(0, middle); 25 | const right = array.slice(middle); 26 | 27 | return mergeTopDown(mergeSortTopDown(left), mergeSortTopDown(right)); 28 | } 29 | 30 | function mergeTopDown(left, right) { 31 | const array = []; 32 | 33 | while(left.length && right.length) { 34 | countInner++; 35 | if(left[0] < right[0]) { 36 | array.push(left.shift()); 37 | } else { 38 | array.push(right.shift()); 39 | } 40 | } 41 | return array.concat(left.slice()).concat(right.slice()); 42 | } 43 | 44 | mergeSortTopDown(arrayRandom.slice()); // => outer: 19 inner: 24 swap: 0 45 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 46 | resetCounters(); 47 | 48 | mergeSortTopDown(arrayOrdered.slice()); // => outer: 19 inner: 15 swap: 0 49 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 50 | resetCounters(); 51 | 52 | mergeSortTopDown(arrayReversed.slice()); // => outer: 19 inner: 19 swap: 0 53 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 54 | resetCounters(); 55 | 56 | // bottom-up implementation 57 | function mergeSortBottomUp(array) { 58 | let step = 1; 59 | while (step < array.length) { 60 | countOuter++; 61 | let left = 0; 62 | while (left + step < array.length) { 63 | countInner++; 64 | mergeBottomUp(array, left, step); 65 | left += step * 2; 66 | } 67 | step *= 2; 68 | } 69 | return array; 70 | } 71 | function mergeBottomUp(array, left, step) { 72 | const right = left + step; 73 | const end = Math.min(left + step * 2 - 1, array.length - 1); 74 | let leftMoving = left; 75 | let rightMoving = right; 76 | const temp = []; 77 | 78 | for (let i = left; i <= end; i++) { 79 | if ((array[leftMoving] <= array[rightMoving] || rightMoving > end) && 80 | leftMoving < right) { 81 | temp[i] = array[leftMoving]; 82 | leftMoving++; 83 | } else { 84 | temp[i] = array[rightMoving]; 85 | rightMoving++; 86 | } 87 | } 88 | 89 | for (let j = left; j <= end; j++) { 90 | countSwap++; 91 | array[j] = temp[j]; 92 | } 93 | } 94 | 95 | mergeSortBottomUp(arrayRandom.slice()); // => outer: 4 inner: 9 swap: 36 96 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 97 | resetCounters(); 98 | 99 | mergeSortBottomUp(arrayOrdered.slice()); // => outer: 4 inner: 9 swap: 36 100 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 101 | resetCounters(); 102 | 103 | mergeSortBottomUp(arrayReversed.slice()); // => outer: 4 inner: 9 swap: 36 104 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 105 | resetCounters(); 106 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/merge-sort-counters.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | var arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | var arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | var arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | var countOuter = 0; 7 | var countInner = 0; 8 | var countSwap = 0; 9 | 10 | function resetCounters() { 11 | countOuter = 0; 12 | countInner = 0; 13 | countSwap = 0; 14 | } 15 | 16 | // top-down implementation 17 | function mergeSortTopDown(array) { 18 | countOuter++; 19 | if(array.length < 2) { 20 | return array; 21 | } 22 | 23 | var middle = Math.floor(array.length / 2); 24 | var left = array.slice(0, middle); 25 | var right = array.slice(middle); 26 | 27 | return mergeTopDown(mergeSortTopDown(left), mergeSortTopDown(right)); 28 | } 29 | 30 | function mergeTopDown(left, right) { 31 | var array = []; 32 | 33 | while(left.length && right.length) { 34 | countInner++; 35 | if(left[0] < right[0]) { 36 | array.push(left.shift()); 37 | } else { 38 | array.push(right.shift()); 39 | } 40 | } 41 | return array.concat(left.slice()).concat(right.slice()); 42 | } 43 | 44 | mergeSortTopDown(arrayRandom.slice()); // => outer: 19 inner: 24 swap: 0 45 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 46 | resetCounters(); 47 | 48 | mergeSortTopDown(arrayOrdered.slice()); // => outer: 19 inner: 15 swap: 0 49 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 50 | resetCounters(); 51 | 52 | mergeSortTopDown(arrayReversed.slice()); // => outer: 19 inner: 19 swap: 0 53 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 54 | resetCounters(); 55 | 56 | // bottom-up implementation 57 | function mergeSortBottomUp(array) { 58 | var step = 1; 59 | while (step < array.length) { 60 | countOuter++; 61 | var left = 0; 62 | while (left + step < array.length) { 63 | countInner++; 64 | mergeBottomUp(array, left, step); 65 | left += step * 2; 66 | } 67 | step *= 2; 68 | } 69 | return array; 70 | } 71 | function mergeBottomUp(array, left, step) { 72 | var right = left + step; 73 | var end = Math.min(left + step * 2 - 1, array.length - 1); 74 | var leftMoving = left; 75 | var rightMoving = right; 76 | var temp = []; 77 | 78 | for (var i = left; i <= end; i++) { 79 | if ((array[leftMoving] <= array[rightMoving] || rightMoving > end) && 80 | leftMoving < right) { 81 | temp[i] = array[leftMoving]; 82 | leftMoving++; 83 | } else { 84 | temp[i] = array[rightMoving]; 85 | rightMoving++; 86 | } 87 | } 88 | 89 | for (var j = left; j <= end; j++) { 90 | countSwap++; 91 | array[j] = temp[j]; 92 | } 93 | } 94 | 95 | mergeSortBottomUp(arrayRandom.slice()); // => outer: 4 inner: 9 swap: 36 96 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 97 | resetCounters(); 98 | 99 | mergeSortBottomUp(arrayOrdered.slice()); // => outer: 4 inner: 9 swap: 36 100 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 101 | resetCounters(); 102 | 103 | mergeSortBottomUp(arrayReversed.slice()); // => outer: 4 inner: 9 swap: 36 104 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 105 | resetCounters(); 106 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/merge-sort.es6.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | const array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | // top-down implementation 5 | function mergeSortTopDown(array) { 6 | if(array.length < 2) { 7 | return array; 8 | } 9 | 10 | const middle = Math.floor(array.length / 2); 11 | const left = array.slice(0, middle); 12 | const right = array.slice(middle); 13 | 14 | return mergeTopDown(mergeSortTopDown(left), mergeSortTopDown(right)); 15 | } 16 | function mergeTopDown(left, right) { 17 | const array = []; 18 | 19 | while(left.length && right.length) { 20 | if(left[0] < right[0]) { 21 | array.push(left.shift()); 22 | } else { 23 | array.push(right.shift()); 24 | } 25 | } 26 | return array.concat(left.slice()).concat(right.slice()); 27 | } 28 | 29 | console.log(mergeSortTopDown(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 30 | 31 | // bottom-up implementation 32 | function mergeSortBottomUp(array) { 33 | let step = 1; 34 | while (step < array.length) { 35 | let left = 0; 36 | while (left + step < array.length) { 37 | mergeBottomUp(array, left, step); 38 | left += step * 2; 39 | } 40 | step *= 2; 41 | } 42 | return array; 43 | } 44 | function mergeBottomUp(array, left, step) { 45 | const right = left + step; 46 | const end = Math.min(left + step * 2 - 1, array.length - 1); 47 | let leftMoving = left; 48 | let rightMoving = right; 49 | const temp = []; 50 | 51 | for (let i = left; i <= end; i++) { 52 | if ((array[leftMoving] <= array[rightMoving] || rightMoving > end) && 53 | leftMoving < right) { 54 | temp[i] = array[leftMoving]; 55 | leftMoving++; 56 | } else { 57 | temp[i] = array[rightMoving]; 58 | rightMoving++; 59 | } 60 | } 61 | 62 | for (let j = left; j <= end; j++) { 63 | array[j] = temp[j]; 64 | } 65 | } 66 | 67 | console.log(mergeSortBottomUp(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 68 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/merge-sort.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | var array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | // top-down implementation 5 | function mergeSortTopDown(array) { 6 | if(array.length < 2) { 7 | return array; 8 | } 9 | 10 | var middle = Math.floor(array.length / 2); 11 | var left = array.slice(0, middle); 12 | var right = array.slice(middle); 13 | 14 | return mergeTopDown(mergeSortTopDown(left), mergeSortTopDown(right)); 15 | } 16 | function mergeTopDown(left, right) { 17 | var array = []; 18 | 19 | while(left.length && right.length) { 20 | if(left[0] < right[0]) { 21 | array.push(left.shift()); 22 | } else { 23 | array.push(right.shift()); 24 | } 25 | } 26 | return array.concat(left.slice()).concat(right.slice()); 27 | } 28 | 29 | console.log(mergeSortTopDown(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 30 | 31 | // bottom-up implementation 32 | function mergeSortBottomUp(array) { 33 | var step = 1; 34 | while (step < array.length) { 35 | var left = 0; 36 | while (left + step < array.length) { 37 | mergeBottomUp(array, left, step); 38 | left += step * 2; 39 | } 40 | step *= 2; 41 | } 42 | return array; 43 | } 44 | function mergeBottomUp(array, left, step) { 45 | var right = left + step; 46 | var end = Math.min(left + step * 2 - 1, array.length - 1); 47 | var leftMoving = left; 48 | var rightMoving = right; 49 | var temp = []; 50 | 51 | for (var i = left; i <= end; i++) { 52 | if ((array[leftMoving] <= array[rightMoving] || rightMoving > end) && 53 | leftMoving < right) { 54 | temp[i] = array[leftMoving]; 55 | leftMoving++; 56 | } else { 57 | temp[i] = array[rightMoving]; 58 | rightMoving++; 59 | } 60 | } 61 | 62 | for (var j = left; j <= end; j++) { 63 | array[j] = temp[j]; 64 | } 65 | } 66 | 67 | console.log(mergeSortBottomUp(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 68 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/quicksort-counters.es6.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | const arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | const arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | const arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | let countOuter = 0; 7 | let countInner = 0; 8 | let countSwap = 0; 9 | 10 | function resetCounters() { 11 | countOuter = 0; 12 | countInner = 0; 13 | countSwap = 0; 14 | } 15 | 16 | // basic implementation (pivot is the first element of the array) 17 | function quicksortBasic(array) { 18 | countOuter++; 19 | if(array.length < 2) { 20 | return array; 21 | } 22 | 23 | const pivot = array[0]; 24 | const lesser = []; 25 | const greater = []; 26 | 27 | for(let i = 1; i < array.length; i++) { 28 | countInner++; 29 | if(array[i] < pivot) { 30 | lesser.push(array[i]); 31 | } else { 32 | greater.push(array[i]); 33 | } 34 | } 35 | 36 | return quicksortBasic(lesser).concat(pivot, quicksortBasic(greater)); 37 | } 38 | 39 | quicksortBasic(arrayRandom.slice()); // => outer: 13 inner: 25 swap: 0 40 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 41 | resetCounters(); 42 | 43 | quicksortBasic(arrayOrdered.slice()); // => outer: 19 inner: 45 swap: 0 44 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 45 | resetCounters(); 46 | 47 | quicksortBasic(arrayReversed.slice()); // => outer: 19 inner: 45 swap: 0 48 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 49 | resetCounters(); 50 | 51 | // classic implementation (with Hoare or Lomuto partition scheme, you can comment either one method or the other to see the difference) 52 | function quicksort(array, left, right) { 53 | countOuter++; 54 | left = left || 0; 55 | right = right || array.length - 1; 56 | 57 | // const pivot = partitionLomuto(array, left, right); // you can play with both partition 58 | const pivot = partitionHoare(array, left, right); // you can play with both partition 59 | 60 | if(left < pivot - 1) { 61 | quicksort(array, left, pivot - 1); 62 | } 63 | if(right > pivot) { 64 | quicksort(array, pivot, right); 65 | } 66 | return array; 67 | } 68 | // Lomuto partition scheme, it is less efficient than the Hoare partition scheme 69 | function partitionLomuto(array, left, right) { 70 | const pivot = right; 71 | let i = left; 72 | let last = left; 73 | 74 | for(var j = left; j < right; j++) { 75 | countInner++; 76 | if(array[j] <= array[pivot]) { 77 | countSwap++; 78 | [array[i], array[j]] = [array[j], array[i]]; 79 | i = i + 1; 80 | } 81 | last = j + 1; 82 | } 83 | countSwap++; 84 | [array[i], array[last]] = [array[last], array[i]]; 85 | return i; 86 | } 87 | // Hoare partition scheme, it is more efficient than the Lomuto partition scheme because it does three times fewer swaps on average 88 | function partitionHoare(array, left, right) { 89 | const pivot = Math.floor((left + right) / 2 ); 90 | 91 | while(left <= right) { 92 | countInner++; 93 | while(array[left] < array[pivot]) { 94 | left++; 95 | } 96 | while(array[right] > array[pivot]) { 97 | right--; 98 | } 99 | if(left <= right) { 100 | countSwap++; 101 | [array[left], array[right]] = [array[right], array[left]]; 102 | left++; 103 | right--; 104 | } 105 | } 106 | return left; 107 | } 108 | 109 | quicksort(arrayRandom.slice()); 110 | // => Hoare: outer: 9 inner: 12 swap: 12 - Lomuto: outer: 10 inner: 35 swap: 28 111 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 112 | resetCounters(); 113 | 114 | quicksort(arrayOrdered.slice()); 115 | // => Hoare: outer: 9 inner: 9 swap: 9 - Lomuto: outer: 9 inner: 45 swap: 54 116 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 117 | resetCounters(); 118 | 119 | quicksort(arrayReversed.slice()); 120 | // => Hoare: outer: 9 inner: 13 swap: 13 - Lomuto: outer: 10 inner: 54 swap: 39 121 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 122 | resetCounters(); 123 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/quicksort-counters.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | var arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | var arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | var arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | var countOuter = 0; 7 | var countInner = 0; 8 | var countSwap = 0; 9 | 10 | function resetCounters() { 11 | countOuter = 0; 12 | countInner = 0; 13 | countSwap = 0; 14 | } 15 | 16 | // basic implementation (pivot is the first element of the array) 17 | function quicksortBasic(array) { 18 | countOuter++; 19 | if(array.length < 2) { 20 | return array; 21 | } 22 | 23 | var pivot = array[0]; 24 | var lesser = []; 25 | var greater = []; 26 | 27 | for(var i = 1; i < array.length; i++) { 28 | countInner++; 29 | if(array[i] < pivot) { 30 | lesser.push(array[i]); 31 | } else { 32 | greater.push(array[i]); 33 | } 34 | } 35 | 36 | return quicksortBasic(lesser).concat(pivot, quicksortBasic(greater)); 37 | } 38 | 39 | quicksortBasic(arrayRandom.slice()); // => outer: 13 inner: 25 swap: 0 40 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 41 | resetCounters(); 42 | 43 | quicksortBasic(arrayOrdered.slice()); // => outer: 19 inner: 45 swap: 0 44 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 45 | resetCounters(); 46 | 47 | quicksortBasic(arrayReversed.slice()); // => outer: 19 inner: 45 swap: 0 48 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 49 | resetCounters(); 50 | 51 | // swap function helper 52 | function swap(array, i, j) { 53 | var temp = array[i]; 54 | array[i] = array[j]; 55 | array[j] = temp; 56 | } 57 | 58 | // classic implementation (with Hoare or Lomuto partition scheme, you can comment either one method or the other to see the difference) 59 | function quicksort(array, left, right) { 60 | countOuter++; 61 | left = left || 0; 62 | right = right || array.length - 1; 63 | 64 | // var pivot = partitionLomuto(array, left, right); // you can play with both partition 65 | var pivot = partitionHoare(array, left, right); // you can play with both partition 66 | 67 | if(left < pivot - 1) { 68 | quicksort(array, left, pivot - 1); 69 | } 70 | if(right > pivot) { 71 | quicksort(array, pivot, right); 72 | } 73 | return array; 74 | } 75 | // Lomuto partition scheme, it is less efficient than the Hoare partition scheme 76 | function partitionLomuto(array, left, right) { 77 | var pivot = right; 78 | var i = left; 79 | 80 | for(var j = left; j < right; j++) { 81 | countInner++; 82 | if(array[j] <= array[pivot]) { 83 | countSwap++; 84 | swap(array, i, j); 85 | i = i + 1; 86 | } 87 | } 88 | countSwap++; 89 | swap(array, i, j); 90 | return i; 91 | } 92 | // Hoare partition scheme, it is more efficient than the Lomuto partition scheme because it does three times fewer swaps on average 93 | function partitionHoare(array, left, right) { 94 | var pivot = Math.floor((left + right) / 2 ); 95 | 96 | while(left <= right) { 97 | countInner++; 98 | while(array[left] < array[pivot]) { 99 | left++; 100 | } 101 | while(array[right] > array[pivot]) { 102 | right--; 103 | } 104 | if(left <= right) { 105 | countSwap++; 106 | swap(array, left, right); 107 | left++; 108 | right--; 109 | } 110 | } 111 | return left; 112 | } 113 | 114 | quicksort(arrayRandom.slice()); 115 | // => Hoare: outer: 9 inner: 12 swap: 12 - Lomuto: outer: 10 inner: 35 swap: 28 116 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 117 | resetCounters(); 118 | 119 | quicksort(arrayOrdered.slice()); 120 | // => Hoare: outer: 9 inner: 9 swap: 9 - Lomuto: outer: 9 inner: 45 swap: 54 121 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 122 | resetCounters(); 123 | 124 | quicksort(arrayReversed.slice()); 125 | // => Hoare: outer: 9 inner: 13 swap: 13 - Lomuto: outer: 10 inner: 54 swap: 39 126 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 127 | resetCounters(); 128 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/quicksort.es6.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | const array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | // basic implementation (pivot is the first element of the array) 5 | function quicksortBasic(array) { 6 | if(array.length < 2) { 7 | return array; 8 | } 9 | 10 | const pivot = array[0]; 11 | const lesser = []; 12 | const greater = []; 13 | 14 | for(let i = 1; i < array.length; i++) { 15 | if(array[i] < pivot) { 16 | lesser.push(array[i]); 17 | } else { 18 | greater.push(array[i]); 19 | } 20 | } 21 | 22 | return quicksortBasic(lesser).concat(pivot, quicksortBasic(greater)); 23 | } 24 | 25 | console.log(quicksortBasic(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 26 | 27 | // classic implementation (with Hoare or Lomuto partition scheme, you can comment either one method or the other to see the difference) 28 | function quicksort(array, left, right) { 29 | left = left || 0; 30 | right = right || array.length - 1; 31 | 32 | // const pivot = partitionLomuto(array, left, right); // you can play with both partition 33 | const pivot = partitionHoare(array, left, right); // you can play with both partition 34 | 35 | if(left < pivot - 1) { 36 | quicksort(array, left, pivot - 1); 37 | } 38 | if(right > pivot) { 39 | quicksort(array, pivot, right); 40 | } 41 | return array; 42 | } 43 | // Lomuto partition scheme, it is less efficient than the Hoare partition scheme 44 | function partitionLomuto(array, left, right) { 45 | const pivot = right; 46 | let i = left; 47 | let last = left; 48 | 49 | for(let j = left; j < right; j++) { 50 | if(array[j] <= array[pivot]) { 51 | [array[i], array[j]] = [array[j], array[i]]; 52 | i = i + 1; 53 | } 54 | last = j + 1; 55 | } 56 | [array[i], array[last]] = [array[last], array[i]]; 57 | return i; 58 | } 59 | // Hoare partition scheme, it is more efficient than the Lomuto partition scheme because it does three times fewer swaps on average 60 | function partitionHoare(array, left, right) { 61 | const pivot = Math.floor((left + right) / 2 ); 62 | 63 | while(left <= right) { 64 | while(array[left] < array[pivot]) { 65 | left++; 66 | } 67 | while(array[right] > array[pivot]) { 68 | right--; 69 | } 70 | if(left <= right) { 71 | [array[left], array[right]] = [array[right], array[left]]; 72 | left++; 73 | right--; 74 | } 75 | } 76 | return left; 77 | } 78 | 79 | console.log(quicksort(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 80 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/quicksort.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | var array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | // basic implementation (pivot is the first element of the array) 5 | function quicksortBasic(array) { 6 | if(array.length < 2) { 7 | return array; 8 | } 9 | 10 | var pivot = array[0]; 11 | var lesser = []; 12 | var greater = []; 13 | 14 | for(var i = 1; i < array.length; i++) { 15 | if(array[i] < pivot) { 16 | lesser.push(array[i]); 17 | } else { 18 | greater.push(array[i]); 19 | } 20 | } 21 | 22 | return quicksortBasic(lesser).concat(pivot, quicksortBasic(greater)); 23 | } 24 | 25 | console.log(quicksortBasic(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 26 | 27 | // swap function helper 28 | function swap(array, i, j) { 29 | var temp = array[i]; 30 | array[i] = array[j]; 31 | array[j] = temp; 32 | } 33 | 34 | // classic implementation (with Hoare or Lomuto partition scheme, you can comment either one method or the other to see the difference) 35 | function quicksort(array, left, right) { 36 | left = left || 0; 37 | right = right || array.length - 1; 38 | 39 | // var pivot = partitionLomuto(array, left, right); // you can play with both partition 40 | var pivot = partitionHoare(array, left, right); // you can play with both partition 41 | 42 | if(left < pivot - 1) { 43 | quicksort(array, left, pivot - 1); 44 | } 45 | if(right > pivot) { 46 | quicksort(array, pivot, right); 47 | } 48 | return array; 49 | } 50 | // Lomuto partition scheme, it is less efficient than the Hoare partition scheme 51 | function partitionLomuto(array, left, right) { 52 | var pivot = right; 53 | var i = left; 54 | 55 | for(var j = left; j < right; j++) { 56 | if(array[j] <= array[pivot]) { 57 | swap(array, i, j); 58 | i = i + 1; 59 | } 60 | } 61 | swap(array, i, j); 62 | return i; 63 | } 64 | // Hoare partition scheme, it is more efficient than the Lomuto partition scheme because it does three times fewer swaps on average 65 | function partitionHoare(array, left, right) { 66 | var pivot = Math.floor((left + right) / 2 ); 67 | 68 | while(left <= right) { 69 | while(array[left] < array[pivot]) { 70 | left++; 71 | } 72 | while(array[right] > array[pivot]) { 73 | right--; 74 | } 75 | if(left <= right) { 76 | swap(array, left, right); 77 | left++; 78 | right--; 79 | } 80 | } 81 | return left; 82 | } 83 | 84 | console.log(quicksort(array.slice())); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 85 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/selection-sort-counters.es6.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | const arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | const arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | const arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | function selectionSort(array) { 7 | let countOuter = 0; 8 | let countInner = 0; 9 | let countSwap = 0; 10 | 11 | for(let i = 0; i < array.length; i++) { 12 | countOuter++; 13 | let min = i; 14 | for(let j = i + 1; j < array.length; j++) { 15 | countInner++; 16 | if(array[j] < array[min]) { 17 | min = j; 18 | } 19 | } 20 | if(i !== min) { 21 | countSwap++; 22 | [array[i], array[min]] = [array[min], array[i]]; 23 | } 24 | } 25 | 26 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 27 | return array; 28 | } 29 | 30 | selectionSort(arrayRandom.slice()); // => outer: 10 inner: 45 swap: 5 31 | selectionSort(arrayOrdered.slice()); // => outer: 10 inner: 45 swap: 0 32 | selectionSort(arrayReversed.slice()); // => outer: 10 inner: 45 swap: 5 33 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/selection-sort-counters.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | var arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | var arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | var arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | // swap function helper 7 | function swap(array, i, j) { 8 | var temp = array[i]; 9 | array[i] = array[j]; 10 | array[j] = temp; 11 | } 12 | 13 | function selectionSort(array) { 14 | var countOuter = 0; 15 | var countInner = 0; 16 | var countSwap = 0; 17 | 18 | for(var i = 0; i < array.length; i++) { 19 | countOuter++; 20 | var min = i; 21 | for(var j = i + 1; j < array.length; j++) { 22 | countInner++; 23 | if(array[j] < array[min]) { 24 | min = j; 25 | } 26 | } 27 | if(i !== min) { 28 | countSwap++; 29 | swap(array, i, min); 30 | } 31 | } 32 | 33 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 34 | return array; 35 | } 36 | 37 | selectionSort(arrayRandom.slice()); // => outer: 10 inner: 45 swap: 5 38 | selectionSort(arrayOrdered.slice()); // => outer: 10 inner: 45 swap: 0 39 | selectionSort(arrayReversed.slice()); // => outer: 10 inner: 45 swap: 5 40 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/selection-sort.es6.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | const array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | function selectionSort(array) { 5 | for(let i = 0; i < array.length; i++) { 6 | let min = i; 7 | for(let j = i + 1; j < array.length; j++) { 8 | if(array[j] < array[min]) { 9 | min = j; 10 | } 11 | } 12 | if(i !== min) { 13 | [array[i], array[min]] = [array[min], array[i]]; 14 | } 15 | } 16 | return array; 17 | } 18 | 19 | console.log(selectionSort(array)); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 20 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/selection-sort.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | var array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | // swap function helper 5 | function swap(array, i, j) { 6 | var temp = array[i]; 7 | array[i] = array[j]; 8 | array[j] = temp; 9 | } 10 | 11 | function selectionSort(array) { 12 | for(var i = 0; i < array.length; i++) { 13 | var min = i; 14 | for(var j = i + 1; j < array.length; j++) { 15 | if(array[j] < array[min]) { 16 | min = j; 17 | } 18 | } 19 | if(i !== min) { 20 | swap(array, i, min); 21 | } 22 | } 23 | return array; 24 | } 25 | 26 | console.log(selectionSort(array)); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 27 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/shellsort-counters.es6.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | const arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | const arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | const arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | // gaps 7 | const gaps = [701, 301, 132, 57, 23, 10, 4, 1]; 8 | 9 | function shellsort(array) { 10 | let countOuter = 0; 11 | let countInner = 0; 12 | let countSwap = 0; 13 | 14 | for(let g = 0; g < gaps.length; g++) { 15 | const gap = gaps[g]; 16 | for(let i = gap; i < array.length; i++) { 17 | countOuter++; 18 | const temp = array[i]; 19 | let last = i; 20 | for(let j = i; j >= gap && array[j - gap] > temp; j -= gap) { 21 | countInner++; 22 | countSwap++; 23 | array[j] = array[j - gap]; 24 | last -= gap; 25 | } 26 | array[last] = temp; 27 | } 28 | } 29 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 30 | return array; 31 | } 32 | 33 | shellsort(arrayRandom.slice()); // => outer: 15 inner: 11 swap: 11 34 | shellsort(arrayOrdered.slice()); // => outer: 15 inner: 0 swap: 0 35 | shellsort(arrayReversed.slice()); // => outer: 15 inner: 13 swap: 13 36 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/shellsort-counters.js: -------------------------------------------------------------------------------- 1 | // sample of arrays to sort 2 | var arrayRandom = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | var arrayOrdered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 4 | var arrayReversed = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; 5 | 6 | // gaps 7 | var gaps = [701, 301, 132, 57, 23, 10, 4, 1]; 8 | 9 | function shellsort(array) { 10 | var countOuter = 0; 11 | var countInner = 0; 12 | var countSwap = 0; 13 | 14 | for(var g = 0; g < gaps.length; g++) { 15 | var gap = gaps[g]; 16 | for(var i = gap; i < array.length; i++) { 17 | countOuter++; 18 | var temp = array[i]; 19 | for(var j = i; j >= gap && array[j - gap] > temp; j -= gap) { 20 | countInner++; 21 | countSwap++; 22 | array[j] = array[j - gap]; 23 | } 24 | array[j] = temp; 25 | } 26 | } 27 | console.log('outer:', countOuter, 'inner:', countInner, 'swap:', countSwap); 28 | return array; 29 | } 30 | 31 | shellsort(arrayRandom.slice()); // => outer: 15 inner: 11 swap: 11 32 | shellsort(arrayOrdered.slice()); // => outer: 15 inner: 0 swap: 0 33 | shellsort(arrayReversed.slice()); // => outer: 15 inner: 13 swap: 13 34 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/shellsort.es6.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | const array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | // gaps 5 | const gaps = [701, 301, 132, 57, 23, 10, 4, 1]; 6 | 7 | function shellsort(array) { 8 | for(let g = 0; g < gaps.length; g++) { 9 | const gap = gaps[g]; 10 | for(let i = gap; i < array.length; i++) { 11 | const temp = array[i]; 12 | let last = i; 13 | for(let j = i; j >= gap && array[j - gap] > temp; j -= gap) { 14 | array[j] = array[j - gap]; 15 | last -= gap; 16 | } 17 | array[last] = temp; 18 | } 19 | } 20 | return array; 21 | } 22 | 23 | console.log(shellsort(array)); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 24 | -------------------------------------------------------------------------------- /sorting-algorithms-in-javascript/shellsort.js: -------------------------------------------------------------------------------- 1 | // array to sort 2 | var array = [9, 2, 5, 6, 4, 3, 7, 10, 1, 8]; 3 | 4 | // gaps 5 | var gaps = [701, 301, 132, 57, 23, 10, 4, 1]; 6 | 7 | function shellsort(array) { 8 | for(var g = 0; g < gaps.length; g++) { 9 | var gap = gaps[g]; 10 | for(var i = gap; i < array.length; i++) { 11 | var temp = array[i]; 12 | for(var j = i; j >= gap && array[j - gap] > temp; j -= gap) { 13 | array[j] = array[j - gap]; 14 | } 15 | array[j] = temp; 16 | } 17 | } 18 | return array; 19 | } 20 | 21 | console.log(shellsort(array)); // => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] 22 | --------------------------------------------------------------------------------