├── .gitignore ├── Base ├── LinkedList │ ├── CircularLinkedList.js │ ├── CircularLinkedList2.js │ ├── DoublyLinkedList.js │ ├── DoublyLinkedList2.js │ ├── InsertionSort.js │ ├── LinkedList.js │ ├── LinkedList2.js │ ├── LinkedListWithCycle.js │ ├── MergeSort.js │ └── QuickSort.js ├── Map │ ├── Dictionaries.js │ ├── HashCollisionLinearProbing.js │ ├── HashCollisionSeparateChaining.js │ ├── HashTable.js │ ├── UsingHashCollisionLinearProbing.js │ └── UsingHashCollisionSeparateChaining.js ├── Queue │ ├── Queue.js │ └── Queue2.js ├── Set │ ├── Set.js │ ├── Set2.js │ └── UsingES6Set.js └── Stack │ ├── Stack.js │ ├── Stack1.js │ ├── Stack1_ES6.js │ ├── Stack2.js │ └── Stack3.js ├── Graph ├── Graph.js ├── MinimumSpanningTree.js ├── ShortestPath.js ├── UsingGraphs.js ├── UsingMinimumSpanningTree.js └── UsingShortestPathAlgorithms.js ├── README.md ├── Sort ├── SortingAlgorithm.js └── UsingSortingAlgorithms.js ├── String ├── KMP.js └── README.md ├── TopK ├── Heap.js ├── PriorityQueue.js ├── Quick_Select.js └── README.md └── Tree ├── AVLTree.js ├── BinarySearchTree.js ├── OrderTraverse.js ├── RedBlackTree.js ├── UsingAVLTree.js ├── UsingBinarySearchTree.js └── UsingRedBlackTree.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | *.log 3 | node_modules 4 | coverage 5 | # Using*.js -------------------------------------------------------------------------------- /Base/LinkedList/CircularLinkedList.js: -------------------------------------------------------------------------------- 1 | function CircularLinkedList() { 2 | 3 | let Node = function(element) { 4 | 5 | this.element = element; 6 | this.next = null; 7 | }; 8 | 9 | let length = 0; 10 | let head = null; 11 | 12 | this.append = function(element) { 13 | 14 | let node = new Node(element), 15 | current; 16 | 17 | if (head === null) { // first node on list 18 | head = node; 19 | } else { 20 | 21 | current = head; 22 | 23 | // loop the list until find last item 24 | while (current.next !== head) { // last element will be head instead of NULL 25 | current = current.next; 26 | } 27 | 28 | // get last item and assign next to added item to make the link 29 | current.next = node; 30 | } 31 | 32 | // set node.next to head - to have circular list 33 | node.next = head; 34 | 35 | length ++; // update size of list 36 | }; 37 | 38 | this.insert = function(position, element) { 39 | 40 | // check for out-of-bounds values 41 | if (position >= 0 && position <= length) { 42 | 43 | let node = new Node(element), 44 | current = head, 45 | previous, 46 | index = 0; 47 | 48 | if (position === 0) { //add on first position 49 | 50 | if (!head) { // if no node in list 51 | head = node; 52 | node.next = head; 53 | } else { 54 | node.next = current; 55 | 56 | //update last element 57 | while (current.next !== head) { //last element will be head instead of NULL 58 | current = current.next; 59 | } 60 | 61 | head = node; 62 | current.next = head; 63 | } 64 | 65 | 66 | } else { 67 | while (index ++ < position) { 68 | previous = current; 69 | current = current.next; 70 | } 71 | node.next = current; 72 | previous.next = node; 73 | } 74 | 75 | length ++; // update size of list 76 | 77 | return true; 78 | 79 | } else { 80 | return false; 81 | } 82 | }; 83 | 84 | this.removeAt = function(position) { 85 | 86 | // check for out-of-bounds values 87 | if (position > -1 && position < length) { 88 | 89 | let current = head, 90 | previous, 91 | index = 0; 92 | 93 | // removing first item 94 | if (position === 0) { 95 | 96 | while (current.next !== head) { // needs to update last element first 97 | current = current.next; 98 | } 99 | 100 | head = head.next; 101 | current.next = head; 102 | 103 | } else { // no need to update last element for circular list 104 | 105 | while (index++ < position) { 106 | 107 | previous = current; 108 | current = current.next; 109 | } 110 | 111 | // link previous with current's next - skip it to remove 112 | previous.next = current.next; 113 | } 114 | 115 | length --; 116 | 117 | return current.element; 118 | 119 | } else { 120 | return null; 121 | } 122 | }; 123 | 124 | this.remove = function(element) { 125 | 126 | let index = this.indexOf(element); 127 | return this.removeAt(index); 128 | }; 129 | 130 | this.indexOf = function(element) { 131 | 132 | let current = head, 133 | index = -1; 134 | 135 | // check first item 136 | if (element == current.element) { 137 | return 0; 138 | } 139 | 140 | index ++; 141 | 142 | // check in the middle of the list 143 | while (current.next !== head) { 144 | 145 | if (element == current.element) { 146 | return index; 147 | } 148 | 149 | current = current.next; 150 | index ++; 151 | } 152 | 153 | // check last item 154 | if (element == current.element) { 155 | return index; 156 | } 157 | 158 | return -1; 159 | }; 160 | 161 | this.isEmpty = function() { 162 | return length === 0; 163 | }; 164 | 165 | this.size = function() { 166 | return length; 167 | }; 168 | 169 | this.getHead = function() { 170 | return head; 171 | }; 172 | 173 | this.toString = function() { 174 | 175 | let current = head, 176 | s = current.element; 177 | 178 | while (current.next !== head) { 179 | current = current.next; 180 | s += ', ' + current.element; 181 | } 182 | 183 | return s.toString(); 184 | }; 185 | 186 | this.print = function() { 187 | console.log(this.toString()); 188 | }; 189 | } 190 | 191 | -------------------------------------------------------------------------------- /Base/LinkedList/CircularLinkedList2.js: -------------------------------------------------------------------------------- 1 | let CircularLinkedList2 = (function () { 2 | 3 | class Node { 4 | constructor(element) { 5 | this.element = element; 6 | this.next = null; 7 | } 8 | } 9 | 10 | const length = new WeakMap(); 11 | const head = new WeakMap(); 12 | 13 | class CircularLinkedList2 { 14 | 15 | constructor () { 16 | length.set(this, 0); 17 | head.set(this, null); 18 | } 19 | 20 | append(element) { 21 | 22 | let node = new Node(element), 23 | current; 24 | 25 | if (this.getHead() === null) { // first node on list 26 | head.set(this, node); 27 | } else { 28 | 29 | current = this.getHead(); 30 | 31 | // loop the list until find last item 32 | while (current.next !== this.getHead()) { // last element will be head instead of NULL 33 | current = current.next; 34 | } 35 | 36 | // get last item and assign next to added item to make the link 37 | current.next = node; 38 | } 39 | 40 | // set node.next to head - to have circular list 41 | node.next = this.getHead(); 42 | 43 | // update size of list 44 | let l = this.size(); 45 | l ++; 46 | length.set(this, l); 47 | } 48 | 49 | insert(position, element) { 50 | 51 | // check for out-of-bounds values 52 | if (position >= 0 && position <= this.size()) { 53 | 54 | let node = new Node(element), 55 | current = this.getHead(), 56 | previous, 57 | index = 0; 58 | 59 | if (position === 0) { // add on first position 60 | 61 | if(!this.getHead()) { // if no node in list 62 | head.set(this, node); 63 | node.next = this.getHead(); 64 | } else { 65 | node.next = current; 66 | // update last element 67 | while(current.next !== this.getHead()) { // last element will be head instead of NULL 68 | current = current.next; 69 | } 70 | head.set(this, node); 71 | current.next = this.getHead(); 72 | } 73 | 74 | } else { 75 | while (index ++ < position) { 76 | previous = current; 77 | current = current.next; 78 | } 79 | node.next = current; 80 | previous.next = node; 81 | } 82 | 83 | // update size of list 84 | let l = this.size(); 85 | l ++; 86 | length.set(this, l); 87 | 88 | return true; 89 | 90 | } else { 91 | return false; 92 | } 93 | } 94 | 95 | removeAt(position) { 96 | 97 | // check for out-of-bounds values 98 | if (position > -1 && position < this.size()) { 99 | 100 | let current = this.getHead(), 101 | previous, 102 | index = 0; 103 | 104 | // removing first item 105 | if (position === 0) { 106 | 107 | while (current.next !== this.getHead()) { //needs to update last element first 108 | current = current.next; 109 | } 110 | 111 | head.set(this, this.getHead().next); 112 | current.next = this.getHead(); 113 | 114 | } else { // no need to update last element for circular list 115 | 116 | while (index++ < position) { 117 | 118 | previous = current; 119 | current = current.next; 120 | } 121 | 122 | // link previous with current's next - skip it to remove 123 | previous.next = current.next; 124 | } 125 | 126 | let l = this.size(); 127 | l --; 128 | length.set(this, l); 129 | 130 | return current.element; 131 | 132 | } else { 133 | return null; 134 | } 135 | } 136 | 137 | remove(element) { 138 | 139 | let index = this.indexOf(element); 140 | return this.removeAt(index); 141 | } 142 | 143 | indexOf(element) { 144 | 145 | let current = this.getHead(), 146 | index = -1; 147 | 148 | // check first item 149 | if (element == current.element) { 150 | return 0; 151 | } 152 | 153 | index ++; 154 | 155 | // check in the middle of the list 156 | while (current.next !== this.getHead()) { 157 | 158 | if (element == current.element) { 159 | return index; 160 | } 161 | 162 | current = current.next; 163 | index++; 164 | } 165 | 166 | // check last item 167 | if (element == current.element) { 168 | return index; 169 | } 170 | 171 | return -1; 172 | } 173 | 174 | isEmpty() { 175 | return this.size() === 0; 176 | } 177 | 178 | size() { 179 | return length.get(this); 180 | } 181 | 182 | getHead() { 183 | return head.get(this); 184 | } 185 | 186 | toString() { 187 | 188 | let current = this.getHead(), 189 | s = current.element; 190 | 191 | while (current.next !== this.getHead()) { 192 | current = current.next; 193 | s += ', ' + current.element; 194 | } 195 | 196 | return s.toString(); 197 | } 198 | 199 | print() { 200 | console.log(this.toString()); 201 | } 202 | } 203 | 204 | return CircularLinkedList2; 205 | })(); 206 | -------------------------------------------------------------------------------- /Base/LinkedList/DoublyLinkedList.js: -------------------------------------------------------------------------------- 1 | function DoublyLinkedList() { 2 | 3 | let Node = function(element) { 4 | 5 | this.element = element; 6 | this.next = null; 7 | this.prev = null; // NEW 8 | }; 9 | 10 | let length = 0; 11 | let head = null; 12 | let tail = null; // NEW 13 | 14 | this.append = function(element) { 15 | 16 | let node = new Node(element), 17 | current; 18 | 19 | if (head === null) { // first node on list 20 | head = node; 21 | tail = node; // NEW 22 | } else { 23 | 24 | // attach to the tail node // NEW 25 | tail.next = node; 26 | node.prev = tail; 27 | tail = node; 28 | } 29 | 30 | length++; // update size of list 31 | }; 32 | 33 | this.insert = function(position, element) { 34 | 35 | // check for out-of-bounds values 36 | if (position >= 0 && position <= length) { 37 | 38 | let node = new Node(element), 39 | current = head, 40 | previous, 41 | index = 0; 42 | 43 | if (position === 0){ // add on first position 44 | 45 | if (!head) { // NEW 46 | head = node; 47 | tail = node; 48 | } else { 49 | node.next = current; 50 | current.prev = node; // NEW 51 | head = node; 52 | } 53 | 54 | } else if (position === length) { // last item // NEW 55 | 56 | current = tail; 57 | current.next = node; 58 | node.prev = current; 59 | tail = node; 60 | 61 | } else { 62 | while (index++ < position) { 63 | previous = current; 64 | current = current.next; 65 | } 66 | node.next = current; 67 | previous.next = node; 68 | 69 | current.prev = node; // NEW 70 | node.prev = previous; // NEW 71 | } 72 | 73 | length ++; // update size of list 74 | 75 | return true; 76 | 77 | } else { 78 | return false; 79 | } 80 | }; 81 | 82 | this.removeAt = function(position) { 83 | 84 | // check for out-of-bounds values 85 | if (position > -1 && position < length) { 86 | 87 | let current = head, 88 | previous, 89 | index = 0; 90 | 91 | // removing first item 92 | if (position === 0) { 93 | 94 | head = current.next; 95 | 96 | // if there is only one item, then we update tail as well // NEW 97 | if (length === 1) { 98 | tail = null; 99 | } else { 100 | head.prev = null; 101 | } 102 | 103 | } else if (position === length - 1) { // last item // NEW 104 | 105 | current = tail; 106 | tail = current.prev; 107 | tail.next = null; 108 | 109 | } else { 110 | 111 | while (index++ < position) { 112 | 113 | previous = current; 114 | current = current.next; 115 | } 116 | 117 | // link previous with current's next - skip it to remove 118 | previous.next = current.next; 119 | current.next.prev = previous; // NEW 120 | } 121 | 122 | length --; 123 | 124 | return current.element; 125 | 126 | } else { 127 | return null; 128 | } 129 | }; 130 | 131 | this.remove = function(element) { 132 | 133 | let index = this.indexOf(element); 134 | return this.removeAt(index); 135 | }; 136 | 137 | this.indexOf = function(element) { 138 | 139 | let current = head, 140 | index = -1; 141 | 142 | // check first item 143 | if (element == current.element) { 144 | return 0; 145 | } 146 | 147 | index ++; 148 | 149 | // check in the middle of the list 150 | while(current.next) { 151 | 152 | if (element == current.element) { 153 | return index; 154 | } 155 | 156 | current = current.next; 157 | index ++; 158 | } 159 | 160 | // check last item 161 | if (element == current.element) { 162 | return index; 163 | } 164 | 165 | return -1; 166 | }; 167 | 168 | this.isEmpty = function() { 169 | return length === 0; 170 | }; 171 | 172 | this.size = function() { 173 | return length; 174 | }; 175 | 176 | this.toString = function() { 177 | 178 | let current = head, 179 | s = current ? current.element : ''; 180 | 181 | while(current && current.next) { 182 | current = current.next; 183 | s += ', ' + current.element; 184 | } 185 | 186 | return s; 187 | }; 188 | 189 | this.inverseToString = function() { 190 | 191 | let current = tail, 192 | s = current ? current.element : ''; 193 | 194 | while(current && current.prev) { 195 | current = current.prev; 196 | s += ', ' + current.element; 197 | } 198 | 199 | return s; 200 | }; 201 | 202 | this.print = function() { 203 | console.log(this.toString()); 204 | }; 205 | 206 | this.printInverse = function() { 207 | console.log(this.inverseToString()); 208 | }; 209 | 210 | this.getHead = function() { 211 | return head; 212 | }; 213 | 214 | this.getTail = function() { 215 | return tail; 216 | } 217 | } -------------------------------------------------------------------------------- /Base/LinkedList/DoublyLinkedList2.js: -------------------------------------------------------------------------------- 1 | let DoublyLinkedList2 = (function () { 2 | 3 | class Node { 4 | constructor(element) { 5 | this.element = element; 6 | this.next = null; 7 | this.prev = null; // NEW 8 | } 9 | } 10 | 11 | const length = new WeakMap(); 12 | const head = new WeakMap(); 13 | const tail = new WeakMap(); // NEW 14 | 15 | class DoublyLinkedList2 { 16 | 17 | constructor () { 18 | length.set(this, 0); 19 | head.set(this, null); 20 | tail.set(this, null); 21 | } 22 | 23 | append(element) { 24 | 25 | let node = new Node(element), 26 | current, _tail; 27 | 28 | if (this.getHead() === null) { // first node on list 29 | head.set(this, node); 30 | tail.set(this, node); // NEW 31 | } else { 32 | // attach to the tail node // NEW 33 | _tail = this.getTail(); 34 | _tail.next = node; 35 | node.prev = _tail; 36 | tail.set(this, node); 37 | } 38 | 39 | // update size of list 40 | let l = this.size(); 41 | l ++; 42 | length.set(this, l); 43 | } 44 | 45 | insert(position, element) { 46 | 47 | // check for out-of-bounds values 48 | if (position >= 0 && position <= this.size()) { 49 | 50 | let node = new Node(element), 51 | current = this.getHead(), 52 | previous, 53 | index = 0; 54 | 55 | if (position === 0) { // add on first position 56 | 57 | if (!this.getHead()) { // NEW 58 | head.set(this, node); 59 | tail.set(this, node); 60 | } else { 61 | node.next = current; 62 | current.prev = node; // NEW 63 | head.set(this, node); 64 | } 65 | 66 | } else if (position === this.size()) { // last item // NEW 67 | 68 | current = tail; 69 | current.next = node; 70 | node.prev = current; 71 | tail.set(this, node); 72 | 73 | } else { 74 | while (index ++ < position) { 75 | previous = current; 76 | current = current.next; 77 | } 78 | node.next = current; 79 | previous.next = node; 80 | 81 | current.prev = node; // NEW 82 | node.prev = previous; // NEW 83 | } 84 | 85 | // update size of list 86 | let l = this.size(); 87 | l ++; 88 | length.set(this, l); 89 | 90 | return true; 91 | 92 | } else { 93 | return false; 94 | } 95 | } 96 | 97 | removeAt(position) { 98 | 99 | // check for out-of-bounds values 100 | if (position > -1 && position < this.size()) { 101 | 102 | let _head = this.getHead(), 103 | _tail = this.getTail(), 104 | current = _head, 105 | previous, 106 | index = 0; 107 | 108 | // removing first item 109 | if (position === 0) { 110 | 111 | _head = current.next; 112 | 113 | // if there is only one item, then we update tail as well // NEW 114 | if (this.size() === 1) { 115 | _tail = null; 116 | } else { 117 | _head.prev = null; 118 | } 119 | 120 | } else if (position === this.size() - 1) { // last item // NEW 121 | 122 | current = _tail; 123 | _tail = current.prev; 124 | _tail.next = null; 125 | 126 | } else { 127 | 128 | while (index ++ < position) { 129 | 130 | previous = current; 131 | current = current.next; 132 | } 133 | 134 | // link previous with current's next - skip it to remove 135 | previous.next = current.next; 136 | current.next.prev = previous; // NEW 137 | } 138 | 139 | head.set(this,_head); 140 | tail.set(this,_tail); 141 | 142 | //update size of list 143 | let l = this.size(); 144 | l--; 145 | length.set(this, l); 146 | 147 | return current.element; 148 | 149 | } else { 150 | return null; 151 | } 152 | } 153 | 154 | remove(element) { 155 | 156 | let index = this.indexOf(element); 157 | return this.removeAt(index); 158 | } 159 | 160 | indexOf(element) { 161 | 162 | let current = this.getHead(), 163 | index = -1; 164 | 165 | // check first item 166 | if (element == current.element) { 167 | return 0; 168 | } 169 | 170 | index ++; 171 | 172 | // check in the middle of the list 173 | while (current.next) { 174 | 175 | if (element == current.element) { 176 | return index; 177 | } 178 | 179 | current = current.next; 180 | index ++; 181 | } 182 | 183 | // check last item 184 | if (element == current.element) { 185 | return index; 186 | } 187 | 188 | return -1; 189 | } 190 | 191 | isEmpty() { 192 | return this.size() === 0; 193 | } 194 | 195 | size() { 196 | return length.get(this); 197 | } 198 | 199 | toString() { 200 | 201 | let current = this.getHead(), 202 | s = current ? current.element : ''; 203 | 204 | while (current && current.next) { 205 | current = current.next; 206 | s += ', ' + current.element; 207 | } 208 | 209 | return s; 210 | } 211 | 212 | inverseToString() { 213 | 214 | let current = this.getTail(), 215 | s = current ? current.element : ''; 216 | 217 | while (current && current.prev) { 218 | current = current.prev; 219 | s += ', ' + current.element; 220 | } 221 | 222 | return s; 223 | } 224 | 225 | print() { 226 | console.log(this.toString()); 227 | } 228 | 229 | printInverse() { 230 | console.log(this.inverseToString()); 231 | } 232 | 233 | getHead() { 234 | return head.get(this); 235 | } 236 | 237 | getTail() { 238 | return tail.get(this); 239 | } 240 | } 241 | 242 | return DoublyLinkedList2; 243 | })(); -------------------------------------------------------------------------------- /Base/LinkedList/InsertionSort.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Definition for singly-linked list. 4 | */ 5 | function ListNode(val) { 6 | this.val = val; 7 | this.next = null; 8 | } 9 | 10 | 11 | /** 12 | * @param {ListNode} head 13 | * @return {ListNode} 14 | */ 15 | let insertionSort = function(head) { 16 | if (!head || !head.next) 17 | return head; 18 | let dummyHead = new ListNode(), p = head; 19 | dummyHead.next = head; 20 | while (p.next) { 21 | if (p.val < p.next.val) { 22 | p = p.next; 23 | } else { 24 | let temp = p.next, q = dummyHead; 25 | p.next = p.next.next; 26 | while (q.next.val < temp.val) 27 | q = q.next; 28 | temp.next = q.next; 29 | q.next = temp; 30 | } 31 | } 32 | return dummyHead.next; 33 | }; -------------------------------------------------------------------------------- /Base/LinkedList/LinkedList.js: -------------------------------------------------------------------------------- 1 | function LinkedList() { 2 | 3 | let Node = function(element) { 4 | 5 | this.element = element; 6 | this.next = null; 7 | }; 8 | 9 | let length = 0; 10 | let head = null; 11 | 12 | this.append = function(element) { 13 | 14 | let node = new Node(element), 15 | current; 16 | 17 | if (head === null) { // first node on list 18 | head = node; 19 | } else { 20 | 21 | current = head; 22 | 23 | // loop the list until find last item 24 | while (current.next) { 25 | current = current.next; 26 | } 27 | 28 | // get last item and assign next to added item to make the link 29 | current.next = node; 30 | } 31 | 32 | length ++; // update size of list 33 | }; 34 | 35 | this.insert = function(position, element) { 36 | 37 | // check for out-of-bounds values 38 | if (position >= 0 && position <= length) { 39 | 40 | let node = new Node(element), 41 | current = head, 42 | previous, 43 | index = 0; 44 | 45 | if (position === 0){ // add on first position 46 | 47 | node.next = current; 48 | head = node; 49 | 50 | } else { 51 | while (index ++ < position) { 52 | previous = current; 53 | current = current.next; 54 | } 55 | node.next = current; 56 | previous.next = node; 57 | } 58 | 59 | length ++; // update size of list 60 | 61 | return true; 62 | 63 | } else { 64 | return false; 65 | } 66 | }; 67 | 68 | this.removeAt = function(position) { 69 | 70 | // check for out-of-bounds values 71 | if (position > -1 && position < length) { 72 | 73 | let current = head, 74 | previous, 75 | index = 0; 76 | 77 | // removing first item 78 | if (position === 0) { 79 | head = current.next; 80 | } else { 81 | 82 | while (index ++ < position) { 83 | 84 | previous = current; 85 | current = current.next; 86 | } 87 | 88 | // link previous with current's next - skip it to remove 89 | previous.next = current.next; 90 | } 91 | 92 | length --; 93 | 94 | return current.element; 95 | 96 | } else { 97 | return null; 98 | } 99 | }; 100 | 101 | this.remove = function(element) { 102 | 103 | let index = this.indexOf(element); 104 | return this.removeAt(index); 105 | }; 106 | 107 | this.indexOf = function(element) { 108 | 109 | let current = head, 110 | index = 0; 111 | 112 | while (current) { 113 | if (element === current.element) { 114 | return index; 115 | } 116 | index++; 117 | current = current.next; 118 | } 119 | 120 | return -1; 121 | }; 122 | 123 | this.isEmpty = function() { 124 | return length === 0; 125 | }; 126 | 127 | this.size = function() { 128 | return length; 129 | }; 130 | 131 | this.getHead = function() { 132 | return head; 133 | }; 134 | 135 | this.toString = function() { 136 | 137 | let current = head, 138 | string = ''; 139 | 140 | while (current) { 141 | string += current.element + (current.next ? ', ' : ''); 142 | current = current.next; 143 | } 144 | return string; 145 | 146 | }; 147 | 148 | this.print = function() { 149 | console.log(this.toString()); 150 | }; 151 | } 152 | -------------------------------------------------------------------------------- /Base/LinkedList/LinkedList2.js: -------------------------------------------------------------------------------- 1 | let LinkedList2 = (function () { 2 | 3 | class Node { 4 | constructor(element) { 5 | this.element = element; 6 | this.next = null; 7 | } 8 | } 9 | 10 | const length = new WeakMap(); 11 | const head = new WeakMap(); 12 | 13 | class LinkedList2 { 14 | 15 | constructor () { 16 | length.set(this, 0); 17 | head.set(this, null); 18 | } 19 | 20 | append(element) { 21 | 22 | let node = new Node(element), 23 | current; 24 | 25 | if (this.getHead() === null) { //first node on list 26 | head.set(this, node); 27 | } else { 28 | 29 | current = this.getHead(); 30 | 31 | // loop the list until find last item 32 | while (current.next) { 33 | current = current.next; 34 | } 35 | 36 | // get last item and assign next to added item to make the link 37 | current.next = node; 38 | } 39 | 40 | // update size of list 41 | let l = this.size(); 42 | l ++; 43 | length.set(this, l); 44 | } 45 | 46 | insert(position, element) { 47 | 48 | // check for out-of-bounds values 49 | if (position >= 0 && position <= this.size()) { 50 | 51 | let node = new Node(element), 52 | current = this.getHead(), 53 | previous, 54 | index = 0; 55 | 56 | if (position === 0) { // add on first position 57 | 58 | node.next = current; 59 | head.set(this, node); 60 | 61 | } else { 62 | while (index++ < position) { 63 | previous = current; 64 | current = current.next; 65 | } 66 | node.next = current; 67 | previous.next = node; 68 | } 69 | 70 | // update size of list 71 | let l = this.size(); 72 | l ++; 73 | length.set(this, l); 74 | 75 | return true; 76 | 77 | } else { 78 | return false; 79 | } 80 | } 81 | 82 | removeAt(position) { 83 | 84 | // check for out-of-bounds values 85 | if (position > -1 && position < this.size()) { 86 | 87 | let current = this.getHead(), 88 | previous, 89 | index = 0; 90 | 91 | // removing first item 92 | if (position === 0) { 93 | head.set(this, current.next); 94 | } else { 95 | 96 | while (index++ < position) { 97 | 98 | previous = current; 99 | current = current.next; 100 | } 101 | 102 | // link previous with current's next - skip it to remove 103 | previous.next = current.next; 104 | } 105 | 106 | let l = this.size(); 107 | l --; 108 | length.set(this, l); 109 | 110 | return current.element; 111 | 112 | } else { 113 | return null; 114 | } 115 | } 116 | 117 | remove(element) { 118 | 119 | let index = this.indexOf(element); 120 | return this.removeAt(index); 121 | } 122 | 123 | indexOf(element) { 124 | 125 | let current = this.getHead(), 126 | index = 0; 127 | 128 | while (current) { 129 | if (element === current.element) { 130 | return index; 131 | } 132 | index++; 133 | current = current.next; 134 | } 135 | 136 | return -1; 137 | } 138 | 139 | isEmpty() { 140 | return this.size() === 0; 141 | } 142 | 143 | size() { 144 | return length.get(this); 145 | } 146 | 147 | getHead() { 148 | return head.get(this); 149 | } 150 | 151 | toString() { 152 | 153 | let current = this.getHead(), 154 | string = ''; 155 | 156 | while (current) { 157 | string += current.element + (current.next ? ', ' : ''); 158 | current = current.next; 159 | } 160 | return string; 161 | 162 | } 163 | 164 | print() { 165 | console.log(this.toString()); 166 | } 167 | } 168 | 169 | return LinkedList2; 170 | })(); 171 | -------------------------------------------------------------------------------- /Base/LinkedList/LinkedListWithCycle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Definition for singly-linked list. 3 | */ 4 | function ListNode(val) { 5 | this.val = val; 6 | this.next = null; 7 | } 8 | 9 | /** 10 | * 判断链表是否成环 11 | * @param {ListNode} head 12 | * @return {boolean} 13 | */ 14 | let hasCycle = function(head) { 15 | if (!head || !head.next) { 16 | return false; 17 | } 18 | let node = head; 19 | while (node) { 20 | if (node.next === head) { 21 | return true; 22 | } 23 | let temp = node.next; 24 | node.next = head; 25 | node = temp; 26 | } 27 | return false; 28 | }; 29 | 30 | /** 31 | * 判断链表是否成环,如果不成环返回 null,否则求环的起始节点 32 | * 要求不使用额外空间,不更新链表 33 | * @param {ListNode} head 34 | * @return {ListNode} 35 | */ 36 | let detectCycle = function(head) { 37 | if (!head || !head.next ) { 38 | return null; 39 | } 40 | let slow = head, fast = head; 41 | while (fast && fast.next) { 42 | slow = slow.next; 43 | fast = fast.next.next; 44 | if (slow === fast) { 45 | let tmp = head; 46 | while (tmp != slow) { 47 | tmp = tmp.next; 48 | slow = slow.next; 49 | } 50 | return slow; 51 | } 52 | } 53 | return null; 54 | }; 55 | 56 | /** 57 | * 求两个单链表相交的起始节点 58 | * @param {ListNode} headA 59 | * @param {ListNode} headB 60 | * @return {ListNode} 61 | */ 62 | let getIntersectionNode = function(headA, headB) { 63 | if (!headA || !headB) 64 | return null; 65 | let a = headA, b = headB; 66 | while (a != b) { 67 | a = a ? a.next : headB; 68 | b = b ? b.next : headA; 69 | } 70 | return a; 71 | }; 72 | -------------------------------------------------------------------------------- /Base/LinkedList/MergeSort.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Definition for singly-linked list. 4 | */ 5 | function ListNode(val) { 6 | this.val = val; 7 | this.next = null; 8 | } 9 | 10 | /** 11 | * @param {ListNode} head 12 | * @return {ListNode} 13 | */ 14 | let MergeSort = function(head) { 15 | if (!head || !head.next) 16 | return head; 17 | let p1 = head, p2 = head.next; 18 | 19 | // Find the midpoint 20 | while (p2 && p2.next) { 21 | p1 = p1.next; 22 | p2 = p2.next.next; 23 | } 24 | p2 = MergeSort(p1.next); 25 | p1.next = null; 26 | p1 = MergeSort(head); 27 | return merge(p1, p2); 28 | }; 29 | 30 | function merge(h1, h2) { 31 | let fakeHead = new ListNode(Number.MIN_VALUE), p = fakeHead; 32 | while (h1 && h2) { 33 | if (h1.val < h2.val) { 34 | p.next = h1; 35 | h1 = h1.next; 36 | } else { 37 | p.next = h2; 38 | h2 = h2.next; 39 | } 40 | p = p.next; 41 | } 42 | p.next = h1 ? h1 : h2; 43 | return fakeHead.next; 44 | } 45 | -------------------------------------------------------------------------------- /Base/LinkedList/QuickSort.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Definition for singly-linked list. 4 | */ 5 | function ListNode(val) { 6 | this.val = val; 7 | this.next = null; 8 | } 9 | 10 | /** 11 | * @param {ListNode} head 12 | * @return {ListNode} 13 | */ 14 | let sortList = function(head) { 15 | if (!head || !head.next) 16 | return head; 17 | quickSort(head, null); 18 | return head; 19 | }; 20 | 21 | // 以 head 为 pivot 进行 partition 22 | function partition(head, end) { 23 | let p = head, q = p.next, val = head.val; 24 | 25 | while (q != end) { 26 | // 和以 end 为 pivot 不一样的地方在于 27 | // end 是先 swap 再移动指针,head 是先移动指针再 swap 28 | if (q.val < val) { 29 | p = p.next; 30 | [p.val, q.val] = [q.val, p.val]; 31 | } 32 | q = q.next; 33 | } 34 | 35 | [p.val, head.val] = [head.val, p.val]; 36 | return p; 37 | } 38 | 39 | // 效率没有 mergeSort 高, 对上百个重复 1 2 3 的数据 TLE了 40 | function quickSort(head, end) { 41 | if (head != end) { 42 | let index = partition(head, end); 43 | quickSort(head, index); 44 | quickSort(index.next, end); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Base/Map/Dictionaries.js: -------------------------------------------------------------------------------- 1 | function Dictionary() { 2 | 3 | let items = {}; 4 | 5 | this.set = function(key, value) { 6 | items[key] = value; 7 | }; 8 | 9 | this.delete = function(key) { 10 | if (this.has(key)) { 11 | delete items[key]; 12 | return true; 13 | } 14 | return false; 15 | }; 16 | 17 | this.has = function(key) { 18 | return items.hasOwnProperty(key); 19 | //return value in items; 20 | }; 21 | 22 | this.get = function(key) { 23 | return this.has(key) ? items[key] : undefined; 24 | }; 25 | 26 | this.clear = function() { 27 | items = {}; 28 | }; 29 | 30 | this.size = function() { 31 | return Object.keys(items).length; 32 | }; 33 | 34 | this.keys = function() { 35 | return Object.keys(items); 36 | }; 37 | 38 | this.values = function() { 39 | let values = []; 40 | for (let k in items) { 41 | if (this.has(k)) { 42 | values.push(items[k]); 43 | } 44 | } 45 | return values; 46 | }; 47 | 48 | this.each = function(fn) { 49 | for (let k in items) { 50 | if (this.has(k)) { 51 | fn(k, items[k]); 52 | } 53 | } 54 | }; 55 | 56 | this.getItems = function() { 57 | return items; 58 | } 59 | } -------------------------------------------------------------------------------- /Base/Map/HashCollisionLinearProbing.js: -------------------------------------------------------------------------------- 1 | // 线性探查 2 | function HashLinearProbing() { 3 | 4 | let table = []; 5 | 6 | let ValuePair = function(key, value) { 7 | this.key = key; 8 | this.value = value; 9 | 10 | this.toString = function() { 11 | return '[' + this.key + ' - ' + this.value + ']'; 12 | } 13 | }; 14 | 15 | let loseloseHashCode = function (key) { 16 | let hash = 0; 17 | for (let i = 0; i < key.length; i++) { 18 | hash += key.charCodeAt(i); 19 | } 20 | return hash % 37; 21 | }; 22 | 23 | let hashCode = function(key) { 24 | return loseloseHashCode(key); 25 | }; 26 | 27 | this.put = function(key, value) { 28 | let position = hashCode(key); 29 | console.log(position + ' - ' + key); 30 | 31 | if (table[position] == undefined) { 32 | table[position] = new ValuePair(key, value); 33 | } else { 34 | let index = ++position; 35 | while (table[index] != undefined) { 36 | index++; 37 | } 38 | table[index] = new ValuePair(key, value); 39 | } 40 | }; 41 | 42 | this.get = function(key) { 43 | let position = hashCode(key); 44 | 45 | if (table[position] !== undefined) { 46 | if (table[position].key === key) { 47 | return table[position].value; 48 | } else { 49 | let index = ++position; 50 | while (table[index] !== undefined && (table[index] && table[index].key !== key)) { 51 | index++; 52 | } 53 | if (table[index] && table[index].key === key) { 54 | return table[index].value; 55 | } 56 | } 57 | } else { //search for possible deleted value 58 | let index = ++ position; 59 | while (table[index] == undefined || index == table.length || 60 | (table[index] !== undefined && table[index] && table[index].key !== key)) { 61 | index++; 62 | } 63 | if (table[index] && table[index].key === key) { 64 | return table[index].value; 65 | } 66 | } 67 | return undefined; 68 | }; 69 | 70 | this.remove = function(key) { 71 | let position = hashCode(key); 72 | 73 | if (table[position] !== undefined) { 74 | if (table[position].key === key) { 75 | table[position] = undefined; 76 | } else { 77 | let index = ++ position; 78 | while (table[index] === undefined || table[index].key !== key) { 79 | index++; 80 | } 81 | if (table[index].key === key) { 82 | table[index] = undefined; 83 | } 84 | } 85 | } 86 | }; 87 | 88 | this.print = function() { 89 | for (let i = 0; i < table.length; ++ i) { 90 | if (table[i] !== undefined) { 91 | console.log(i + ' -> ' + table[i].toString()); 92 | } 93 | } 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /Base/Map/HashCollisionSeparateChaining.js: -------------------------------------------------------------------------------- 1 | // 分离链接 2 | function HashTableSeparateChaining() { 3 | 4 | let table = []; 5 | 6 | let ValuePair = function(key, value) { 7 | this.key = key; 8 | this.value = value; 9 | 10 | this.toString = function() { 11 | return '[' + this.key + ' - ' + this.value + ']'; 12 | } 13 | }; 14 | 15 | let loseloseHashCode = function (key) { 16 | let hash = 0; 17 | for (let i = 0; i < key.length; i ++) { 18 | hash += key.charCodeAt(i); 19 | } 20 | return hash % 37; 21 | }; 22 | 23 | let hashCode = function(key) { 24 | return loseloseHashCode(key); 25 | }; 26 | 27 | this.put = function(key, value) { 28 | let position = hashCode(key); 29 | console.log(position + ' - ' + key); 30 | 31 | if (table[position] == undefined) { 32 | table[position] = new LinkedList(); 33 | } 34 | table[position].append(new ValuePair(key, value)); 35 | }; 36 | 37 | this.get = function(key) { 38 | let position = hashCode(key); 39 | 40 | if (table[position] !== undefined && !table[position].isEmpty()) { 41 | 42 | //iterate linked list to find key/value 43 | let current = table[position].getHead(); 44 | 45 | do { 46 | if (current.element.key === key) { 47 | return current.element.value; 48 | } 49 | current = current.next; 50 | } while(current); 51 | } 52 | return undefined; 53 | }; 54 | 55 | this.remove = function(key) { 56 | 57 | let position = hashCode(key); 58 | 59 | if (table[position] !== undefined) { 60 | 61 | //iterate linked list to find key/value 62 | let current = table[position].getHead(); 63 | 64 | do { 65 | if (current.element.key === key) { 66 | table[position].remove(current.element); 67 | if (table[position].isEmpty()) { 68 | table[position] = undefined; 69 | } 70 | return true; 71 | } 72 | current = current.next; 73 | } while(current); 74 | } 75 | 76 | return false; 77 | }; 78 | 79 | this.print = function() { 80 | for (let i = 0; i < table.length; ++ i) { 81 | if (table[i] !== undefined) { 82 | console.log(table[i].toString()); 83 | } 84 | } 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /Base/Map/HashTable.js: -------------------------------------------------------------------------------- 1 | function HashTable() { 2 | 3 | let table = []; 4 | 5 | let loseloseHashCode = function (key) { 6 | let hash = 0; 7 | for (let i = 0; i < key.length; i ++) { 8 | hash += key.charCodeAt(i); 9 | } 10 | return hash % 37; 11 | }; 12 | 13 | let djb2HashCode = function (key) { 14 | let hash = 5381; 15 | for (let i = 0; i < key.length; i ++) { 16 | hash = hash * 33 + key.charCodeAt(i); 17 | } 18 | return hash % 1013; 19 | }; 20 | 21 | let hashCode = function (key) { 22 | return loseloseHashCode(key); 23 | }; 24 | 25 | this.put = function (key, value) { 26 | let position = hashCode(key); 27 | console.log(position + ' - ' + key); 28 | table[position] = value; 29 | }; 30 | 31 | this.get = function (key) { 32 | return table[hashCode(key)]; 33 | }; 34 | 35 | this.remove = function(key) { 36 | table[hashCode(key)] = undefined; 37 | }; 38 | 39 | this.print = function () { 40 | for (let i = 0; i < table.length; ++ i) { 41 | if (table[i] !== undefined) { 42 | console.log(i + ": " + table[i]); 43 | } 44 | } 45 | }; 46 | } -------------------------------------------------------------------------------- /Base/Map/UsingHashCollisionLinearProbing.js: -------------------------------------------------------------------------------- 1 | var hashLinearProbing = new HashLinearProbing(); 2 | 3 | hashLinearProbing.put('Gandalf', 'gandalf@email.com'); 4 | hashLinearProbing.put('John', 'johnsnow@email.com'); 5 | hashLinearProbing.put('Tyrion', 'tyrion@email.com'); 6 | hashLinearProbing.put('Aaron', 'aaron@email.com'); 7 | hashLinearProbing.put('Donnie', 'donnie@email.com'); 8 | hashLinearProbing.put('Ana', 'ana@email.com'); 9 | hashLinearProbing.put('Jonathan', 'jonathan@email.com'); 10 | hashLinearProbing.put('Jamie', 'jamie@email.com'); 11 | hashLinearProbing.put('Sue', 'sue@email.com'); 12 | hashLinearProbing.put('Mindy', 'mindy@email.com'); 13 | hashLinearProbing.put('Paul', 'paul@email.com'); 14 | hashLinearProbing.put('Nathan', 'nathan@email.com'); 15 | 16 | console.log('**** Printing Hash **** '); 17 | 18 | hashLinearProbing.print(); 19 | 20 | console.log('**** Get **** '); 21 | 22 | console.log(hashLinearProbing.get('Nathan')); 23 | console.log(hashLinearProbing.get('Loiane')); 24 | 25 | console.log('**** Remove **** '); 26 | 27 | hashLinearProbing.remove('Gandalf'); 28 | console.log(hashLinearProbing.get('Gandalf')); 29 | hashLinearProbing.print(); 30 | 31 | console.log('**** Remove Test 2 **** '); 32 | console.log('Removing Jonathan', hashLinearProbing.remove('Jonathan')); 33 | console.log('**** Print **** '); 34 | hashLinearProbing.print(); 35 | console.log('Get Jamie', hashLinearProbing.get('Jamie')); 36 | console.log('**** Print **** '); 37 | hashLinearProbing.print(); -------------------------------------------------------------------------------- /Base/Map/UsingHashCollisionSeparateChaining.js: -------------------------------------------------------------------------------- 1 | var hashTableSeparateChaining = new HashTableSeparateChaining(); 2 | 3 | hashTableSeparateChaining.put('Gandalf', 'gandalf@email.com'); 4 | hashTableSeparateChaining.put('John', 'johnsnow@email.com'); 5 | hashTableSeparateChaining.put('Tyrion', 'tyrion@email.com'); 6 | hashTableSeparateChaining.put('Aaron', 'aaron@email.com'); 7 | hashTableSeparateChaining.put('Donnie', 'donnie@email.com'); 8 | hashTableSeparateChaining.put('Ana', 'ana@email.com'); 9 | hashTableSeparateChaining.put('Jonathan', 'jonathan@email.com'); 10 | hashTableSeparateChaining.put('Jamie', 'jamie@email.com'); 11 | hashTableSeparateChaining.put('Sue', 'sue@email.com'); 12 | hashTableSeparateChaining.put('Mindy', 'mindy@email.com'); 13 | hashTableSeparateChaining.put('Paul', 'paul@email.com'); 14 | hashTableSeparateChaining.put('Nathan', 'nathan@email.com'); 15 | 16 | console.log('**** Printing Hash **** '); 17 | 18 | hashTableSeparateChaining.print(); 19 | 20 | console.log('**** Get **** '); 21 | 22 | console.log(hashTableSeparateChaining.get('Jamie')); 23 | console.log(hashTableSeparateChaining.get('Sue')); 24 | console.log(hashTableSeparateChaining.get('Jonathan')); 25 | console.log(hashTableSeparateChaining.get('Loiane')); 26 | 27 | console.log('**** Remove **** '); 28 | 29 | console.log(hashTableSeparateChaining.remove('Gandalf')); 30 | console.log(hashTableSeparateChaining.get('Gandalf')); 31 | hashTableSeparateChaining.print(); 32 | 33 | console.log(hashTableSeparateChaining.remove('Sue')); 34 | hashTableSeparateChaining.print(); 35 | 36 | console.log(hashTableSeparateChaining.remove('Jamie')); 37 | hashTableSeparateChaining.print(); 38 | 39 | console.log(hashTableSeparateChaining.remove('Donnie')); 40 | hashTableSeparateChaining.print(); -------------------------------------------------------------------------------- /Base/Queue/Queue.js: -------------------------------------------------------------------------------- 1 | function Queue() { 2 | 3 | let items = []; 4 | 5 | this.enqueue = function(element) { 6 | items.push(element); 7 | }; 8 | 9 | this.dequeue = function() { 10 | return items.shift(); 11 | }; 12 | 13 | this.front = function() { 14 | return items[0]; 15 | }; 16 | 17 | this.isEmpty = function() { 18 | return items.length == 0; 19 | }; 20 | 21 | this.clear = function() { 22 | items = []; 23 | }; 24 | 25 | this.size = function() { 26 | return items.length; 27 | }; 28 | 29 | this.print = function() { 30 | console.log(items.toString()); 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /Base/Queue/Queue2.js: -------------------------------------------------------------------------------- 1 | let Queue = (function () { 2 | 3 | const items = new WeakMap(); 4 | 5 | class Queue { 6 | 7 | constructor () { 8 | items.set(this, []); 9 | } 10 | 11 | enqueue(element) { 12 | let q = items.get(this); 13 | q.push(element); 14 | } 15 | 16 | dequeue() { 17 | let q = items.get(this); 18 | let r = q.shift(); 19 | return r; 20 | } 21 | 22 | front() { 23 | let q = items.get(this); 24 | return q[0]; 25 | } 26 | 27 | isEmpty() { 28 | return items.get(this).length == 0; 29 | } 30 | 31 | size() { 32 | let q = items.get(this); 33 | return q.length; 34 | } 35 | 36 | clear() { 37 | items.set(this, []); 38 | } 39 | 40 | print() { 41 | console.log(this.toString()); 42 | } 43 | 44 | toString() { 45 | return items.get(this).toString(); 46 | } 47 | } 48 | 49 | return Queue; 50 | })(); 51 | -------------------------------------------------------------------------------- /Base/Set/Set.js: -------------------------------------------------------------------------------- 1 | 2 | function Set() { 3 | 4 | let items = {}; 5 | 6 | this.add = function(value) { 7 | if (!this.has(value)) { 8 | items[value] = value; 9 | return true; 10 | } 11 | return false; 12 | }; 13 | 14 | this.delete = function(value) { 15 | if (this.has(value)) { 16 | delete items[value]; 17 | return true; 18 | } 19 | return false; 20 | }; 21 | 22 | this.has = function(value) { 23 | return items.hasOwnProperty(value); 24 | //return value in items; 25 | }; 26 | 27 | this.clear = function() { 28 | items = {}; 29 | }; 30 | 31 | /** 32 | * Modern browsers function 33 | * IE9+, FF4+, Chrome5+, Opera12+, Safari5+ 34 | * @returns {Number} 35 | */ 36 | this.size = function() { 37 | return Object.keys(items).length; 38 | }; 39 | 40 | /** 41 | * cross browser compatibility - legacy browsers 42 | * for modern browsers use size function 43 | * @returns {number} 44 | */ 45 | this.sizeLegacy = function() { 46 | let count = 0; 47 | for (let key in items) { 48 | if (items.hasOwnProperty(key)) 49 | ++count; 50 | } 51 | return count; 52 | }; 53 | 54 | /** 55 | * Modern browsers function 56 | * IE9+, FF4+, Chrome5+, Opera12+, Safari5+ 57 | * @returns {Array} 58 | */ 59 | this.values = function(){ 60 | let values = []; 61 | for (let i=0, keys=Object.keys(items); i otherSet.size()){ 126 | return false; 127 | } else { 128 | let values = this.values(); 129 | for (let i=0; i otherSet.size()) { 102 | return false; 103 | } else { 104 | let values = this.values(); 105 | for (let i = 0; i < values.length; i ++) { 106 | if (!otherSet.has(values[i])) { 107 | return false; 108 | } 109 | } 110 | return true; 111 | } 112 | }; 113 | 114 | } 115 | 116 | return Set2; 117 | })(); -------------------------------------------------------------------------------- /Base/Set/UsingES6Set.js: -------------------------------------------------------------------------------- 1 | let set = new Set(); 2 | 3 | set.add(1); 4 | console.log(set.values()); // outputs @Iterator 5 | console.log(set.has(1)); // outputs true 6 | console.log(set.size); // outputs 1 7 | 8 | set.add(2); 9 | console.log(set.values()); // outputs [1, 2] 10 | console.log(set.has(2)); // true 11 | console.log(set.size); // 2 12 | 13 | set.delete(1); 14 | console.log(set.values()); // outputs [2] 15 | 16 | set.delete(2); 17 | console.log(set.values()); // outputs [] 18 | 19 | let setA = new Set(); 20 | setA.add(1); 21 | setA.add(2); 22 | setA.add(3); 23 | 24 | let setB = new Set(); 25 | setB.add(2); 26 | setB.add(3); 27 | setB.add(4); 28 | 29 | // --------- Union ---------- 30 | let unionAb = new Set(); 31 | for (let x of setA) unionAb.add(x); 32 | for (let x of setB) unionAb.add(x); 33 | console.log(unionAb); 34 | 35 | // --------- Intersection ---------- 36 | let intersection = function(setA, setB) { 37 | let intersectionSet = new Set(); 38 | 39 | for (let x of setA) { 40 | if (setB.has(x)) { 41 | intersectionSet.add(x); 42 | } 43 | } 44 | 45 | return intersectionSet; 46 | }; 47 | 48 | let intersectionAB = intersection(setA, setB); 49 | console.log(intersectionAB); 50 | 51 | // alternative - works on FF only 52 | // intersectionAb = new Set([x for (x of setA) if (setB.has(x))]); 53 | // console.log(intersectionAB); 54 | 55 | // --------- Difference ---------- 56 | let difference = function(setA, setB){ 57 | let differenceSet = new Set(); 58 | 59 | for (let x of setA){ 60 | if (!setB.has(x)){ 61 | differenceSet.add(x); 62 | } 63 | } 64 | 65 | return differenceSet; 66 | }; 67 | 68 | let differenceAB = difference(setA, setB); 69 | console.log(differenceAB); 70 | 71 | // alternative - works on FF only 72 | // differenceAB = new Set([x for (x of setA) if (!setB.has(x))]); 73 | // console.log(differenceAB); 74 | -------------------------------------------------------------------------------- /Base/Stack/Stack.js: -------------------------------------------------------------------------------- 1 | //*** 声明了一个私有的items变量,只能被Stack类访问。 2 | //*** 但是每个类的实例方法不共享 3 | function Stack() { 4 | 5 | let items = []; 6 | 7 | this.push = function(element){ 8 | items.push(element); 9 | }; 10 | 11 | this.pop = function(){ 12 | return items.pop(); 13 | }; 14 | 15 | this.peek = function(){ 16 | return items[items.length - 1]; 17 | }; 18 | 19 | this.isEmpty = function(){ 20 | return items.length === 0; 21 | }; 22 | 23 | this.size = function() { 24 | return items.length; 25 | }; 26 | 27 | this.clear = function() { 28 | items = []; 29 | }; 30 | 31 | this.print = function() { 32 | console.log(items.toString()); 33 | }; 34 | 35 | this.toString = function() { 36 | return items.toString(); 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /Base/Stack/Stack1.js: -------------------------------------------------------------------------------- 1 | //*** 基于原型的类,更节省内存,但是无法声明私有属性或方法 2 | //*** items可直接被实例访问 3 | function Stack() { 4 | this.items = []; 5 | } 6 | 7 | Stack.prototype.push = function(element) { 8 | this.items.push(element); 9 | }; 10 | 11 | Stack.prototype.pop = function() { 12 | return this.items.pop(); 13 | }; 14 | 15 | Stack.prototype.peek = function() { 16 | return this.items[this.items.length - 1]; 17 | }; 18 | 19 | Stack.prototype.isEmpty = function() { 20 | return this.items.length == 0; 21 | }; 22 | 23 | Stack.prototype.size = function() { 24 | return this.items.length; 25 | }; 26 | 27 | Stack.prototype.clear = function() { 28 | this.items = []; 29 | }; 30 | 31 | Stack.prototype.print = function() { 32 | console.log(this.items.toString()); 33 | }; 34 | 35 | Stack.prototype.toString = function() { 36 | return this.items.toString(); 37 | }; 38 | -------------------------------------------------------------------------------- /Base/Stack/Stack1_ES6.js: -------------------------------------------------------------------------------- 1 | //*** 基于原型的类,更节省内存,但是无法声明私有属性或方法 2 | //*** items可直接被实例访问 3 | class Stack { 4 | 5 | constructor () { 6 | this.items = []; 7 | } 8 | 9 | push(element) { 10 | this.items.push(element); 11 | } 12 | 13 | pop() { 14 | return this.items.pop(); 15 | } 16 | 17 | peek() { 18 | return this.items[this.items.length - 1]; 19 | } 20 | 21 | isEmpty() { 22 | return this.items.length == 0; 23 | } 24 | 25 | size() { 26 | return this.items.length; 27 | } 28 | 29 | clear() { 30 | this.items = []; 31 | } 32 | 33 | print() { 34 | console.log(this.toString()); 35 | } 36 | 37 | toString() { 38 | return this.items.toString(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Base/Stack/Stack2.js: -------------------------------------------------------------------------------- 1 | let _items = Symbol(); // 不可变,可以用作对象的属性 2 | 3 | // 私有属性是假的,Object.getOwnPropertyNames()虽然无法获取Symbols属性 4 | // 但是,ES6新增的Object.getOwnPropertySymbols()能去到类里面声明的所有Symbols属性 5 | class Stack2 { 6 | 7 | constructor () { 8 | this[_items] = []; 9 | } 10 | 11 | push(element) { 12 | this[_items].push(element); 13 | } 14 | 15 | pop() { 16 | return this[_items].pop(); 17 | } 18 | 19 | peek() { 20 | return this[_items][this[_items].length - 1]; 21 | } 22 | 23 | isEmpty() { 24 | return this[_items].length == 0; 25 | } 26 | 27 | size() { 28 | return this[_items].length; 29 | } 30 | 31 | clear() { 32 | this[_items] = []; 33 | } 34 | 35 | print() { 36 | console.log(this.toString()); 37 | } 38 | 39 | toString() { 40 | return this[_items].toString(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Base/Stack/Stack3.js: -------------------------------------------------------------------------------- 1 | // 用闭包将Stack类包起来,这样就只能在这个函数里访问WeekMap,items就成为私有属性 2 | // 但是用这种方法的话,扩展类无法继承私有属性 3 | let Stack3 = (function () { 4 | 5 | const items = new WeakMap(); 6 | 7 | // 内部属性items是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏 8 | class Stack3 { 9 | 10 | constructor () { 11 | items.set(this, []); 12 | } 13 | 14 | push(element) { 15 | let s = items.get(this); 16 | s.push(element); 17 | } 18 | 19 | pop() { 20 | let s = items.get(this); 21 | let r = s.pop(); 22 | return r; 23 | } 24 | 25 | peek() { 26 | let s = items.get(this); 27 | return s[s.length - 1]; 28 | } 29 | 30 | isEmpty() { 31 | return items.get(this).length == 0; 32 | } 33 | 34 | size() { 35 | let s = items.get(this); 36 | return s.length; 37 | } 38 | 39 | clear() { 40 | items.set(this, []); 41 | } 42 | 43 | print() { 44 | console.log(this.toString()); 45 | } 46 | 47 | toString() { 48 | return items.get(this).toString(); 49 | } 50 | } 51 | 52 | return Stack3; 53 | })(); 54 | -------------------------------------------------------------------------------- /Graph/Graph.js: -------------------------------------------------------------------------------- 1 | function Graph() { 2 | 3 | let vertices = []; 4 | 5 | let adjList = new Dictionary(); 6 | 7 | this.addVertex = function(v) { 8 | vertices.push(v); 9 | adjList.set(v, []); // initialize adjacency list with array as well; 10 | }; 11 | 12 | this.addEdge = function(v, w) { 13 | adjList.get(v).push(w); 14 | // adjList.get(w).push(v); // commented to run the improved DFS with topological sorting 15 | }; 16 | 17 | this.toString = function() { 18 | let s = ''; 19 | for (let i = 0; i < vertices.length; i ++) { 20 | s += vertices[i] + ' -> '; 21 | let neighbors = adjList.get(vertices[i]); 22 | for (let j = 0; j < neighbors.length; j ++) { 23 | s += neighbors[j] + ' '; 24 | } 25 | s += '\n'; 26 | } 27 | return s; 28 | }; 29 | 30 | let initializeColor = function() { 31 | let color = {}; 32 | for (let i = 0; i < vertices.length; i ++) { 33 | color[vertices[i]] = 'white'; 34 | } 35 | return color; 36 | }; 37 | 38 | this.bfs = function(v, callback) { 39 | 40 | let color = initializeColor(), 41 | queue = new Queue(); 42 | queue.enqueue(v); 43 | 44 | while (!queue.isEmpty()) { 45 | let u = queue.dequeue(), 46 | neighbors = adjList.get(u); 47 | color[u] = 'grey'; 48 | for (let i = 0; i < neighbors.length; i ++) { 49 | let w = neighbors[i]; 50 | if (color[w] === 'white') { 51 | color[w] = 'grey'; 52 | queue.enqueue(w); 53 | } 54 | } 55 | color[u] = 'black'; 56 | if (callback) { 57 | callback(u); 58 | } 59 | } 60 | }; 61 | 62 | this.dfs = function(callback) { 63 | 64 | let color = initializeColor(); 65 | 66 | for (let i = 0; i < vertices.length; i ++) { 67 | if (color[vertices[i]] === 'white') { 68 | dfsVisit(vertices[i], color, callback); 69 | } 70 | } 71 | }; 72 | 73 | let dfsVisit = function(u, color, callback) { 74 | 75 | // grey被发现,刚从队列中拿出来 76 | // black已被探索,已将所有邻接点加入队列 77 | // white未被访问,刚要加入队列 78 | color[u] = 'grey'; 79 | if (callback) { 80 | callback(u); 81 | } 82 | console.log('Discovered ' + u); 83 | let neighbors = adjList.get(u); 84 | for (let i = 0; i < neighbors.length; i ++) { 85 | let w = neighbors[i]; 86 | if (color[w] === 'white') { 87 | dfsVisit(w, color, callback); 88 | } 89 | } 90 | color[u] = 'black'; 91 | console.log('explored ' + u); 92 | }; 93 | 94 | 95 | this.BFS = function(v) { 96 | 97 | // d[u]:v到u的距离 98 | // pred[u]:u的前缀点 99 | let color = initializeColor(), 100 | queue = new Queue(), 101 | d = {}, 102 | pred = {}; 103 | queue.enqueue(v); 104 | 105 | for (let i = 0; i < vertices.length; i ++) { 106 | d[vertices[i]] = 0; 107 | pred[vertices[i]] = null; 108 | } 109 | 110 | while (!queue.isEmpty()) { 111 | let u = queue.dequeue(), 112 | neighbors = adjList.get(u); 113 | color[u] = 'grey'; 114 | for (let i = 0; i < neighbors.length; i ++) { 115 | let w = neighbors[i]; 116 | if (color[w] === 'white') { 117 | color[w] = 'grey'; 118 | d[w] = d[u] + 1; 119 | pred[w] = u; 120 | queue.enqueue(w); 121 | } 122 | } 123 | color[u] = 'black'; 124 | } 125 | 126 | return { 127 | distances: d, 128 | predecessors: pred 129 | }; 130 | }; 131 | 132 | let time = 0; 133 | this.DFS = function() { 134 | 135 | // d[u]:顶点u的发现时间 136 | // f[u]:u的完成探索时间即顶点被标注为黑色是的时间 137 | // p[u]:顶点u的前溯点 138 | // 其中 1 <= d[u] < f[u] <= 2|V| 139 | let color = initializeColor(), 140 | d = {}, 141 | f = {}, 142 | p = {}; 143 | time = 0; 144 | 145 | for (let i = 0; i < vertices.length; i ++) { 146 | f[vertices[i]] = 0; 147 | d[vertices[i]] = 0; 148 | p[vertices[i]] = null; 149 | } 150 | 151 | for (let i = 0; i < vertices.length; i ++) { 152 | if (color[vertices[i]] === 'white') { 153 | DFSVisit(vertices[i], color, d, f, p); 154 | } 155 | } 156 | 157 | return { 158 | discovery: d, 159 | finished: f, 160 | predecessors: p 161 | }; 162 | }; 163 | 164 | let DFSVisit = function(u, color, d, f, p) { 165 | 166 | console.log('discovered ' + u); 167 | color[u] = 'grey'; 168 | d[u] = ++ time; 169 | let neighbors = adjList.get(u); 170 | for (let i = 0; i < neighbors.length; i ++) { 171 | let w = neighbors[i]; 172 | if (color[w] === 'white') { 173 | p[w] = u; 174 | DFSVisit(w, color, d, f, p); 175 | } 176 | } 177 | color[u] = 'black'; 178 | f[u] = ++ time; 179 | console.log('explored ' + u); 180 | }; 181 | } -------------------------------------------------------------------------------- /Graph/MinimumSpanningTree.js: -------------------------------------------------------------------------------- 1 | function MinimumSpanningTree(graph) { 2 | 3 | this.graph = graph; 4 | 5 | let INF = Number.MAX_SAFE_INTEGER; 6 | 7 | let minKey = function (key, visited) { 8 | // Initialize min value 9 | let min = INF, minIndex; 10 | 11 | for (let v = 0; v < this.graph.length; v ++) { 12 | if (visited[v] == false && key[v] < min) { 13 | min = key[v]; 14 | minIndex = v; 15 | } 16 | } 17 | 18 | return minIndex; 19 | }; 20 | 21 | this.prim = function() { 22 | let parent = [], 23 | key = [], 24 | visited = [], 25 | length = this.graph.length; 26 | 27 | for (let i = 0; i < length; i ++) { 28 | key[i] = INF; 29 | visited[i] = false; 30 | } 31 | 32 | key[0] = 0; 33 | parent[0] = -1; 34 | 35 | for (let i = 0; i < length - 1; i ++) { 36 | let u = minKey(key, visited); 37 | visited[u] = true; 38 | 39 | for (let v = 0; v < length; v ++) { 40 | if (this.graph[u][v] && visited[v] == false && this.graph[u][v] < key[v]) { 41 | parent[v] = u; 42 | key[v] = this.graph[u][v]; 43 | } 44 | } 45 | } 46 | 47 | return parent; 48 | }; 49 | 50 | let find = function(i, parent) { 51 | while(parent[i]) { 52 | i = parent[i]; 53 | } 54 | return i; 55 | }; 56 | 57 | let union = function(i, j, parent) { 58 | if(i != j) { 59 | parent[j] = i; 60 | return true; 61 | } 62 | return false; 63 | }; 64 | 65 | let initializeCost = function() { 66 | let cost = [], length = this.graph.length; 67 | for (let i = 0; i < length; i++) { 68 | cost[i] = []; 69 | for (let j = 0; j < length; j++) { 70 | if (this.graph[i][j] == 0) { 71 | cost[i][j] = INF; 72 | } else { 73 | cost[i][j] = this.graph[i][j]; 74 | } 75 | } 76 | } 77 | return cost; 78 | }; 79 | 80 | this.kruskal = function() { 81 | 82 | let length = this.graph.length, 83 | parent = [], cost, 84 | ne = 0, a, b, u, v; 85 | 86 | cost = initializeCost(); 87 | 88 | while(ne < length-1) { 89 | 90 | for (let i = 0, min = INF; i < length; i ++) { 91 | for (let j = 0; j < length; j ++) { 92 | if (cost[i][j] < min) { 93 | min = cost[i][j]; 94 | a = u = i; 95 | b = v = j; 96 | } 97 | } 98 | } 99 | 100 | u = find(u, parent); 101 | v = find(v, parent); 102 | 103 | if (union(u, v, parent)) { 104 | ne ++; 105 | } 106 | 107 | cost[a][b] = cost[b][a] = INF; 108 | } 109 | 110 | return parent; 111 | } 112 | } -------------------------------------------------------------------------------- /Graph/ShortestPath.js: -------------------------------------------------------------------------------- 1 | function ShortestPath(graph) { 2 | 3 | this.graph = graph; 4 | 5 | let INF = Number.MAX_SAFE_INTEGER; 6 | 7 | let minDistance = function(dist, visited) { 8 | 9 | let min = INF, 10 | minIndex = -1; 11 | 12 | for (let v = 0; v < dist.length; v ++) { 13 | if (visited[v] == false && dist[v] <= min) { 14 | min = dist[v]; 15 | minIndex = v; 16 | } 17 | } 18 | 19 | return minIndex; 20 | }; 21 | 22 | this.dijkstra = function(src) { 23 | 24 | let dist = [], 25 | visited = [], 26 | length = this.graph.length; 27 | 28 | for (let i = 0; i < length; i ++) { 29 | dist[i] = INF; 30 | visited[i] = false; 31 | } 32 | 33 | dist[src] = 0; 34 | 35 | for (let i = 0; i < length - 1; i ++) { 36 | 37 | let u = minDistance(dist, visited); 38 | 39 | visited[u] = true; 40 | 41 | for (let v = 0; v < length; v ++) { 42 | if (!visited[v] && this.graph[u][v] != 0 && dist[u] != INF && dist[u] + this.graph[u][v] < dist[v]) { 43 | dist[v] = dist[u] + this.graph[u][v]; 44 | } 45 | } 46 | } 47 | 48 | return dist; 49 | }; 50 | 51 | this.floydWarshall = function() { 52 | 53 | let dist = [], 54 | length = this.graph.length; 55 | 56 | for (let i = 0; i < length; i++){ 57 | dist[i] = []; 58 | for (let j = 0; j < length; j++){ 59 | dist[i][j] = this.graph[i][j]; 60 | } 61 | } 62 | 63 | for (let k = 0; k < length; k++){ 64 | for (let i = 0; i < length; i++){ 65 | for (let j = 0; j < length; j++){ 66 | if (dist[i][k] + dist[k][j] < dist[i][j]){ 67 | dist[i][j] = dist[i][k] + dist[k][j]; 68 | } 69 | } 70 | } 71 | } 72 | 73 | return dist; 74 | } 75 | } -------------------------------------------------------------------------------- /Graph/UsingGraphs.js: -------------------------------------------------------------------------------- 1 | let graph = new Graph(); 2 | 3 | let myVertices = ['A','B','C','D','E','F','G','H','I']; 4 | 5 | for (let i = 0; i < myVertices.length; i ++) { 6 | graph.addVertex(myVertices[i]); 7 | } 8 | 9 | graph.addEdge('A', 'B'); 10 | graph.addEdge('A', 'C'); 11 | graph.addEdge('A', 'D'); 12 | graph.addEdge('C', 'D'); 13 | graph.addEdge('C', 'G'); 14 | graph.addEdge('D', 'G'); 15 | graph.addEdge('D', 'H'); 16 | graph.addEdge('B', 'E'); 17 | graph.addEdge('B', 'F'); 18 | graph.addEdge('E', 'I'); 19 | 20 | console.log('********* printing graph ***********'); 21 | 22 | console.log(graph.toString()); 23 | 24 | console.log('********* bfs ***********'); 25 | 26 | function printNode(value) { 27 | console.log('Visited vertex: ' + value); 28 | } 29 | 30 | graph.bfs(myVertices[0], printNode); 31 | 32 | console.log('********* dfs ***********'); 33 | 34 | graph.dfs(); 35 | 36 | console.log('********* shortest path - BFS ***********'); 37 | let shortestPathA = graph.BFS(myVertices[0]); 38 | console.log(shortestPathA.distances); 39 | console.log(shortestPathA.predecessors); 40 | 41 | //from A to all other vertices 42 | let fromVertex = myVertices[0]; 43 | 44 | for (let i = 1; i < myVertices.length; i ++) { 45 | let toVertex = myVertices[i], 46 | path = new Stack(); 47 | for (let v = toVertex; v !== fromVertex; v = shortestPathA.predecessors[v]) { 48 | path.push(v); 49 | } 50 | path.push(fromVertex); 51 | let s = path.pop(); 52 | while (!path.isEmpty()) { 53 | s += ' - ' + path.pop(); 54 | } 55 | console.log(s); 56 | } 57 | 58 | console.log('********* topological sort - DFS ***********'); 59 | 60 | // let result = graph.DFS(); 61 | // console.log(result.discovery); 62 | // console.log(result.finished); 63 | // console.log(result.predecessors); 64 | 65 | graph = new Graph(); 66 | 67 | myVertices = ['A','B','C','D','E','F']; 68 | for (let i = 0; i < myVertices.length; i ++) { 69 | graph.addVertex(myVertices[i]); 70 | } 71 | graph.addEdge('A', 'C'); 72 | graph.addEdge('A', 'D'); 73 | graph.addEdge('B', 'D'); 74 | graph.addEdge('B', 'E'); 75 | graph.addEdge('C', 'F'); 76 | graph.addEdge('F', 'E'); 77 | 78 | 79 | let result = graph.DFS(); 80 | console.log(result.discovery); 81 | console.log(result.finished); 82 | console.log(result.predecessors); 83 | 84 | let fTimes = result.finished; 85 | let s = ''; 86 | for (let count = 0; count < myVertices.length; count ++) { 87 | let max = 0; 88 | let maxName = null; 89 | for (let i = 0; i < myVertices.length; i ++) { 90 | if (fTimes[myVertices[i]] > max) { 91 | max = fTimes[myVertices[i]]; 92 | maxName = myVertices[i]; 93 | } 94 | } 95 | s += ' - ' + maxName; 96 | delete fTimes[maxName]; 97 | } 98 | console.log(s); 99 | -------------------------------------------------------------------------------- /Graph/UsingMinimumSpanningTree.js: -------------------------------------------------------------------------------- 1 | var i; 2 | 3 | var graph = [[0, 2, 4, 0, 0, 0], 4 | [2, 0, 2, 4, 2, 0], 5 | [4, 2, 0, 0, 3, 0], 6 | [0, 4, 0, 0, 3, 2], 7 | [0, 2, 3, 3, 0, 2], 8 | [0, 0, 0, 2, 2, 0]]; 9 | 10 | var mst = new MinimumSpanningTree(graph); 11 | 12 | 13 | console.log("********* Prim's Algorithm - Minimum Spanning Tree ***********"); 14 | 15 | var parent = mst.prim(); 16 | 17 | console.log('Edge Weight'); 18 | for (i = 1; i < graph.length; i++){ 19 | console.log(parent[i] + ' - ' + i + ' ' + graph[i][parent[i]]); 20 | } 21 | 22 | console.log("********* Kruskal Algorithm - Minimum Spanning Tree ***********"); 23 | 24 | parent = mst.kruskal(); 25 | 26 | console.log('Edge Weight'); 27 | for (i = 1; i < graph.length; i++){ 28 | console.log(parent[i] + ' - ' + i + ' ' + graph[i][parent[i]]); 29 | } -------------------------------------------------------------------------------- /Graph/UsingShortestPathAlgorithms.js: -------------------------------------------------------------------------------- 1 | // adjacent matrix 2 | let graph = [[0, 2, 4, 0, 0, 0], 3 | [0, 0, 2, 4, 2, 0], 4 | [0, 0, 0, 0, 3, 0], 5 | [0, 0, 0, 0, 0, 2], 6 | [0, 0, 0, 3, 0, 2], 7 | [0, 0, 0, 0, 0, 0]]; 8 | 9 | let shortestPath = new ShortestPath(graph); 10 | 11 | console.log("********* Dijkstra's Algorithm - Shortest Path ***********"); 12 | 13 | let dist = shortestPath.dijkstra(0); 14 | 15 | for (let i = 0; i < dist.length; i ++) { 16 | console.log(i + '\t\t' + dist[i]); 17 | } 18 | 19 | console.log("********* Floyd-Warshall Algorithm - All-Pairs Shortest Path ***********"); 20 | 21 | let INF = Number.MAX_SAFE_INTEGER; 22 | graph = [[0, 2, 4, INF, INF, INF], 23 | [INF, 0, 2, 4, 2, INF], 24 | [INF, INF, 0, INF, 3, INF], 25 | [INF, INF, INF, 0, INF, 2], 26 | [INF, INF, INF, 3, 0, 2], 27 | [INF, INF, INF, INF, INF, 0]]; 28 | 29 | shortestPath = new ShortestPath(graph); 30 | 31 | dist = shortestPath.floydWarshall(); 32 | 33 | let s = ''; 34 | for (let i = 0; i < dist.length; ++ i) { 35 | s = ''; 36 | for (let j = 0; j < dist.length; ++ j) { 37 | if (dist[i][j] === INF) 38 | s += "INF "; 39 | else 40 | s += dist[i][j]+" "; 41 | } 42 | console.log(s); 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Learning JavaScript Data Structures and Algorithms 2 | ==================================== 3 | 4 | ### Base 5 | - 数组 ([Array](http://www.jianshu.com/p/5abc1b6044bd)) 6 | 7 | - 栈 (Stack) 8 | 9 | - 队列 (Queue) 10 | 11 | - 链表 (LinkedList) 12 | - 单链表 (LinkedList) 13 | - 双链表 (DoublyLinkedList) 14 | - 循环链表 (CircularLinkedList) 15 | - 判断链表是否成环 (LinkedListWithCycle) 16 | - 链表插入排序 (InsertionSort) 17 | - 链表快速排序 (QuickSort) 18 | - 链表归并排序 (MergeSort) 19 | - 集合 (Set) 20 | 21 | - 映射表(Map) 22 | - 字典 (Dictionary) 23 | - 散列表 (HashTable) 24 | - 分离链接 (HashCollisionSeparateChaining) 25 | - 线性探查 (HashCollisionLinearProbing) 26 | 27 | ### TopK 28 | - 找第 k 大的元素 (Quick_Select) 29 | - 找最大的 k 个元素 (Heap) 30 | - 优先队列 (PriorityQueue) 31 | 32 | ### Sort 33 | - 排序算法 (SortingAlgorithm) 34 | - 冒泡排序 (bubbleSort) 35 | - 选择排序 (selectionSort) 36 | - 插入排序 (insertionSort) 37 | - 归并排序 (mergeSort) 38 | - 快速排序 (quickSort) 39 | - 堆排序 (heapSort) 40 | - 希尔排序 (shellSort) 41 | - 计数排序 (countingSort) 42 | - 桶排序 (bucketSort) 43 | - 基数排序(分布式排序) (radixSort) 44 | 45 | ### String 46 | - 字符串匹配 (KMP) 47 | 48 | ### Tree 49 | - 二叉搜索树 (BinarySearchTree) 50 | - 迭代先序遍历、中序遍历、后序遍历 (OrderTraverse) 51 | - AVL树 (AVLTree) 52 | 1. BST 的扩展 53 | 1. 自平衡,任意节点的左右子树高度最多相差 1 54 | 2. 插入和移除节点性能并不总是最好的(虽然自平衡),更好的选择是红黑树 55 | - 红黑树 (RedBlackTree) 56 | 1. 可高效率有序地遍历其节点 57 | 58 | ### Graph 59 | - 最短路 (ShortestPath) 60 | - dijkstra 61 | - floydWarshall 62 | - 最小生成树 (MinimumSpanningTree) 63 | - prim 64 | - kruskal 65 | 66 | ### TODO 67 | - 优先队列 (PriorityQueue) 68 | - AVL树 (AVLTree) 69 | - 红黑树 (RedBlackTree) -------------------------------------------------------------------------------- /Sort/SortingAlgorithm.js: -------------------------------------------------------------------------------- 1 | function ArrayList() { 2 | 3 | let array = []; 4 | 5 | this.insert = function (item) { 6 | array.push(item); 7 | }; 8 | 9 | let swap = function (array, index1, index2) { 10 | let aux = array[index1]; 11 | array[index1] = array[index2]; 12 | array[index2] = aux; 13 | //ES2015 swap - Firefox only, for other browser, uncomment code above and coment line below 14 | //[array[index1], array[index2]] = [array[index2], array[index1]]; 15 | }; 16 | 17 | this.toString = function () { 18 | return array.join(); 19 | }; 20 | 21 | this.array = function () { 22 | return array; 23 | }; 24 | 25 | this.bubbleSort = function () { 26 | let length = array.length; 27 | 28 | for (let i = 0; i < length; i ++) { 29 | console.log('--- '); 30 | for (let j = 0; j < length - 1; j ++) { 31 | console.log('compare ' + array[j] + ' with ' + array[j + 1]); 32 | if (array[j] > array[j + 1]) { 33 | console.log('swap ' + array[j] + ' with ' + array[j + 1]); 34 | swap(array, j, j + 1); 35 | } 36 | } 37 | } 38 | }; 39 | 40 | this.modifiedBubbleSort = function () { 41 | let length = array.length; 42 | 43 | for (let i = 0; i < length; i ++) { 44 | console.log('--- '); 45 | for (let j = 0; j < length - 1 - i; j ++) { 46 | console.log('compare ' + array[j] + ' with ' + array[j + 1]); 47 | if (array[j] > array[j + 1]) { 48 | console.log('swap ' + array[j] + ' with ' + array[j + 1]); 49 | swap(array, j, j + 1); 50 | } 51 | } 52 | } 53 | 54 | }; 55 | 56 | this.selectionSort = function () { 57 | let length = array.length, 58 | indexMin; 59 | 60 | for (let i = 0; i < length - 1; i ++) { 61 | indexMin = i; 62 | console.log('index ' + array[i]); 63 | for (let j = i; j < length; j ++) { 64 | if (array[indexMin] > array[j]) { 65 | console.log('new index min ' + array[j]); 66 | indexMin = j; 67 | } 68 | } 69 | if (i !== indexMin) { 70 | console.log('swap ' + array[i] + ' with ' + array[indexMin]); 71 | swap(array, i, indexMin); 72 | } 73 | } 74 | }; 75 | 76 | this.insertionSort = function () { 77 | let length = array.length, 78 | j, temp; 79 | for (let i = 1; i < length; i ++) { 80 | j = i; 81 | temp = array[i]; 82 | console.log('to be inserted ' + temp); 83 | while (j > 0 && array[j - 1] > temp) { 84 | console.log('shift ' + array[j - 1]); 85 | array[j] = array[j - 1]; 86 | j--; 87 | } 88 | console.log('insert ' + temp); 89 | array[j] = temp; 90 | } 91 | }; 92 | 93 | // 插入排序在排序小型数组时性能不错,所以可以用来排序桶内的数 94 | let insertionSort_ = function (array) { 95 | let length = array.length, 96 | j, temp; 97 | for (let i = 1; i < length; i ++) { 98 | j = i; 99 | temp = array[i]; 100 | while (j > 0 && array[j - 1] > temp) { 101 | array[j] = array[j - 1]; 102 | j--; 103 | } 104 | array[j] = temp; 105 | } 106 | }; 107 | 108 | this.mergeSort = function () { 109 | array = mergeSortRec(array); 110 | }; 111 | 112 | let mergeSortRec = function (array) { 113 | 114 | let length = array.length; 115 | 116 | if (length === 1) { 117 | console.log(array); 118 | return array; 119 | } 120 | 121 | let mid = Math.floor(length / 2), 122 | left = array.slice(0, mid), 123 | right = array.slice(mid, length); 124 | 125 | return merge(mergeSortRec(left), mergeSortRec(right)); 126 | }; 127 | 128 | let merge = function (left, right) { 129 | let result = [], 130 | il = 0, 131 | ir = 0; 132 | 133 | while (il < left.length && ir < right.length) { 134 | 135 | if (left[il] < right[ir]) { 136 | result.push(left[il ++]); 137 | } else { 138 | result.push(right[ir ++]); 139 | } 140 | } 141 | 142 | while (il < left.length) { 143 | result.push(left[il ++]); 144 | } 145 | 146 | while (ir < right.length) { 147 | result.push(right[ir ++]); 148 | } 149 | 150 | console.log(result); 151 | 152 | return result; 153 | }; 154 | 155 | this.quickSort = function () { 156 | quick(array, 0, array.length - 1); 157 | }; 158 | 159 | // 创建两个指针分别指向数组第一项和最后一项 160 | // 移动左指针直到找到一个比 pivot 大的元素,接着移动右指针知道找到一个比 pivot 小的元素,然后 swap 161 | // 重复这个过程,直到左指针超过了右指针 162 | // 这时比 pivot 小的值都排在 pivot 之前;比 pivot 大的值都排在 pivot 之后 163 | let partition = function (array, left, right) { 164 | 165 | let pivot = array[Math.floor((right + left) / 2)], 166 | i = left, 167 | j = right; 168 | 169 | console.log('pivot is ' + pivot + '; left is ' + left + '; right is ' + right); 170 | 171 | while (i <= j) { 172 | while (array[i] < pivot) { 173 | i ++; 174 | console.log('i = ' + i); 175 | } 176 | 177 | while (array[j] > pivot) { 178 | j --; 179 | console.log('j = ' + j); 180 | } 181 | 182 | if (i <= j) { 183 | console.log('swap ' + array[i] + ' with ' + array[j]); 184 | swap(array, i, j); 185 | i ++; 186 | j --; 187 | } 188 | } 189 | 190 | return i; 191 | }; 192 | 193 | // 以 end 为 pivot, 初始化两个指针指向 begin, low左边的都是比 end 小的, 最后的 low 就是所求的 partition 194 | let partition2 = function (nums, begin, end) { 195 | let low = begin, l = begin, r = end; 196 | while (l < r) { 197 | if (nums[l] < nums[r]) { 198 | [nums[l], nums[low]] = [nums[low], nums[l]]; 199 | low ++; 200 | } 201 | l ++; 202 | } 203 | [nums[low], nums[r]] = [nums[r], nums[low]]; 204 | return low; 205 | }; 206 | 207 | let quick = function (array, left, right) { 208 | 209 | let index; 210 | 211 | if (array.length > 1) { 212 | 213 | index = partition(array, left, right); 214 | 215 | if (left < index - 1) { 216 | quick(array, left, index - 1); 217 | } 218 | 219 | if (index < right) { 220 | quick(array, index, right); 221 | } 222 | } 223 | return array; 224 | }; 225 | 226 | // 构建大根堆,升序 227 | this.heapSort = function () { 228 | let heapSize = array.length; 229 | 230 | buildHeap(array); 231 | 232 | while (heapSize > 1) { 233 | heapSize--; 234 | console.log('swap (' + +array[0] + ',' + array[heapSize] + ')'); 235 | swap(array, 0, heapSize); 236 | console.log('heapify ' + array.join()); 237 | heapify(array, heapSize, 0); 238 | } 239 | }; 240 | 241 | // 构建大根堆 242 | let buildHeap = function (array) { 243 | console.log('building heap'); 244 | let heapSize = array.length; 245 | for (let i = Math.floor(array.length / 2); i >= 0; i --) { 246 | heapify(array, heapSize, i); 247 | } 248 | console.log('heap created: ' + array.join()); 249 | }; 250 | 251 | let heapify = function (array, heapSize, i) { 252 | let left = i * 2 + 1, 253 | right = i * 2 + 2, 254 | largest = i; 255 | 256 | if (left < heapSize && array[left] > array[largest]) { 257 | largest = left; 258 | } 259 | 260 | if (right < heapSize && array[right] > array[largest]) { 261 | largest = right; 262 | } 263 | 264 | console.log('Heapify Index = ' + i + ' and Heap Size = ' + heapSize); 265 | 266 | if (largest !== i) { 267 | console.log('swap index ' + i + ' with ' + largest + ' (' + +array[i] + ',' + array[largest] + ')'); 268 | swap(array, i, largest); 269 | console.log('heapify ' + array.join()); 270 | heapify(array, heapSize, largest); 271 | } 272 | }; 273 | 274 | // 希尔排序 275 | // 该方法的基本思想是: 276 | // 先将整个待排元素序列分割成若干个子序列(由相隔某个"增量"的元素组成的)分别进行直接插入排序, 277 | // 然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。 278 | // 因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。 279 | this.shellSort = function () { 280 | let n = array.length; 281 | for (let gap = parseInt(n / 2); gap > 0; gap = parseInt(gap / 2)) 282 | for (let i = gap; i < n; i ++) 283 | for (let j = i - gap; j >= 0 && array[j] > array[j + gap]; j -= gap) 284 | swap(array, j, j + gap); 285 | }; 286 | 287 | // 计数排序 288 | this.countingSort = function () { 289 | 290 | let i, 291 | maxValue = this.findMaxValue(), 292 | sortedIndex = 0, 293 | counts = new Array(maxValue + 1); 294 | 295 | for (i = 0; i < array.length; i ++) { 296 | if (!counts[array[i]]) { 297 | counts[array[i]] = 0; 298 | } 299 | counts[array[i]] ++; 300 | } 301 | 302 | console.log('Frequencies: ' + counts.join()); 303 | 304 | for (i = 0; i < counts.length; i ++) { 305 | while (counts[i] > 0) { 306 | array[sortedIndex ++] = i; 307 | counts[i] --; 308 | } 309 | } 310 | }; 311 | 312 | this.bucketSort = function (bucketSize) { 313 | 314 | let i, 315 | minValue = this.findMinValue(), 316 | maxValue = this.findMaxValue(), 317 | BUCKET_SIZE = 5; 318 | 319 | console.log('minValue ' + minValue); 320 | console.log('maxValue ' + maxValue); 321 | 322 | bucketSize = bucketSize || BUCKET_SIZE; 323 | let bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1; 324 | let buckets = new Array(bucketCount); 325 | console.log('bucketSize = ' + bucketCount); 326 | for (i = 0; i < buckets.length; i ++) { 327 | buckets[i] = []; 328 | } 329 | 330 | for (i = 0; i < array.length; i ++) { 331 | buckets[Math.floor((array[i] - minValue) / bucketSize)].push(array[i]); 332 | console.log('pushing item ' + array[i] + ' to bucket index ' + Math.floor((array[i] - minValue) / bucketSize)); 333 | } 334 | 335 | array = []; 336 | for (i = 0; i < buckets.length; i ++) { 337 | insertionSort_(buckets[i]); 338 | 339 | console.log('bucket sorted ' + i + ': ' + buckets[i].join()); 340 | 341 | for (let j = 0; j < buckets[i].length; j ++) { 342 | array.push(buckets[i][j]); 343 | } 344 | } 345 | }; 346 | 347 | // 基数排序(分布式排序) 348 | this.radixSort = function (radixBase) { 349 | 350 | let i, 351 | minValue = this.findMinValue(), 352 | maxValue = this.findMaxValue(); 353 | 354 | radixBase = radixBase || 10; 355 | 356 | // 和两个数比大小一样,从个位开始排序 357 | let significantDigit = 1; 358 | while (((maxValue - minValue) / significantDigit) >= 1) { 359 | console.log('radix sort for digit ' + significantDigit); 360 | array = countingSortForRadix(array, radixBase, significantDigit, minValue); 361 | console.log(array.join()); 362 | significantDigit *= radixBase; 363 | } 364 | }; 365 | 366 | let countingSortForRadix = function (array, radixBase, significantDigit, minValue) { 367 | let i, countsIndex, 368 | counts = new Array(radixBase), 369 | aux = new Array(radixBase); 370 | 371 | for (i = 0; i < radixBase; i ++) { 372 | counts[i] = 0; 373 | } 374 | 375 | for (i = 0; i < array.length; i ++) { 376 | countsIndex = Math.floor(((array[i] - minValue) / significantDigit) % radixBase); 377 | counts[countsIndex] ++; 378 | } 379 | 380 | for (i = 1; i < radixBase; i ++) { 381 | counts[i] += counts[i - 1]; 382 | } 383 | 384 | // 保证稳定排序 385 | for (i = array.length - 1; i >= 0; i --) { 386 | countsIndex = Math.floor(((array[i] - minValue) / significantDigit) % radixBase); 387 | aux[-- counts[countsIndex]] = array[i]; 388 | } 389 | 390 | for (i = 0; i < array.length; i ++) { 391 | array[i] = aux[i]; 392 | } 393 | 394 | return array; 395 | }; 396 | 397 | this.findMaxValue = function() { 398 | let max = array[0]; 399 | for (let i = 1; i < array.length; i ++) { 400 | if (max < array[i]){ 401 | max = array[i]; 402 | } 403 | } 404 | 405 | return max; 406 | }; 407 | 408 | this.findMinValue = function() { 409 | let min = array[0]; 410 | for (let i = 1; i < array.length; i ++) { 411 | if (min > array[i]){ 412 | min = array[i]; 413 | } 414 | } 415 | 416 | return min; 417 | }; 418 | } 419 | 420 | module.exports = ArrayList; -------------------------------------------------------------------------------- /Sort/UsingSortingAlgorithms.js: -------------------------------------------------------------------------------- 1 | let ArrayList = require('./SortingAlgorithm.js'); 2 | 3 | function createNonSortedArray(size){ 4 | let array = new ArrayList(); 5 | 6 | for (let i = size; i> 0; i--){ 7 | array.insert(i); 8 | } 9 | 10 | return array; 11 | } 12 | 13 | function createRandomNonSortedArray(){ 14 | let array = new ArrayList(); 15 | 16 | array.insert(3); 17 | array.insert(5); 18 | array.insert(1); 19 | array.insert(6); 20 | array.insert(4); 21 | array.insert(7); 22 | array.insert(2); 23 | 24 | return array; 25 | } 26 | 27 | function printArray(array){ 28 | console.log(array.toString()); 29 | } 30 | 31 | function createNonSortedArrayAndPrint(size){ 32 | let array = createNonSortedArray(size); 33 | printArray(array); 34 | 35 | return array; 36 | } 37 | 38 | console.log('********** Bubble Sort **********'); 39 | 40 | let array = createNonSortedArrayAndPrint(5); 41 | 42 | array.bubbleSort(); 43 | 44 | printArray(array); 45 | 46 | console.log('********** Modified Bubble Sort **********'); 47 | 48 | array = createNonSortedArrayAndPrint(5); 49 | 50 | array.modifiedBubbleSort(); 51 | 52 | printArray(array); 53 | 54 | console.log('********** Selection Sort **********'); 55 | 56 | array = createNonSortedArrayAndPrint(5); 57 | 58 | array.selectionSort(); 59 | 60 | printArray(array); 61 | 62 | console.log('********** Insertion Sort **********'); 63 | 64 | array = createNonSortedArrayAndPrint(5); 65 | 66 | array.insertionSort(); 67 | 68 | printArray(array); 69 | 70 | console.log('********** Merge Sort **********'); 71 | 72 | array = createNonSortedArrayAndPrint(8); 73 | 74 | array.mergeSort(); 75 | 76 | printArray(array); 77 | 78 | console.log('********** Quick Sort **********'); 79 | array = createRandomNonSortedArray(); 80 | 81 | printArray(array); 82 | 83 | array.quickSort(); 84 | 85 | printArray(array); 86 | 87 | console.log('********** Heap Sort **********'); 88 | array = createRandomNonSortedArray(); 89 | 90 | printArray(array); 91 | 92 | array.heapSort(); 93 | 94 | printArray(array); 95 | 96 | console.log('********** Shell Sort **********'); 97 | array = createRandomNonSortedArray(); 98 | 99 | printArray(array); 100 | 101 | array.shellSort(); 102 | 103 | printArray(array); 104 | 105 | console.log('********** Counting Sort **********'); 106 | 107 | array = createNonSortedArrayAndPrint(8); 108 | 109 | array.countingSort(); 110 | 111 | printArray(array); 112 | 113 | console.log('********** Bucket Sort **********'); 114 | 115 | array = createNonSortedArrayAndPrint(8); 116 | 117 | array.bucketSort(3); 118 | 119 | printArray(array); 120 | 121 | console.log('********** Radix Sort **********'); 122 | 123 | array = new ArrayList(); 124 | 125 | array.insert(30); 126 | array.insert(52); 127 | array.insert(13); 128 | array.insert(25); 129 | array.insert(31); 130 | array.insert(23); 131 | array.insert(2); 132 | 133 | array.radixSort(); 134 | 135 | console.log('Result: '); 136 | printArray(array); 137 | -------------------------------------------------------------------------------- /String/KMP.js: -------------------------------------------------------------------------------- 1 | let A = 'hello'; 2 | let B = 'll'; 3 | // let B = 'a'; 4 | 5 | 6 | 7 | // getNext P[i]: 满足B[0 ... P[i] - 1] = B[i - P[i] + 1 ... i]的最大值(即匹配值),P[i] = 0时,表示匹配值为 0,不能再倒退 8 | let j = 0, P = []; 9 | P[0] = 0; 10 | for (let i = 1; i < B.length; i ++) { 11 | while (j && B[j] !== B[i]) j = P[j - 1]; 12 | if (B[i] === B[j]) j ++; 13 | P[i] = j; 14 | } 15 | 16 | console.log(P); 17 | 18 | // j表示已经匹配的长度,即A[i - j + 1 ... i] = B[0 ... j - 1],如果匹配失败,倒退到P[j - 1] 19 | j = 0; 20 | for (let i = 0; i < A.length; i ++) { 21 | while (j && A[i] !== B[j]) j = P[j - 1]; 22 | if (A[i] === B[j]) j ++; 23 | if (j === B.length) { 24 | console.log('Pattern occurs with shift ', i - B.length + 1); 25 | j = P[j - 1]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /String/README.md: -------------------------------------------------------------------------------- 1 | 字符串算法 2 | ==================================== 3 | 4 | - [kmp](http://www.jianshu.com/p/dc99046a77c3) 5 | 6 | -------------------------------------------------------------------------------- /TopK/Heap.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @name 最大堆的构造函数 4 | * @param {number} capacity - 容量 5 | */ 6 | function MaxHeap(capacity) { 7 | if (capacity) { // 总的容量 8 | this.capacity = capacity; 9 | } else { 10 | this.capacity = 30; 11 | } 12 | this.heap = []; // 堆化数组 13 | } 14 | 15 | /** 16 | * @name 最大堆的向下调整算法:当从最大堆中删除数据时,先删除该数据,然后用最大堆中最后一个的元素插入这个空位;接着,把这个空位尽量往上挪,直到剩余的数据变成一个最大堆 17 | * @param {number} current - 被下调节点的起始位置(一般为0,表示从第1个开始) 18 | * @param {number} end - 截至范围(一般为数组中最后一个元素的索引) 19 | */ 20 | MaxHeap.prototype.filterDown = function(current, end) { 21 | let l = 2 * current + 1; // 左孩子的位置 22 | let temp = this.heap[current]; 23 | 24 | while (l <= end) { 25 | // "l"是左孩子,"l+1"是右孩子 26 | if (l < end && this.heap[l] < this.heap[l + 1]) 27 | l ++; // 左右两孩子中选择较大者,即this.heap[l+1] 28 | if (temp >= this.heap[l]) 29 | break; // 调整结束 30 | else { 31 | this.heap[current] = this.heap[l]; 32 | current = l; 33 | l = 2 * l + 1; 34 | } 35 | } 36 | this.heap[current] = temp; 37 | }; 38 | 39 | /** 40 | * @name 删除最大堆中的data 41 | * @param {Object} data - 删除数据 42 | * @return {number} -1/0 - 失败/成功 43 | */ 44 | MaxHeap.prototype.remove = function(data) { 45 | // 如果"堆"已空,则返回-1 46 | if (this.heap.length === 0) 47 | return -1; 48 | 49 | let index = this.getIndex(data); 50 | if (index === -1) 51 | return -1; 52 | 53 | this.heap[index] = this.heap[this.heap.length - 1]; // 用最后元素填补 54 | this.heap.length --; 55 | this.filterDown(index, this.heap.length - 1); // 从index位置开始自上向下调整为最大堆 56 | 57 | return 0; 58 | }; 59 | 60 | /** 61 | * @name 最大堆的向上调整算法:将数据data添加到最大堆中。当堆已满的时候,添加失败;否则data添加到最大堆的末尾。然后通过上调算法重新调整数组,使之重新成为最大堆 62 | * @param {number} current - 当前节点的位置 63 | */ 64 | MaxHeap.prototype.filterUp = function(current) { 65 | let parent = parseInt((current - 1) / 2); 66 | let temp = this.heap[current]; 67 | 68 | while (current > 0) { 69 | if (this.heap[parent] >= temp) 70 | break; 71 | else { 72 | this.heap[current] = this.heap[parent]; 73 | current = parent; 74 | parent = parseInt((parent - 1) / 2); 75 | } 76 | } 77 | this.heap[current] = temp; 78 | }; 79 | 80 | /** 81 | * @name 将data插入到二叉堆中 82 | * @param {Object} data - 插入数据 83 | * @return {number} -1/0 - 失败/成功 84 | */ 85 | MaxHeap.prototype.insert = function(data) { 86 | // 如果"堆"已满,则返回 87 | if (this.heap.length === this.capacity) 88 | return -1; 89 | 90 | this.heap[this.heap.length] = data; // 将"数组"插在表尾 91 | this.filterUp(this.heap.length - 1); // 向上调整堆 92 | 93 | return 0; 94 | }; 95 | 96 | /** 97 | * @name 返回data在二叉堆中的索引 98 | * @param {Object} data - 插入数据 99 | * @return {number} -1/i - 不存在/data在堆化数组中的索引 100 | */ 101 | MaxHeap.prototype.getIndex = function(data) { 102 | for (let i = 0; i < this.heap.length; i ++) { 103 | if (data === this.heap[i]) 104 | return i; 105 | } 106 | return -1; 107 | }; 108 | 109 | /** 110 | * @name 打印二叉堆 111 | */ 112 | MaxHeap.prototype.print = function() { 113 | console.log(this.heap); 114 | }; 115 | 116 | 117 | /** 118 | * @name 测试最大堆 119 | */ 120 | function test() { 121 | let maxHeap = new MaxHeap(); 122 | let a = [10, 40, 30, 60, 90, 70, 20, 50, 80]; 123 | 124 | console.log("== 依次添加: "); 125 | for(let i = 0; i < a.length; i ++) { 126 | process.stdout.write(a[i] + ' '); 127 | maxHeap.insert(a[i]); 128 | } 129 | console.log("\n== 最 大 堆: "); 130 | maxHeap.print(); 131 | let i = 85; 132 | maxHeap.insert(i); 133 | console.log("\n== 添加元素: ", i); 134 | console.log("\n== 最 大 堆: "); 135 | maxHeap.print(); 136 | i = 90; 137 | maxHeap.remove(i); 138 | console.log("\n== 删除元素: ", i); 139 | console.log("\n== 最 大 堆: "); 140 | maxHeap.print(); 141 | } 142 | 143 | test(); 144 | -------------------------------------------------------------------------------- /TopK/PriorityQueue.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @name 优先队列的构造函数 4 | * @param {number} capacity - 容量 5 | */ 6 | function PriorityQueue(capacity) { 7 | if (capacity) { // 总的容量 8 | this.capacity = capacity; 9 | } else { 10 | this.capacity = 30; 11 | } 12 | this.queue = []; // 队列 13 | } 14 | 15 | /** 16 | * @name 优先队列的向下调整算法:当从优先队列中删除数据时,先删除该数据,然后用优先队列中最后一个的元素插入这个空位;接着,把这个空位尽量往上挪,直到剩余的数据变成一个优先队列 17 | * @param {number} current - 被下调节点的起始位置(一般为0,表示从第1个开始) 18 | * @param {number} end - 截至范围(一般为数组中最后一个元素的索引) 19 | */ 20 | PriorityQueue.prototype.filterDown = function(current, end) { 21 | let l = 2 * current + 1; // 左孩子的位置 22 | let temp = this.queue[current]; 23 | 24 | while (l <= end) { 25 | // "l"是左孩子,"l+1"是右孩子 26 | if (l < end && this.queue[l] < this.queue[l + 1]) 27 | l ++; // 左右两孩子中选择较大者,即this.queue[l+1] 28 | if (temp >= this.queue[l]) 29 | break; // 调整结束 30 | else { 31 | this.queue[current] = this.queue[l]; 32 | current = l; 33 | l = 2 * l + 1; 34 | } 35 | } 36 | this.queue[current] = temp; 37 | }; 38 | 39 | /** 40 | * @name 获取并删除优先队列队首元素 41 | * @return {Object} - 返回队首元素或空 42 | */ 43 | PriorityQueue.prototype.remove = function() { 44 | // 如果队列已空,则返回null 45 | if (this.queue.length === 0) 46 | return null; 47 | 48 | let ret = this.queue[0]; 49 | 50 | this.queue[0] = this.queue[this.queue.length - 1]; // 用最后元素填补 51 | this.queue.length --; 52 | this.filterDown(0, this.queue.length - 1); // 从队首开始自上向下调整为优先队列 53 | 54 | return ret; 55 | }; 56 | 57 | /** 58 | * @name 优先队列的向上调整算法:将数据data添加到优先队列中。当队列已满的时候,添加失败;否则data添加到优先队列的末尾。然后通过上调算法重新调整队列,使之重新成为优先队列 59 | * @param {number} current - 当前节点的位置 60 | */ 61 | PriorityQueue.prototype.filterUp = function(current) { 62 | let parent = parseInt((current - 1) / 2); 63 | let temp = this.queue[current]; 64 | 65 | while (current > 0) { 66 | if (this.queue[parent] >= temp) 67 | break; 68 | else { 69 | this.queue[current] = this.queue[parent]; 70 | current = parent; 71 | parent = parseInt((parent - 1) / 2); 72 | } 73 | } 74 | this.queue[current] = temp; 75 | }; 76 | 77 | /** 78 | * @name 将data插入到优先队列中 79 | * @param {Object} data - 插入数据 80 | * @return {number} -1/0 - 失败/成功 81 | */ 82 | PriorityQueue.prototype.add = function(data) { 83 | // 如果队列已满,则返回 84 | if (this.queue.length === this.capacity) 85 | return -1; 86 | 87 | this.queue[this.queue.length] = data; // 将数据插在表尾 88 | this.filterUp(this.queue.length - 1); // 向上调整队列 89 | 90 | return 0; 91 | }; 92 | 93 | /** 94 | * @name 获取但不删除队首元素 95 | * @return {Object} - 返回队首元素或空 96 | */ 97 | PriorityQueue.prototype.peek = function() { 98 | if (this.queue.length === 0) 99 | return null; 100 | return this.queue[0]; 101 | }; 102 | 103 | /** 104 | * @name 打印优先队列 105 | */ 106 | PriorityQueue.prototype.print = function() { 107 | console.log(this.queue); 108 | }; 109 | 110 | 111 | /** 112 | * @name 测试优先队列 113 | */ 114 | function test() { 115 | let priorityQueue = new PriorityQueue(); 116 | let a = [10, 40, 30, 60, 90, 70, 20, 50, 80]; 117 | 118 | console.log("== 依次添加: "); 119 | for(let i = 0; i < a.length; i ++) { 120 | process.stdout.write(a[i] + ' '); 121 | priorityQueue.add(a[i]); 122 | } 123 | console.log("\n== 优先队列: "); 124 | priorityQueue.print(); 125 | 126 | console.log("\n== 队首: ", priorityQueue.peek()); 127 | 128 | let i = 85; 129 | priorityQueue.add(i); 130 | console.log("\n== 添加元素: ", i); 131 | console.log("\n== 优先队列: "); 132 | priorityQueue.print(); 133 | 134 | let element = priorityQueue.remove(); 135 | console.log("\n== 删除元素: ", element); 136 | console.log("\n== 优先队列: "); 137 | priorityQueue.print(); 138 | } 139 | 140 | test(); 141 | -------------------------------------------------------------------------------- /TopK/Quick_Select.js: -------------------------------------------------------------------------------- 1 | 2 | var findKthLargest = function(nums, k) { 3 | return findKthSmallest(nums, 0, nums.length - 1, nums.length + 1 - k); 4 | }; 5 | 6 | function findKthSmallest(nums, begin, end, k) { 7 | let pivot = partition(nums, begin, end); 8 | if (pivot + 1 === k) { 9 | return nums[pivot]; 10 | } else if (pivot + 1 > k) { 11 | return findKthSmallest(nums, begin, pivot - 1, k); 12 | } else { 13 | return findKthSmallest(nums, pivot + 1, end, k); 14 | } 15 | } 16 | 17 | // 以 end 为 pivot 18 | function partition(nums, begin, end) { 19 | let low = begin, l = begin, r = end; 20 | while (l < r) { 21 | if (nums[l] < nums[r]) { 22 | [nums[l], nums[low]] = [nums[low], nums[l]]; 23 | low ++; 24 | } 25 | l ++; 26 | } 27 | [nums[low], nums[r]] = [nums[r], nums[low]]; 28 | return low; 29 | } 30 | 31 | // console.log(findKthLargest([3,2,1,5,6,4], 2)); 32 | 33 | -------------------------------------------------------------------------------- /TopK/README.md: -------------------------------------------------------------------------------- 1 | # 在数组中找到第k大的元素 2 | ------------ 3 | 4 | ### Quick Select 5 | 6 | #### 时间复杂度 7 | 完整的平均时间复杂度分析非常复杂,在这里不再赘述。有兴趣的可以看算导。 8 | 9 | #####理想状况 10 | 11 | 假如每次都平分,这样时间复杂度展开是 O(n + n/2 + n/4 + ... .+1) = O(2n) = O(n)。 12 | 13 | 因为算法是,挑选一个 pivot 之后,O(n) 时间进行 partition,然后判断一下第 k 大在左边还是在右边。然后继续往下进行,那么理想情况下,无论去左边还是去右边,规模都从 n 变成了 n/2。 14 | 15 | T(n) = O(n) + T(n/2) 16 | 17 | T(n/2) = O(n/2) + T(n/4) 18 | 19 | ... 20 | 21 | T(2) = O(1) + T(1) 22 | 23 | T(1) = O(1) 24 | 25 | 所以 T(n) = T(n/2) + O(n) = O(n). 26 | 27 | ##### 不理想情况 28 | 运气很不好的话,每次的 pivot 都取到了最大或者最小的值,那么复杂度展开是 O(n + n-1 + n-2 + n-3 ... + 1) = O(n^2)。 29 | 30 | 那么我们应该怎么衡量这个算法的时间复杂度呢? 31 | 32 | 类似快速排序我们认为是 O(nlogn) 的,因为我们算的是平均时间复杂度,也就是平摊一下各种情况的概率和时间复杂度。 33 | 34 | 因为并不一定每次都运气那么不好,比如你用随机的方式挑选 pivot 的话,这样我们认为 quick select 的时间复杂度均摊是 O(n) 的。 35 | 36 | 37 | #### 空间复杂度 38 | 39 | 算法没有使用额外空间,所以算法的空间复杂度为 O(1)。 40 | 41 | 42 | top k(找最大的k个数) 43 | ------------ 44 | 堆 45 | 46 | 二叉堆是完全二元树或者是近似完全二元树,按照数据的排列方式可以分为两种:最大堆和最小堆。 47 | 48 | 最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。 49 | 50 | 优先队列 -------------------------------------------------------------------------------- /Tree/AVLTree.js: -------------------------------------------------------------------------------- 1 | function AVLTree() { 2 | 3 | let Node = function(key){ 4 | this.key = key; 5 | this.left = null; 6 | this.right = null; 7 | }; 8 | 9 | let root = null; 10 | 11 | this.getRoot = function(){ 12 | return root; 13 | }; 14 | 15 | let heightNode = function(node) { 16 | if (node === null) { 17 | return -1; 18 | } else { 19 | return Math.max(heightNode(node.left), heightNode(node.right)) + 1; 20 | } 21 | }; 22 | 23 | // 向右的单旋转 24 | let rotationLL = function(node) { 25 | let tmp = node.left; 26 | node.left = tmp.right; 27 | tmp.right = node; 28 | 29 | return tmp; 30 | }; 31 | 32 | // 向左的单旋转 33 | let rotationRR = function(node) { 34 | let tmp = node.right; 35 | node.right = tmp.left; 36 | tmp.left = node; 37 | 38 | return tmp; 39 | }; 40 | 41 | // 向右的双旋转 42 | let rotationLR = function(node) { 43 | node.left = rotationRR(node.left); 44 | return rotationLL(node); 45 | }; 46 | 47 | // 向左的双旋转 48 | let rotationRL = function(node) { 49 | node.right = rotationLL(node.right); 50 | return rotationRR(node); 51 | }; 52 | 53 | let insertNode = function(node, element) { 54 | 55 | if (node === null) { 56 | node = new Node(element); 57 | 58 | } else if (element < node.key) { 59 | 60 | node.left = insertNode(node.left, element); 61 | 62 | if (node.left !== null) { 63 | // 确认是否需要平衡 64 | if ((heightNode(node.left) - heightNode(node.right)) > 1){ 65 | // 向左子树插入新节点,且节点值小于其左子节点时,应进行LL旋转。否则,进行LR旋转 66 | if (element < node.left.key){ 67 | node = rotationLL(node); 68 | } else { 69 | node = rotationLR(node); 70 | } 71 | } 72 | } 73 | } else if (element > node.key) { 74 | 75 | node.right = insertNode(node.right, element); 76 | 77 | if (node.right !== null) { 78 | // 确认是否需要平衡 79 | if ((heightNode(node.right) - heightNode(node.left)) > 1){ 80 | // 向右子树插入新节点,且节点值大于其右子节点时,应进行RR旋转。否则,进行RL旋转 81 | if (element > node.right.key){ 82 | node = rotationRR(node); 83 | } else { 84 | node = rotationRL(node); 85 | } 86 | } 87 | } 88 | } 89 | 90 | return node; 91 | }; 92 | 93 | this.insert = function(element) { 94 | root = insertNode(root, element); 95 | }; 96 | 97 | let parentNode; 98 | let nodeToBeDeleted; 99 | 100 | let removeNode = function(node, element) { 101 | if (node === null) { 102 | return null; 103 | } 104 | parentNode = node; 105 | 106 | if (element < node.key) { 107 | node.left = removeNode(node.left, element); 108 | } else { 109 | nodeToBeDeleted = node; 110 | node.right = removeNode(node.right, element); 111 | } 112 | 113 | if (node === parentNode) { //remove node 114 | if (nodeToBeDeleted !== null && element === nodeToBeDeleted.key) { 115 | if (nodeToBeDeleted === parentNode) { 116 | node = node.left; 117 | } else { 118 | let tmp = nodeToBeDeleted.key; 119 | nodeToBeDeleted.key = parentNode.key; 120 | parentNode.key = tmp; 121 | node = node.right; 122 | } 123 | } 124 | } else { //do balancing 125 | 126 | if (node.left === undefined) node.left = null; 127 | if (node.right === undefined) node.right = null; 128 | 129 | if ((heightNode(node.left) - heightNode(node.right)) === 2) { 130 | if (element < node.left.key) { 131 | node = rotationLR(node); 132 | } else { 133 | node = rotationLL(node); 134 | } 135 | } 136 | 137 | if ((heightNode(node.right) - heightNode(node.left)) === 2) { 138 | if (element > node.right.key) { 139 | node = rotationRL(node); 140 | } else { 141 | node = rotationRR(node); 142 | } 143 | } 144 | } 145 | 146 | return node; 147 | }; 148 | 149 | this.remove = function(element) { 150 | parentNode = null; 151 | nodeToBeDeleted = null; 152 | root = removeNode(root, element); 153 | }; 154 | } -------------------------------------------------------------------------------- /Tree/BinarySearchTree.js: -------------------------------------------------------------------------------- 1 | function BinarySearchTree() { 2 | 3 | let Node = function(key) { 4 | this.key = key; 5 | this.left = null; 6 | this.right = null; 7 | }; 8 | 9 | let root = null; 10 | 11 | this.insert = function(key) { 12 | 13 | let newNode = new Node(key); 14 | 15 | //special case - first element 16 | if (root === null) { 17 | root = newNode; 18 | } else { 19 | insertNode(root, newNode); 20 | } 21 | }; 22 | 23 | let insertNode = function(root, newNode) { 24 | if (newNode.key < root.key) { 25 | if (root.left === null) { 26 | root.left = newNode; 27 | } else { 28 | insertNode(root.left, newNode); 29 | } 30 | } else { 31 | if (root.right === null) { 32 | root.right = newNode; 33 | } else { 34 | insertNode(root.right, newNode); 35 | } 36 | } 37 | }; 38 | 39 | this.getRoot = function() { 40 | return root; 41 | }; 42 | 43 | this.search = function(key) { 44 | 45 | return searchNode(root, key); 46 | }; 47 | 48 | let searchNode = function(node, key) { 49 | 50 | if (node === null) { 51 | return false; 52 | } 53 | 54 | if (key < node.key) { 55 | return searchNode(node.left, key); 56 | 57 | } else if (key > node.key) { 58 | return searchNode(node.right, key); 59 | 60 | } else { //element is equal to node.item 61 | return true; 62 | } 63 | }; 64 | 65 | this.inOrderTraverse = function(callback) { 66 | inOrderTraverseNode(root, callback); 67 | }; 68 | 69 | let inOrderTraverseNode = function (root, callback) { 70 | if (root !== null) { 71 | inOrderTraverseNode(root.left, callback); 72 | callback(root.key); 73 | inOrderTraverseNode(root.right, callback); 74 | } 75 | }; 76 | 77 | this.preOrderTraverse = function(callback){ 78 | preOrderTraverseNode(root, callback); 79 | }; 80 | 81 | let preOrderTraverseNode = function (root, callback) { 82 | if (root !== null) { 83 | callback(root.key); 84 | preOrderTraverseNode(root.left, callback); 85 | preOrderTraverseNode(root.right, callback); 86 | } 87 | }; 88 | 89 | this.postOrderTraverse = function(callback) { 90 | postOrderTraverseNode(root, callback); 91 | }; 92 | 93 | let postOrderTraverseNode = function (root, callback) { 94 | if (root !== null) { 95 | postOrderTraverseNode(root.left, callback); 96 | postOrderTraverseNode(root.right, callback); 97 | callback(root.key); 98 | } 99 | }; 100 | 101 | this.min = function() { 102 | return minNode(root); 103 | }; 104 | 105 | let minNode = function (root) { 106 | if (root) { 107 | while (root && root.left !== null) { 108 | root = root.left; 109 | } 110 | 111 | return root.key; 112 | } 113 | return null; 114 | }; 115 | 116 | this.max = function() { 117 | return maxNode(root); 118 | }; 119 | 120 | let maxNode = function (root) { 121 | if (root){ 122 | while (root && root.right !== null) { 123 | root = root.right; 124 | } 125 | 126 | return root.key; 127 | } 128 | return null; 129 | }; 130 | 131 | this.remove = function(element) { 132 | root = removeNode(root, element); 133 | }; 134 | 135 | let findMinNode = function(root) { 136 | while (root && root.left !== null) { 137 | root = root.left; 138 | } 139 | 140 | return root; 141 | }; 142 | 143 | let removeNode = function(root, element) { 144 | 145 | if (root === null) { 146 | return null; 147 | } 148 | 149 | if (element < root.key) { 150 | root.left = removeNode(root.left, element); 151 | return root; 152 | 153 | } else if (element > root.key) { 154 | root.right = removeNode(root.right, element); 155 | return root; 156 | 157 | } else { // element is equal to node.item 158 | 159 | // handle 3 special conditions 160 | // 1 - a leaf node 161 | // 2 - a node with only 1 child 162 | // 3 - a node with 2 children 163 | 164 | // case 1 165 | if (root.left === null && root.right === null) { 166 | root = null; 167 | return root; 168 | } 169 | 170 | // case 2 171 | if (root.left === null) { 172 | root = root.right; 173 | return root; 174 | 175 | } else if (root.right === null) { 176 | root = root.left; 177 | return root; 178 | } 179 | 180 | // case 3 181 | // 将该节点替换为右子树最小的节点 182 | let aux = findMinNode(root.right); 183 | root.key = aux.key; 184 | root.right = removeNode(root.right, aux.key); 185 | return root; 186 | } 187 | }; 188 | } 189 | 190 | module.exports = BinarySearchTree; -------------------------------------------------------------------------------- /Tree/OrderTraverse.js: -------------------------------------------------------------------------------- 1 | let BinarySearchTree = require('./BinarySearchTree.js'); 2 | 3 | let tree = new BinarySearchTree(); 4 | 5 | tree.insert(11); 6 | tree.insert(7); 7 | tree.insert(15); 8 | tree.insert(5); 9 | tree.insert(3); 10 | tree.insert(9); 11 | tree.insert(8); 12 | tree.insert(10); 13 | tree.insert(13); 14 | tree.insert(12); 15 | tree.insert(14); 16 | tree.insert(20); 17 | tree.insert(18); 18 | tree.insert(25); 19 | tree.insert(6); 20 | 21 | function printNode(node) { 22 | process.stdout.write(node.key.toString() + ' '); 23 | } 24 | 25 | 26 | // 基本思想:用栈模拟函数递归时的行为,没轮到的时候先存到栈里 27 | 28 | 29 | function inOrderTraverse(root, callback) { 30 | if (!root) return; 31 | let nodes = [], cur = root, stack = []; 32 | while (stack.length || cur) { 33 | if (cur) { 34 | stack.push(cur); 35 | cur = cur.left; 36 | } else { 37 | cur = stack.pop(); 38 | nodes.push(cur); 39 | cur = cur.right; 40 | } 41 | } 42 | console.log('********* in-order transverse ***********'); 43 | for (let i = 0; i < nodes.length; i ++) 44 | callback(nodes[i]); 45 | console.log(); 46 | } 47 | 48 | function preOrderTraverse(root, callback) { 49 | if (!root) return; 50 | let nodes = [], cur = root, stack = []; 51 | while (stack.length || cur) { 52 | if (cur) { 53 | nodes.push(cur); 54 | stack.push(cur); 55 | cur = cur.left; 56 | } else { 57 | cur = stack.pop().right; 58 | } 59 | } 60 | console.log('********* pre-order transverse ***********'); 61 | for (let i = 0; i < nodes.length; i ++) 62 | callback(nodes[i]); 63 | console.log(); 64 | } 65 | 66 | // 先序输出的时候是【根左右】,后序输出是【左右根】 67 | // 所以只需要按照先序遍历非递归的方法,输出的时候改改顺序即可 68 | // 按【根右左】的顺序push到栈中,然后倒序输出 69 | function postOrderTraverse(root, callback) { 70 | if (!root) return; 71 | let nodes = [], cur = root, stack = []; 72 | while (stack.length || cur) { 73 | if (cur) { 74 | stack.push(cur); 75 | nodes.push(cur); 76 | cur = cur.right; 77 | } else { 78 | cur = stack.pop().left; 79 | } 80 | } 81 | nodes = nodes.reverse(); 82 | console.log('********* post-order transverse ***********'); 83 | for (let i = 0; i < nodes.length; i ++) 84 | callback(nodes[i]); 85 | console.log(); 86 | } 87 | 88 | // 按上面的思路改进:用栈实现先根后右左的处理 89 | // 因为左必须在右前,所以想到先跟后右左,最后倒序输出的办法 90 | function postOrderTraverse_1(root, callback) { 91 | if (!root) return; 92 | let stack = []; 93 | stack.push(root); 94 | let node = null, nodes = []; 95 | while (stack.length) { 96 | node = stack.pop(); 97 | nodes.push(node); 98 | if (node.left) { 99 | stack.push(node.left); 100 | } 101 | if (node.right) { 102 | stack.push(node.right); 103 | } 104 | } 105 | nodes = nodes.reverse(); 106 | console.log('********* post-order transverse ***********'); 107 | for (let i = 0; i < nodes.length; i ++) 108 | callback(nodes[i]); 109 | console.log(); 110 | } 111 | 112 | inOrderTraverse(tree.getRoot(), printNode); 113 | preOrderTraverse(tree.getRoot(), printNode); 114 | postOrderTraverse(tree.getRoot(), printNode); 115 | postOrderTraverse_1(tree.getRoot(), printNode); 116 | 117 | -------------------------------------------------------------------------------- /Tree/RedBlackTree.js: -------------------------------------------------------------------------------- 1 | function RedBlackTree() { 2 | 3 | var Colors = { 4 | RED: 0, 5 | BLACK: 1 6 | }; 7 | 8 | var Node = function (key, color) { 9 | this.key = key; 10 | this.left = null; 11 | this.right = null; 12 | this.color = color; 13 | 14 | this.flipColor = function(){ 15 | if (this.color === Colors.RED) { 16 | this.color = Colors.BLACK; 17 | } else { 18 | this.color = Colors.RED; 19 | } 20 | }; 21 | }; 22 | 23 | var root = null; 24 | 25 | this.getRoot = function () { 26 | return root; 27 | }; 28 | 29 | var isRed = function(node){ 30 | if (!node){ 31 | return false; 32 | } 33 | return node.color === Colors.RED; 34 | }; 35 | 36 | var flipColors = function(node){ 37 | node.left.flipColor(); 38 | node.right.flipColor(); 39 | }; 40 | 41 | var rotateLeft = function(node){ 42 | var temp = node.right; 43 | if (temp !== null) { 44 | node.right = temp.left; 45 | temp.left = node; 46 | temp.color = node.color; 47 | node.color = Colors.RED; 48 | } 49 | return temp; 50 | }; 51 | 52 | var rotateRight = function (node) { 53 | var temp = node.left; 54 | if (temp !== null) { 55 | node.left = temp.right; 56 | temp.right = node; 57 | temp.color = node.color; 58 | node.color = Colors.RED; 59 | } 60 | return temp; 61 | }; 62 | 63 | var insertNode = function(node, element) { 64 | 65 | if (node === null) { 66 | return new Node(element, Colors.RED); 67 | } 68 | 69 | var newRoot = node; 70 | 71 | if (element < node.key) { 72 | 73 | node.left = insertNode(node.left, element); 74 | 75 | } else if (element > node.key) { 76 | 77 | node.right = insertNode(node.right, element); 78 | 79 | } else { 80 | node.key = element; 81 | } 82 | 83 | if (isRed(node.right) && !isRed(node.left)) { 84 | newRoot = rotateLeft(node); 85 | } 86 | 87 | if (isRed(node.left) && isRed(node.left.left)) { 88 | newRoot = rotateRight(node); 89 | } 90 | if (isRed(node.left) && isRed(node.right)) { 91 | flipColors(node); 92 | } 93 | 94 | return newRoot; 95 | }; 96 | 97 | this.insert = function(element) { 98 | root = insertNode(root, element); 99 | root.color = Colors.BLACK; 100 | }; 101 | } 102 | -------------------------------------------------------------------------------- /Tree/UsingAVLTree.js: -------------------------------------------------------------------------------- 1 | let avlTree = new AVLTree(); 2 | 3 | avlTree.insert(1); 4 | avlTree.insert(2); 5 | avlTree.insert(3); 6 | avlTree.insert(4); 7 | avlTree.insert(5); 8 | avlTree.insert(6); 9 | avlTree.insert(7); 10 | avlTree.insert(14); 11 | avlTree.insert(15); 12 | avlTree.insert(13); 13 | avlTree.insert(12); 14 | avlTree.insert(11); 15 | 16 | //RR rotation 17 | /*avlTree.insert(50); 18 | avlTree.insert(30); 19 | avlTree.insert(70); 20 | avlTree.insert(60); 21 | avlTree.insert(80); 22 | avlTree.insert(90);*/ 23 | 24 | //LL rotation 25 | /*avlTree.insert(50); 26 | avlTree.insert(30); 27 | avlTree.insert(70); 28 | avlTree.insert(10); 29 | avlTree.insert(40); 30 | avlTree.insert(5);*/ 31 | 32 | //LR rotation 33 | /*avlTree.insert(50); 34 | avlTree.insert(30); 35 | avlTree.insert(70); 36 | avlTree.insert(40); 37 | avlTree.insert(10); 38 | avlTree.insert(35);*/ 39 | 40 | //RL rotation 41 | /*avlTree.insert(70); 42 | avlTree.insert(50); 43 | avlTree.insert(80); 44 | avlTree.insert(72); 45 | avlTree.insert(90); 46 | avlTree.insert(75);*/ 47 | 48 | console.log('********* raw data structure ***********'); 49 | console.log(avlTree.getRoot()); 50 | 51 | /*avlTree.remove(12); 52 | avlTree.remove(15); 53 | avlTree.remove(11); 54 | avlTree.remove(14); 55 | avlTree.remove(13); 56 | avlTree.remove(7); 57 | avlTree.remove(6); 58 | avlTree.remove(2); 59 | avlTree.remove(4); 60 | 61 | console.log(avlTree.getRoot());*/ 62 | 63 | -------------------------------------------------------------------------------- /Tree/UsingBinarySearchTree.js: -------------------------------------------------------------------------------- 1 | let BinarySearchTree = require('./BinarySearchTree.js'); 2 | 3 | let tree = new BinarySearchTree(); 4 | 5 | tree.insert(11); 6 | tree.insert(7); 7 | tree.insert(15); 8 | tree.insert(5); 9 | tree.insert(3); 10 | tree.insert(9); 11 | tree.insert(8); 12 | tree.insert(10); 13 | tree.insert(13); 14 | tree.insert(12); 15 | tree.insert(14); 16 | tree.insert(20); 17 | tree.insert(18); 18 | tree.insert(25); 19 | tree.insert(6); 20 | 21 | console.log('********* in-order transverse ***********'); 22 | function printNode(value){ 23 | process.stdout.write(value.toString() + ' '); 24 | } 25 | tree.inOrderTraverse(printNode); 26 | console.log(); 27 | 28 | console.log('********* pre-order transverse ***********'); 29 | tree.preOrderTraverse(printNode); 30 | console.log(); 31 | 32 | console.log('********* post-order transverse ***********'); 33 | tree.postOrderTraverse(printNode); 34 | console.log(); 35 | 36 | console.log('********* max and min ***********'); 37 | console.log(tree.max()); 38 | console.log(tree.min()); 39 | console.log(tree.search(1) ? 'Key 1 found.' : 'Key 1 not found.'); 40 | console.log(tree.search(8) ? 'Key 8 found.' : 'Key 8 not found.'); 41 | 42 | 43 | console.log('********* remove 6 ***********'); 44 | tree.remove(6); 45 | tree.inOrderTraverse(printNode); 46 | console.log(); 47 | 48 | console.log('********* remove 5 ***********'); 49 | tree.remove(5); 50 | tree.inOrderTraverse(printNode); 51 | console.log(); 52 | 53 | console.log('********* remove 15 ***********'); 54 | tree.remove(15); 55 | tree.inOrderTraverse(printNode); 56 | console.log(); 57 | 58 | console.log('********* raw data structure ***********'); 59 | console.log(tree.getRoot()); 60 | -------------------------------------------------------------------------------- /Tree/UsingRedBlackTree.js: -------------------------------------------------------------------------------- 1 | var rbTree = new RedBlackTree(); 2 | 3 | rbTree.insert(1); 4 | rbTree.insert(2); 5 | rbTree.insert(3); 6 | rbTree.insert(4); 7 | rbTree.insert(5); 8 | rbTree.insert(6); 9 | rbTree.insert(7); 10 | rbTree.insert(14); 11 | rbTree.insert(15); 12 | rbTree.insert(13); 13 | rbTree.insert(12); 14 | rbTree.insert(11); 15 | 16 | console.log('********* raw data structure ***********'); 17 | console.log(rbTree.getRoot()); --------------------------------------------------------------------------------