目的:ES6标准下的JS数据结构的一些实现代码。(作为记录和启发)
内容:二叉搜索树。(未完成,待继续)
所有源码在我的Github上(如果觉得不错记得给星鼓励我哦):ES6的JavaScript数据结构实现之树(二叉搜索树、AVL树、红黑树)
一、基础数据结构
1、二叉搜索树(插入元素;树的遍历:中序,先序和后序;搜索树中的值;移除元素。)
1 function defaultCompare(a, b) { 2 if (a === b) { 3 return Compare.EQUALS; 4 } 5 return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; 6 } 7 const Compare = { 8 LESS_THAN: -1, 9 BIGGER_THAN: 1, 10 EQUALS: 0 11 }; 12 class Node { 13 constructor(key) { 14 this.key = key; 15 this.left = undefined; 16 this.right = undefined; 17 } 18 19 toString() { 20 return `${this.key}`; 21 } 22 } 23 24 class BinarySearchTree { 25 constructor(compareFn = defaultCompare) { 26 this.compareFn = compareFn; 27 this.root = undefined; 28 } 29 insert(key) { 30 if (this.root == null) { 31 this.root = new Node(key); 32 } else { 33 this.insertNode(this.root, key); 34 } 35 36 } 37 insertNode(node, key) { 38 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 39 if (node.left == null) { 40 node.left = new Node(key); 41 } else { 42 this.insertNode(node.left, key); 43 } 44 } else if (node.right == null) { 45 node.right = new Node(key); 46 } else { 47 this.insertNode(node.right, key); 48 } 49 } 50 inOrderTraverse(callback) { 51 this.inOrderTraverseNode(this.root, callback); 52 } 53 inOrderTraverseNode(node, callback) { 54 if(node != null) { 55 this.inOrderTraverseNode(node.left, callback); 56 callback(node.key); 57 this.inOrderTraverseNode(node.right, callback); 58 } 59 } 60 preOrderTraverse(callback) { 61 this.preOrderTraverseNode(this.root, callback); 62 } 63 64 preOrderTraverseNode(node, callback) { 65 if (node != null) { 66 callback(node.key); 67 this.preOrderTraverseNode(node.left, callback); 68 this.preOrderTraverseNode(node.right, callback); 69 } 70 } 71 postOrderTraverse(callback) { 72 this.postOrderTraverseNode(this.root, callback); 73 } 74 75 postOrderTraverseNode(node, callback) { 76 if (node != null) { 77 this.postOrderTraverseNode(node.left, callback); 78 this.postOrderTraverseNode(node.right, callback); 79 callback(node.key); 80 } 81 } 82 min() { 83 return this.minNode(this.root); 84 } 85 minNode(node) { 86 let current = node; 87 while (current != null && current.left != null) { 88 current = current.left; 89 } 90 return current; 91 } 92 max() { 93 return this.maxNode(this.root); 94 } 95 96 maxNode(node) { 97 let current = node; 98 while (current != null && current.right != null) { 99 current = current.right; 100 } 101 return current; 102 } 103 search(key) { 104 return this.searchNode(this.root, key); 105 } 106 searchNode(node, key) { 107 if (node == null) { 108 return false; 109 } 110 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 111 return this.searchNode(node.left, key); 112 } 113 if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { 114 return this.searchNode(node.right, key); 115 } 116 return true; 117 } 118 remove(key) { 119 this.root = this.removeNode(this.root, key); 120 121 } 122 removeNode(node, key) { 123 if (node == null) { 124 return undefined; 125 } 126 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 127 node.left = this.removeNode(node.left, key); 128 return node; 129 } if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { 130 node.right = this.removeNode(node.right, key); 131 return node; 132 } 133 // key is equal to node.item 134 // handle 3 special conditions 135 // 1 - a leaf node 136 // 2 - a node with only 1 child 137 // 3 - a node with 2 children 138 // case 1 139 if (node.left == null && node.right == null) { 140 node = undefined; 141 return node; 142 } 143 // case 2 144 if (node.left == null) { 145 node = node.right; 146 return node; 147 } if (node.right == null) { 148 node = node.left; 149 return node; 150 } 151 // case 3 152 const aux = this.minNode(node.right); 153 node.key = aux.key; 154 node.right = this.removeNode(node.right, aux.key); 155 return node; 156 } 157 158 } 159 160 const tree = new BinarySearchTree(); 161 tree.insert(11); 162 tree.insert(12); 163 tree.insert(7); 164 tree.insert(5); 165 tree.insert(13); 166 console.log(tree); 167 tree.insert(8); 168 tree.insert(3); 169 console.log(tree); 170 171 const printNode = (value) => console.log(value); 172 tree.inOrderTraverse(printNode); 173 console.log('******'); 174 tree.preOrderTraverse(printNode); 175 console.log('******'); 176 tree.postOrderTraverse(printNode); 177 console.log('******'); 178 console.log(tree.min()); 179 console.log(tree.max()); 180 console.log(tree.search(7)); 181 console.log(tree.search(6)); 182 console.log(tree); 183 tree.remove(12); 184 console.log(tree);
二、二叉搜索树的扩展(自平衡树、AVL树、红黑树)
BST存在一个问题:取决于我们添加的节点数,数的一条边可能会非常深。这会在需要在某条边上添加、移除和搜索某个节点时引起的一些性能问题。为解决这个问题,有一种树叫Adelson-Velskii-Landi树(AVL树)。AVL树是一种自平衡的二叉搜索树(任何一个节点左右两侧子树的高度之差最多为1)。此外,红黑树也是一个自平衡二叉搜索树(如果需要一个包含多次插入和删除的自平衡树,红黑树要优于AVL树)。
1、Adelson-Velskii-Landi树(AVL树)(节点的高度和平衡因子;平衡操作--AVL旋转;插入节点;移除节点)
1 function defaultCompare(a, b) { 2 if (a === b) { 3 return Compare.EQUALS; 4 } 5 return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; 6 } 7 const Compare = { 8 LESS_THAN: -1, 9 BIGGER_THAN: 1, 10 EQUALS: 0 11 }; 12 class Node { 13 constructor(key) { 14 this.key = key; 15 this.left = undefined; 16 this.right = undefined; 17 } 18 19 toString() { 20 return `${this.key}`; 21 } 22 } 23 24 class BinarySearchTree { 25 constructor(compareFn = defaultCompare) { 26 this.compareFn = compareFn; 27 this.root = undefined; 28 } 29 insert(key) { 30 if (this.root == null) { 31 this.root = new Node(key); 32 } else { 33 this.insertNode(this.root, key); 34 } 35 36 } 37 insertNode(node, key) { 38 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 39 if (node.left == null) { 40 node.left = new Node(key); 41 } else { 42 this.insertNode(node.left, key); 43 } 44 } else if (node.right == null) { 45 node.right = new Node(key); 46 } else { 47 this.insertNode(node.right, key); 48 } 49 } 50 inOrderTraverse(callback) { 51 this.inOrderTraverseNode(this.root, callback); 52 } 53 inOrderTraverseNode(node, callback) { 54 if(node != null) { 55 this.inOrderTraverseNode(node.left, callback); 56 callback(node.key); 57 this.inOrderTraverseNode(node.right, callback); 58 } 59 } 60 preOrderTraverse(callback) { 61 this.preOrderTraverseNode(this.root, callback); 62 } 63 64 preOrderTraverseNode(node, callback) { 65 if (node != null) { 66 callback(node.key); 67 this.preOrderTraverseNode(node.left, callback); 68 this.preOrderTraverseNode(node.right, callback); 69 } 70 } 71 postOrderTraverse(callback) { 72 this.postOrderTraverseNode(this.root, callback); 73 } 74 75 postOrderTraverseNode(node, callback) { 76 if (node != null) { 77 this.postOrderTraverseNode(node.left, callback); 78 this.postOrderTraverseNode(node.right, callback); 79 callback(node.key); 80 } 81 } 82 min() { 83 return this.minNode(this.root); 84 } 85 minNode(node) { 86 let current = node; 87 while (current != null && current.left != null) { 88 current = current.left; 89 } 90 return current; 91 } 92 max() { 93 return this.maxNode(this.root); 94 } 95 96 maxNode(node) { 97 let current = node; 98 while (current != null && current.right != null) { 99 current = current.right; 100 } 101 return current; 102 } 103 search(key) { 104 return this.searchNode(this.root, key); 105 } 106 searchNode(node, key) { 107 if (node == null) { 108 return false; 109 } 110 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 111 return this.searchNode(node.left, key); 112 } 113 if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { 114 return this.searchNode(node.right, key); 115 } 116 return true; 117 } 118 remove(key) { 119 this.root = this.removeNode(this.root, key); 120 121 } 122 removeNode(node, key) { 123 if (node == null) { 124 return undefined; 125 } 126 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 127 node.left = this.removeNode(node.left, key); 128 return node; 129 } if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { 130 node.right = this.removeNode(node.right, key); 131 return node; 132 } 133 // key is equal to node.item 134 // handle 3 special conditions 135 // 1 - a leaf node 136 // 2 - a node with only 1 child 137 // 3 - a node with 2 children 138 // case 1 139 if (node.left == null && node.right == null) { 140 node = undefined; 141 return node; 142 } 143 // case 2 144 if (node.left == null) { 145 node = node.right; 146 return node; 147 } if (node.right == null) { 148 node = node.left; 149 return node; 150 } 151 // case 3 152 const aux = this.minNode(node.right); 153 node.key = aux.key; 154 node.right = this.removeNode(node.right, aux.key); 155 return node; 156 } 157 158 } 159 const BalanceFactor = { 160 UNBALANCED_RIGHT: 1, 161 SLIGHTLY_UNBALANCED_RIGHT: 2, 162 BALANCED: 3, 163 SLIGHTLY_UNBALANCED_LEFT: 4, 164 UNBALANCED_LEFT: 5 165 }; 166 class AVLTree extends BinarySearchTree { 167 constructor(compareFn = defaultCompare) { 168 super(compareFn); 169 this.compareFn = compareFn; 170 this.root = null; 171 } 172 getNodeHeigh(node) { 173 if (node == null) { 174 return -1; 175 } 176 return Max.max ( 177 this.getNodeHeigh(node.left), this.getNodeHeigh(node.right) 178 ) + 1; 179 } 180 getBalanceFactor(node) { 181 const heightDifference = this.getNodeHeigh(node.left) - 182 this.getNodeHeigh(node.right); 183 switch (heightDifference) { 184 case -2: 185 return BalanceFactor.UNBALANCED_RIGHT; 186 case -1: 187 return BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT; 188 case 1: 189 return BalanceFactor.SLIGHTLY_UNBALANCED_LEFT; 190 case 2: 191 return BalanceFactor.UNBALANCED_LEFT; 192 default: 193 return BalanceFactor.BALANCED; 194 } 195 } 196 rotationLL(node) { 197 const tmp = node.left; 198 node.left = tmp.right; 199 tmp.right = node; 200 return tmp; 201 } 202 rotationRR(node) { 203 const tmp = node.right; 204 node.right = tmp.left; 205 tmp.left = node; 206 return tmp; 207 } 208 rotationLR(node) { 209 node.left = this.rotationRR(node.left); 210 return this.rotationLL(node); 211 } 212 rotationRL(node) { 213 node.right = this.ratationLL(node.right); 214 return this.rotationRR(node); 215 } 216 insert(key) { 217 this.root = this.insertNode(this.root, key); 218 } 219 insertNode(node, key) { 220 if(node == null) { 221 return new Node(key); 222 } 223 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 224 node.left = this.insertNode(node.left, key); 225 } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { 226 node.right = this.insertNode(node.right, key); 227 } else { 228 return node; 229 } 230 const balanceFactor = this.getBalanceFactor(node); 231 if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) { 232 if (this.compareFn(key, node.left.key) === Compare.LESS_THAN) { 233 node = this.rotationLL(node); 234 } else { 235 return this.rotationLR(node); 236 } 237 } 238 if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) { 239 if (this.compareFn(key, node.right.key) === Compare.BIGGER_THAN) { 240 node = this. rotationRR(node); 241 } else { 242 return this.rotationRL(node); 243 } 244 } 245 return node; 246 } 247 248 removeNode(node, key) { 249 node = super.removeNode(node, key); 250 if (node == null) { 251 return node; 252 } 253 const balanceFactor = this.getBalanceFactor(node); 254 if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) { 255 if ( 256 balanceFactorLeft === BalanceFactor.BALANCED || 257 balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT 258 ) { 259 return this.rotationLL(node); 260 } 261 if ( 262 balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT 263 ) { 264 return this.rotationLR(node.left); 265 } 266 } 267 if (BalanceFactor === BalanceFactor.UNBALANCED_RIGHT) { 268 if ( 269 balanceFactorRight === BalanceFactor.BALANCED || 270 balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT 271 ) { 272 return this.rotationRR(node); 273 274 } 275 if ( 276 balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT 277 ) { 278 return this.rotationRL(node.right); 279 } 280 } 281 return node; 282 } 283 284 285 286 }
2、红黑树(红黑树的规则;重新填色和旋转;插入节点;移除节点;)
1 function defaultCompare(a, b) { 2 if (a === b) { 3 return Compare.EQUALS; 4 } 5 return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; 6 } 7 const Compare = { 8 LESS_THAN: -1, 9 BIGGER_THAN: 1, 10 EQUALS: 0 11 }; 12 class Node { 13 constructor(key) { 14 this.key = key; 15 this.left = undefined; 16 this.right = undefined; 17 } 18 19 toString() { 20 return `${this.key}`; 21 } 22 } 23 24 class BinarySearchTree { 25 constructor(compareFn = defaultCompare) { 26 this.compareFn = compareFn; 27 this.root = undefined; 28 } 29 insert(key) { 30 if (this.root == null) { 31 this.root = new Node(key); 32 } else { 33 this.insertNode(this.root, key); 34 } 35 36 } 37 insertNode(node, key) { 38 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 39 if (node.left == null) { 40 node.left = new Node(key); 41 } else { 42 this.insertNode(node.left, key); 43 } 44 } else if (node.right == null) { 45 node.right = new Node(key); 46 } else { 47 this.insertNode(node.right, key); 48 } 49 } 50 inOrderTraverse(callback) { 51 this.inOrderTraverseNode(this.root, callback); 52 } 53 inOrderTraverseNode(node, callback) { 54 if(node != null) { 55 this.inOrderTraverseNode(node.left, callback); 56 callback(node.key); 57 this.inOrderTraverseNode(node.right, callback); 58 } 59 } 60 preOrderTraverse(callback) { 61 this.preOrderTraverseNode(this.root, callback); 62 } 63 64 preOrderTraverseNode(node, callback) { 65 if (node != null) { 66 callback(node.key); 67 this.preOrderTraverseNode(node.left, callback); 68 this.preOrderTraverseNode(node.right, callback); 69 } 70 } 71 postOrderTraverse(callback) { 72 this.postOrderTraverseNode(this.root, callback); 73 } 74 75 postOrderTraverseNode(node, callback) { 76 if (node != null) { 77 this.postOrderTraverseNode(node.left, callback); 78 this.postOrderTraverseNode(node.right, callback); 79 callback(node.key); 80 } 81 } 82 min() { 83 return this.minNode(this.root); 84 } 85 minNode(node) { 86 let current = node; 87 while (current != null && current.left != null) { 88 current = current.left; 89 } 90 return current; 91 } 92 max() { 93 return this.maxNode(this.root); 94 } 95 96 maxNode(node) { 97 let current = node; 98 while (current != null && current.right != null) { 99 current = current.right; 100 } 101 return current; 102 } 103 search(key) { 104 return this.searchNode(this.root, key); 105 } 106 searchNode(node, key) { 107 if (node == null) { 108 return false; 109 } 110 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 111 return this.searchNode(node.left, key); 112 } 113 if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { 114 return this.searchNode(node.right, key); 115 } 116 return true; 117 } 118 remove(key) { 119 this.root = this.removeNode(this.root, key); 120 121 } 122 removeNode(node, key) { 123 if (node == null) { 124 return undefined; 125 } 126 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 127 node.left = this.removeNode(node.left, key); 128 return node; 129 } if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { 130 node.right = this.removeNode(node.right, key); 131 return node; 132 } 133 // key is equal to node.item 134 // handle 3 special conditions 135 // 1 - a leaf node 136 // 2 - a node with only 1 child 137 // 3 - a node with 2 children 138 // case 1 139 if (node.left == null && node.right == null) { 140 node = undefined; 141 return node; 142 } 143 // case 2 144 if (node.left == null) { 145 node = node.right; 146 return node; 147 } if (node.right == null) { 148 node = node.left; 149 return node; 150 } 151 // case 3 152 const aux = this.minNode(node.right); 153 node.key = aux.key; 154 node.right = this.removeNode(node.right, aux.key); 155 return node; 156 } 157 158 } 159 160 class RedBlackNode extends Node { 161 constructor(key) { 162 super(key); 163 this.key = key; 164 this.color = Colors.RED; 165 this.parent = null; 166 } 167 isRed() { 168 return this.color === Colors.RED; 169 } 170 } 171 172 class RedBlackTree extends BinarySearchTree { 173 constructor(compareFn = defaultCompare) { 174 super(compareFn); 175 this.compareFn = compareFn; 176 this.root = null; 177 } 178 insert(key) { 179 if (this.root == null) { 180 this.root = new RedBlackNode(key); 181 this.root.color = Colors.BLACK; 182 } else { 183 const newNode = this.inserNode(this.root, key); 184 this.fixTreeProperties(newNode); 185 } 186 } 187 insertNode(node, key) { 188 if (this.compareFn(key, node.key) === Compare.LESS_THAN) { 189 if (node.left == null) { 190 node.left = new RedBlackNode(key); 191 node.left.parent = node; 192 return node.left; 193 } 194 else { 195 return this.insertNode(node.left, key); 196 } 197 } 198 else if (node.right == null) { 199 node.right = new RedBlackNode(key); 200 node.right.parent = node; 201 return node.right; 202 } 203 else { 204 return this.insertNode(node.right, key); 205 } 206 } 207 fixTreeProperties(node) { 208 while (node && node.parent && node.parent.color.isRed() 209 && node.color !== Colors.BLACK) { 210 let parent = node.parent; 211 const grandParent = parent.parent; 212 if (grandParent && grandParent.left === parent) { 213 const uncle = grandParent.right; 214 if (uncle && uncle.color === Colors.RED) { 215 grandParent.color = Colors.RED; 216 parent.color = Colors.BLACK; 217 uncle.color = Colors.BLACK; 218 node = grandParent; 219 } 220 else { 221 if (node === parent.right) { 222 this.rotationRR(parent); 223 node = parent; 224 parent = node.parent; 225 } 226 this.rotationLL(grandParent); 227 parent.color = Colors.BLACK; 228 grandParent.color = Colors.RED; 229 node = parent; 230 231 } 232 } 233 else { 234 const uncle = grandParent.left; 235 if (uncle && uncle.color === Colors.RED) { 236 grandParent.color = Colors.RED; 237 parent.color = Colors.BLACK; 238 uncle.color = Colors.BLACK; 239 node = grandParent; 240 } 241 else { 242 if (node === parent.left) { 243 this.rotationLL(parent); 244 node = parent; 245 parent = node.parent; 246 } 247 this.rotationRR(grandParent); 248 parent.color = Colors.BLACK; 249 grandParent.color = Colors.RED; 250 node = parent; 251 } 252 } 253 254 255 } 256 this.root.color = Colors.BLACK; 257 } 258 rotationLL(node) { 259 const tmp = node.left; 260 node.left = tmp.right; 261 if (tmp.right && tmp.right.key) { 262 tmp.right.parent = node; 263 } 264 tmp.parent = node.parent; 265 if (!node.parent) { 266 this.root = tmp; 267 } 268 else { 269 if(node === node.parent.left) { 270 node.parent.left = tmp; 271 } 272 else { 273 node.parent.right = tmp; 274 } 275 } 276 tmp.right = node; 277 node.parent = tmp; 278 } 279 rotationRR(node) { 280 const tmp = node.right; 281 node.right = tmp.left; 282 if (tmp.left && tmp.left.key) { 283 tmp.left.parent = node; 284 } 285 tmp.parent = node.parent; 286 if (!node.parent) { 287 this.root = tmp; 288 } 289 else { 290 if (node === node.parent.left) { 291 node.parent.left = tmp; 292 } 293 else { 294 node.parent.right = tmp; 295 } 296 } 297 tmp.left = node; 298 node.parent = tmp; 299 } 300 301 }