├── .gitignore ├── BinarySearchTree └── BinarySearchTree.js ├── CircleLineCollide └── CircleLineCollide.js ├── ExpressionTree └── ExpTree.js ├── LICENSE ├── LinkedList ├── LinkedList.js └── Poly.js ├── README.md └── Stack └── InfixExpression.js /.gitignore: -------------------------------------------------------------------------------- 1 | ##########Sublime Text 2########## 2 | # workspace files are user-specific 3 | *.sublime-workspace 4 | 5 | # project files should be checked into the repository, unless a significant 6 | # proportion of contributors will probably not be using SublimeText 7 | *.sublime-project 8 | 9 | ##########OSX########## 10 | .DS_Store 11 | .AppleDouble 12 | .LSOverride 13 | 14 | # Icon must ends with two \r. 15 | Icon 16 | 17 | 18 | # Thumbnails 19 | ._* 20 | 21 | # Files that might appear on external disk 22 | .Spotlight-V100 23 | .Trashes -------------------------------------------------------------------------------- /BinarySearchTree/BinarySearchTree.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Tim Wu (吴智炜) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | /**二叉查找树*/ 24 | function BinarySearchTree() { 25 | this.root = null; 26 | } 27 | 28 | /**二叉查找树节点*/ 29 | function BstNode(key, value, parent) { 30 | this.key = key; 31 | this.valueList = [value]; 32 | this.parent = parent || null; 33 | this.left = null; 34 | this.right = null; 35 | } 36 | 37 | BinarySearchTree.prototype._add = function(node, key, value) { 38 | if(this.root == null) { 39 | this.root = new BstNode(key, value); 40 | return ; 41 | } 42 | if(key < node.key) { 43 | node.left == null ? 44 | node.left = new BstNode(key, value, node) : 45 | this._add(node.left, key, value); 46 | } 47 | else if(key > node.key) { 48 | node.right == null ? 49 | node.right = new BstNode(key, value, node) : 50 | this._add(node.right, key, value); 51 | } 52 | else { 53 | node.valueList.push(value); 54 | } 55 | }; 56 | 57 | BinarySearchTree.prototype._remove = function(key, value, root) { 58 | if(!root) return null; 59 | if(key < root.key) { 60 | this._remove(key, value, root.left); 61 | } 62 | else if(key > root.key) { 63 | this._remove(key, value, root.right); 64 | } 65 | else if(value && root.valueList.length > 1) { 66 | root.valueList.splice(root.valueList.indexOf(value), 1); 67 | } 68 | else if(root.left && root.right) { 69 | var rightMin = this._findMin(root.right); 70 | root.key = rightMin.key; 71 | root.valueList = rightMin.valueList; 72 | this._remove(root.key, null, root.right); 73 | } 74 | else { 75 | var next = root.left ? root.left : root.right 76 | if(root == this.root) { 77 | this.root = next; 78 | this.root.parent = null; 79 | return; 80 | } 81 | if(root.key >= root.parent.key) { 82 | root.parent.right = next; 83 | } 84 | else if(root.key < root.parent.key) { 85 | root.parent.left = next; 86 | } 87 | if(next) { 88 | next.parent = root.parent; 89 | } 90 | } 91 | return root; 92 | }; 93 | 94 | BinarySearchTree.prototype._findMin = function(root) { 95 | while(root && root.left) { 96 | root = root.left; 97 | } 98 | return root; 99 | }; 100 | 101 | BinarySearchTree.prototype._findMax = function(root) { 102 | while(root && root.right) { 103 | root = root.right; 104 | } 105 | return root; 106 | }; 107 | 108 | BinarySearchTree.prototype._find = function(node, key) { 109 | if(node == null || node.key == key) { 110 | return node; 111 | } 112 | if(node.key > key) { 113 | return this._find(node.left, key); 114 | } 115 | return this._find(node.right, key); 116 | }; 117 | 118 | BinarySearchTree.prototype._inOrderTravel = function(root, func) { 119 | if(root) { 120 | this._inOrderTravel(root.left, func); 121 | func(root); 122 | this._inOrderTravel(root.right, func); 123 | } 124 | }; 125 | 126 | BinarySearchTree.prototype._findInRange = function(root, list, min, max) { 127 | if(root) { 128 | this._findInRange(root.left, list, min, max); 129 | if(root.key > max) { 130 | return; 131 | } 132 | if(root.key >= min && root.key <= max) { 133 | list.push(root.key); 134 | } 135 | this._findInRange(root.right, list, min, max); 136 | } 137 | }; 138 | 139 | BinarySearchTree.prototype.add = function(key, value) { 140 | return this._add(this.root, key, value); 141 | }; 142 | 143 | BinarySearchTree.prototype.remove = function(key, value) { 144 | return this._remove(key, value, this.root); 145 | }; 146 | 147 | BinarySearchTree.prototype.findMin = function() { 148 | return this._findMin(this.root); 149 | }; 150 | 151 | BinarySearchTree.prototype.findMax = function() { 152 | return this._findMax(this.root); 153 | }; 154 | 155 | BinarySearchTree.prototype.find = function(key) { 156 | return this._find(this.root, key); 157 | }; 158 | 159 | BinarySearchTree.prototype.inOrderTravel = function(func) { 160 | return this._inOrderTravel(this.root, func); 161 | }; 162 | 163 | BinarySearchTree.prototype.findInRange = function(min, max) { 164 | var list = []; 165 | this._findInRange(this.root, list, min, max); 166 | return list; 167 | }; 168 | 169 | BinarySearchTree.prototype.findNR = function(key) { 170 | var node = this.root; 171 | while(node) { 172 | if(node.key == key) { 173 | return node; 174 | } 175 | if(node.key > key) { 176 | node = node.left; 177 | } 178 | else if(node.key < key) { 179 | node = node.right; 180 | } 181 | } 182 | return node; 183 | }; 184 | 185 | BinarySearchTree.prototype.addNR = function(key, value) { 186 | if(this.root == null) { 187 | this.root = new BstNode(key, value); 188 | return ; 189 | } 190 | var node = this.root; 191 | while(key != node.key) { 192 | if(key < node.key) { 193 | if(node.left) { 194 | node = node.left; 195 | } 196 | else { 197 | node.left = new BstNode(key, value, node); 198 | return; 199 | } 200 | } 201 | else if(key > node.key) { 202 | if(node.right) { 203 | node = node.right; 204 | } 205 | else { 206 | node.right = new BstNode(key, value, node); 207 | return; 208 | } 209 | } 210 | } 211 | if(key == node.key) { 212 | node.valueList.push(value); 213 | } 214 | }; 215 | 216 | BinarySearchTree.prototype.findInRangeNR = function(min, max) { 217 | var list = []; 218 | var node = this.root; 219 | var stack = []; 220 | var handle = function(n) { 221 | if(!n || n.key > max) { 222 | return false; 223 | } 224 | if(n.key >= min && n.key <= max) { 225 | list.push(n.key); 226 | } 227 | return true; 228 | }; 229 | while(node) { 230 | if(node.left) { 231 | stack.push(node); 232 | node = node.left; 233 | } 234 | else { 235 | handle(node); 236 | while(! node.right) { 237 | node = stack.pop(); 238 | if(!node) { 239 | return list; 240 | } 241 | handle(node); 242 | } 243 | node = node.right; 244 | } 245 | } 246 | return list; 247 | }; 248 | 249 | BinarySearchTree.prototype.inOrderTravelNR = function(func) { 250 | var node = this.root; 251 | var stack = []; 252 | while(node) { 253 | if(node.left) { 254 | stack.push(node); 255 | node = node.left; 256 | } 257 | else { 258 | func(node); 259 | while(! node.right) { 260 | node = stack.pop(); 261 | if(! node) { 262 | return ; 263 | } 264 | func(node); 265 | } 266 | node = node.right; 267 | } 268 | } 269 | }; 270 | 271 | // $('#test').click( function () { 272 | 273 | var bst = new BinarySearchTree(); 274 | var dic = {}; 275 | // var num = parseInt($("#nodes").val(), 10); 276 | var num = 10000; 277 | var key; 278 | var start = 'bob'; 279 | var end = 'tim'; 280 | for(var i = 0; i < num; ++i) { 281 | key = Math.random().toString(36).substr(2,3); 282 | bst.add(key,i); 283 | dic[key] = i; 284 | } 285 | var s1 = new Date().getTime(); 286 | var l1 = bst.findInRangeNR(start, end); 287 | var e1 = new Date().getTime(); 288 | var l2 = []; 289 | var s2 = new Date().getTime(); 290 | for (var k in dic) { 291 | if(k <= end && k >= start) { 292 | l2.push(k); 293 | } 294 | } 295 | var e2 = new Date().getTime(); 296 | var res = '字典:找到' + l2.length + '个,耗时:'+ (e2-s2) +'ms<br>'; 297 | res += '二叉查找树:找到' + l1.length + '个,耗时:'+ (e1-s1) +'ms'; 298 | console.log(res); 299 | console.log(l1.length, l2.length); 300 | console.log(e1-s1, e2-s2); 301 | // }); 302 | -------------------------------------------------------------------------------- /CircleLineCollide/CircleLineCollide.js: -------------------------------------------------------------------------------- 1 | //判断点c是否在线段ab的左侧 2 | function isLeft(a, b, c) { 3 | return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x) < 0; 4 | } 5 | 6 | //根据时间间隔更新p1 7 | function updateObjPosByTime(v, delta) { 8 | v.vx = v.vx * delta 9 | v.vy = v.vy * delta 10 | 11 | v.p1 = {x: v.p0.x + v.vx, y: v.p0.y + v.vy} 12 | v.len = Math.sqrt(v.vx*v.vx + v.vy*v.vy) 13 | 14 | v.delta = delta 15 | } 16 | 17 | //更新向量 18 | function updateVector(v, isCorner) { 19 | if(isCorner) { 20 | v.vx = v.p1.x-v.p0.x; 21 | v.vy = v.p1.y-v.p0.y; 22 | }else if(v.p0) { 23 | v.p1.x = v.p0.x+v.vx; 24 | v.p1.y = v.p0.y+v.vy; 25 | } 26 | v.len = Math.sqrt(v.vx*v.vx + v.vy*v.vy); 27 | v.dx = v.vx / v.len; 28 | v.dy = v.vy / v.len; 29 | v.rx = -v.dy; 30 | v.ry = v.dx; 31 | v.lx = v.dy; 32 | v.ly = -v.dx; 33 | return v; 34 | } 35 | 36 | //检查墙壁,修正p1 37 | function checkWalls(obj, walls) { 38 | for (var i = walls.length - 1; i >= 0; i--) { 39 | var w = walls[i]; 40 | var iv = getIntersectVector(obj, w); 41 | iv = updateVector(iv, false); 42 | var pen = obj.r - iv.len; 43 | if(pen > 0) { 44 | console.log("collide", obj.p1.x, obj.p1.y, iv.len, i) 45 | //回移 46 | obj.p1.x += iv.dx*pen; 47 | obj.p1.y += iv.dy*pen; 48 | var bv = getBounceVector(obj, w); 49 | obj.vx = bv.vx; 50 | obj.vy = bv.vy; 51 | } 52 | } 53 | } 54 | 55 | //获取回弹向量 56 | function getBounceVector(obj, w) { 57 | var projw = getProjectVector(obj, w.dx, w.dy); 58 | var projn; 59 | var left = isLeft(w.p0, w.p1, obj.p0); 60 | if(left) { 61 | projn = getProjectVector(obj, w.rx, w.ry); 62 | }else { 63 | projn = getProjectVector(obj, w.lx, w.ly); 64 | } 65 | projn.vx *= -1; 66 | projn.vy *= -1; 67 | return { 68 | vx: w.wf*projw.vx + w.bf*projn.vx, 69 | vy: w.wf*projw.vy + w.bf*projn.vy, 70 | }; 71 | } 72 | 73 | //获取圆与线段的碰撞修正向量 74 | function getIntersectVector(obj, w) { 75 | var v1 = {vx:obj.p1.x-w.p0.x, vy:obj.p1.y-w.p0.y}; 76 | if(v1.vx*w.vx + v1.vy*w.vy < 0) { 77 | return v1; 78 | } 79 | var v2 = {vx:obj.p1.x-w.p1.x, vy:obj.p1.y-w.p1.y}; 80 | if(v2.vx*w.vx + v2.vy*w.vy > 0) { 81 | return v2; 82 | } 83 | if(isLeft(w.p0, w.p1, obj.p0)){ 84 | return getProjectVector(v1, w.lx, w.ly); 85 | } 86 | return getProjectVector(v1, w.rx, w.ry); 87 | } 88 | 89 | //获取向量u在向量dx,dy上的投影向量 90 | function getProjectVector(u, dx, dy) { 91 | var dp = u.vx*dx + u.vy*dy; 92 | return {vx:dp*dx, vy:dp*dy}; 93 | } -------------------------------------------------------------------------------- /ExpressionTree/ExpTree.js: -------------------------------------------------------------------------------- 1 | /**操作符-优先级表*/ 2 | var OPN_TABLE = 3 | { 4 | '+' : 0, 5 | '-' : 0, 6 | '*' : 1, 7 | '/' : 1, 8 | '^' : 2, 9 | }; 10 | 11 | /**函数查询表*/ 12 | var FUNC_TABLE = 13 | { 14 | 'sin' : 1, 15 | 'cos' : 1, 16 | 'tan' : 1, 17 | 'cot' : 1, 18 | 'arcsin' : 1, 19 | 'arccos' : 1, 20 | 'arctan' : 1, 21 | 'ln' : 1, 22 | 'log' : 1, 23 | }; 24 | 25 | /**检查是否为数字*/ 26 | var isNumber = function(n) { 27 | return !isNaN(parseFloat(n)) && isFinite(n); 28 | }; 29 | 30 | /**二叉表达树*/ 31 | function ExpTree(key) { 32 | //根节点 33 | this.root = null; 34 | //未知量的表达形式,默认为x 35 | this.key = key || 'x'; 36 | //中序遍历的字符串 37 | this.infix = ''; 38 | //后续遍历的字符串 39 | this.postfix = ''; 40 | } 41 | 42 | /**二叉表达树节点*/ 43 | function ExpTreeNode(key, left, right) { 44 | //节点值 45 | this.key = key; 46 | //左子树,默认为空 47 | this.left = left || null; 48 | //右子树,默认为空 49 | this.right = right || null; 50 | } 51 | 52 | /**预处理表达式字串*/ 53 | ExpTree.prototype.preHandleExp = function(exp) { 54 | if(!exp) return ''; 55 | //补全小数点的前缀0 56 | exp = exp.replace(/([^0-9]|^)(\.\d+)/g, '$10$2'); 57 | //处理省略乘号的情况 58 | var reg2 = new RegExp('([0-9]+(?:[\.][0-9]*)?|\.[0-9]+)(['+this.key+' e pi])', 'g'); 59 | exp = exp.replace( reg2, '$1*$2'); 60 | //处理单目运算符 '+' '-' 61 | var c1 = '+', c2; 62 | var addRp = false; 63 | for (var i = 0; i < exp.length; ++i) { 64 | c2 = exp[i]; 65 | if(c2 in OPN_TABLE && addRp) { 66 | addRp = false; 67 | exp = exp.slice(0,i) + ')' + exp.slice(i); 68 | ++ i; 69 | } 70 | if((c1 in OPN_TABLE || c1 == '(' || c1 == ',') && c2 in OPN_TABLE) { 71 | if(c2 == '+') { 72 | //清除多余的+ 73 | exp = exp.slice(0,i) + exp.slice(i+1); 74 | -- i; 75 | } 76 | else if(c2 == '-') { 77 | //在前面添加'(0' 78 | exp = exp.slice(0,i) + '(0' + exp.slice(i); 79 | i += 2; 80 | //置为需要添加')'模式 81 | addRp = true; 82 | } 83 | } 84 | c1 = c2; 85 | }; 86 | if(addRp) { 87 | exp += ')'; 88 | } 89 | return exp; 90 | }; 91 | 92 | /**中缀表达式转后缀表达式 调度场算法*/ 93 | ExpTree.prototype.inFixToPostFix = function(exp) { 94 | var postFix = []; 95 | var stack = []; 96 | //调度场算法 97 | exp = this.preHandleExp(exp); 98 | console.log(exp); 99 | var c, o1, o2, sign; 100 | var func = ''; 101 | for (var i = 0; i < exp.length; ++i) { 102 | c = exp[i]; 103 | //数字 104 | if(c >= '0' && c <= '9') { 105 | sign = false; 106 | while(i < exp.length && (exp[i] == '.' || (exp[i] >= '0' && exp[i] <= '9'))) { 107 | ++ i; 108 | c += exp[i]; 109 | sign = true; 110 | } 111 | if(sign) { 112 | -- i; 113 | } 114 | //try catch here 115 | try { 116 | postFix.push(parseFloat(c)); 117 | }catch(error) { 118 | return ''; 119 | } 120 | } 121 | //特殊字符 e x 122 | else if(c == 'e' || c == 'x') { 123 | postFix.push(c); 124 | } 125 | //函数分隔符 126 | else if(c == ',') { 127 | while(stack.length && stack[stack.length-1] != '(') { 128 | postFix.push(stack.pop()); 129 | } 130 | } 131 | //运算符 132 | else if(c in OPN_TABLE) { 133 | o1 = c; 134 | if(stack.length) { 135 | o2 = stack[stack.length-1]; 136 | while(o2 in OPN_TABLE && OPN_TABLE[o2] - OPN_TABLE[o1] >= 0) { 137 | postFix.push(stack.pop()); 138 | if(!stack.length) { 139 | break; 140 | } 141 | o2 = stack[stack.length-1]; 142 | } 143 | } 144 | stack.push(o1); 145 | } 146 | //左括号 147 | else if(c == '(') { 148 | stack.push(c); 149 | } 150 | //右括号 151 | else if(c == ')') { 152 | while(stack.length && stack[stack.length-1] != '(') { 153 | postFix.push(stack.pop()); 154 | } 155 | if(!stack.length) { 156 | return ''; 157 | } 158 | if(stack.length && stack[stack.length-1] == '('){ 159 | stack.pop(); 160 | if(stack.length && stack[stack.length-1] in FUNC_TABLE) { 161 | postFix.push(stack.pop()); 162 | } 163 | } 164 | } 165 | //函数符 166 | else { 167 | func += c; 168 | if(func in FUNC_TABLE) { 169 | stack.push(func); 170 | func = ''; 171 | } 172 | } 173 | 174 | }; 175 | if(stack.length) { 176 | if(stack[stack.length-1] == '(' || stack[stack.length-1] == ')' ) { 177 | return ''; 178 | } 179 | else { 180 | while(stack.length) { 181 | postFix.push(stack.pop()); 182 | } 183 | } 184 | } 185 | if(func) { 186 | return ''; 187 | } 188 | 189 | return postFix; 190 | }; 191 | 192 | /**构建二叉表达树*/ 193 | ExpTree.prototype.build = function(infixExp) { 194 | var exp = infixExp.toLowerCase(); 195 | exp = exp.replace( /\s/g, "" ); 196 | var postFix = this.inFixToPostFix(exp); 197 | if(postFix == '') { 198 | return false; 199 | } 200 | console.log('postFix:',postFix); 201 | var stack = []; 202 | var c, n; 203 | for (var i = 0; i < postFix.length; i++) { 204 | c = postFix[i]; 205 | if(isNumber(c) || c == 'e' || c == 'pi' || c == 'x') { 206 | stack.push(new ExpTreeNode(c)); 207 | } 208 | else { 209 | if(c == '(' || c == ')') { 210 | return false; 211 | } 212 | n = new ExpTreeNode(c); 213 | n.right = stack.pop(); 214 | if(c in OPN_TABLE || c == 'log') { 215 | n.left = stack.pop(); 216 | if(!n.left) { 217 | return false; 218 | } 219 | } 220 | stack.push(n); 221 | } 222 | }; 223 | this.root = stack[0]; 224 | if(!this.root.left && !this.root.right) { 225 | return false; 226 | } 227 | return stack.length <= 1; 228 | }; 229 | 230 | ExpTree.prototype._toInFix = function(node) { 231 | if(node) { 232 | this._toInFix(node.left); 233 | this.infix += node.key; 234 | this._toInFix(node.right); 235 | } 236 | }; 237 | 238 | ExpTree.prototype.toInFix = function(node) { 239 | this.infix = ''; 240 | this._toInFix(this.root); 241 | return this.infix; 242 | }; 243 | 244 | ExpTree.prototype._toPostFix = function(node) { 245 | if(node) { 246 | this._toPostFix(node.left); 247 | this._toPostFix(node.right); 248 | this.postfix += node.key; 249 | } 250 | }; 251 | 252 | ExpTree.prototype.toPostFix = function(node) { 253 | this.postfix = ''; 254 | this._toPostFix(this.root); 255 | return this.postfix; 256 | }; 257 | 258 | /**执行原子计算*/ 259 | ExpTree.prototype.op = function(opr, left, right) { 260 | switch(opr) { 261 | case '+': 262 | return left + right; 263 | case '-': 264 | return left - right; 265 | case '*': 266 | if(left == 0 || right == 0) { 267 | return 0; 268 | } 269 | return left * right; 270 | case '/': 271 | return left / right; 272 | case '^': 273 | return Math.pow(left, right); 274 | case 'sin': 275 | return Math.sin(right); 276 | case 'cos': 277 | return Math.cos(right); 278 | case 'tan': 279 | return Math.tan(right); 280 | case 'cot': 281 | return 1 / Math.tan(right); 282 | case 'arcsin': 283 | return Math.asin(right); 284 | case 'arccos': 285 | return Math.acos(right); 286 | case 'arctan': 287 | return Math.atan(right); 288 | case 'ln': 289 | return Math.log(right); 290 | case 'log': 291 | return Math.log(right) / Math.log(left); 292 | default: 293 | //error! 294 | console.log(opr, 'error!'); 295 | return 0; 296 | } 297 | }; 298 | 299 | /**是否含未知量*/ 300 | ExpTreeNode.prototype._containParam = function(node) { 301 | if(node){ 302 | if(node.key == this.key) { 303 | return true; 304 | } 305 | else { 306 | return this._containParam(node.left) || this._containParam(node.right); 307 | } 308 | } 309 | return false; 310 | }; 311 | 312 | /**递归计算表达式的值*/ 313 | ExpTree.prototype.cal = function(node, x) { 314 | if(!node) { 315 | return; 316 | } 317 | if(node.left || node.right) { 318 | return this.op( node.key, this.cal(node.left, x), this.cal(node.right, x) ); 319 | } 320 | switch(node.key) { 321 | case 'x': 322 | return x; 323 | case 'e': 324 | return Math.E; 325 | case 'pi': 326 | return Math.PI; 327 | default: 328 | return node.key; 329 | } 330 | }; 331 | 332 | /**求导函数树*/ 333 | ExpTree.prototype.dao = function(node) { 334 | if(!node) { 335 | return; 336 | } 337 | var t; 338 | switch(node.key) { 339 | case '+': 340 | case '-': 341 | //(l+r)' = l' + r' 342 | //(l-r)' = l' - r' 343 | t = new ExpTreeNode(node.key, this.dao(node.left), this.dao(node.right)); 344 | break; 345 | case '*': 346 | //(l*r)' = l'*r + l*r' 347 | t = new ExpTreeNode('+'); 348 | t.left = new ExpTreeNode('*', this.dao(node.left), node.right); 349 | t.right = new ExpTreeNode('*', node.left, this.dao(node.right)); 350 | break; 351 | case '/': 352 | //(l/r)' = (l'*r - l*r') / (r*r) 353 | t = new ExpTreeNode('/'); 354 | t.left = new ExpTreeNode('-'); 355 | t.left.left = new ExpTreeNode('*', this.dao(node.left), node.right); 356 | t.left.right = new ExpTreeNode('*', node.left, this.dao(node.right)); 357 | t.right = new ExpTreeNode('*', node.right, node.right); 358 | break; 359 | case '^': 360 | t = new ExpTreeNode('*'); 361 | if(node.right._containParam(node.right)) { 362 | //(l^r)' = (l^r) * (ln(l)*r)' 363 | t.left = node; 364 | t.right = this.dao(new ExpTreeNode('*', new ExpTreeNode('ln', null, node.left), node.right)); 365 | } 366 | else { 367 | //(l^c)' = (l'*c) * l^(c-1) 368 | t.left = new ExpTreeNode('*', this.dao(node.left), node.right); 369 | t.right = new ExpTreeNode('^', node.left, new ExpTreeNode('-', node.right, new ExpTreeNode(1))); 370 | } 371 | break; 372 | case 'sin': 373 | //sin(r)' = cos(r)*r' 374 | t = new ExpTreeNode('*'); 375 | t.left = new ExpTreeNode('cos', null, node.right); 376 | t.right = this.dao(node.right); 377 | break; 378 | case 'cos': 379 | //cos(r)' = 0-sin(r)*r' 380 | t = new ExpTreeNode('-', new ExpTreeNode(0)); 381 | t.right = new ExpTreeNode('*', new ExpTreeNode('sin', null, node.right), this.dao(node.right)); 382 | break; 383 | case 'tan': 384 | //tan(r)' = r' / (cos(r) * cos(r)) 385 | t = new ExpTreeNode('/', this.dao(node.right)); 386 | t.right = new ExpTreeNode('*', new ExpTreeNode('cos', null, node.right), new ExpTreeNode('cos', null, node.right)); 387 | break; 388 | case 'cot': 389 | //cot(r)' = 0-r'/(sin(r)*sin(r)) 390 | t = new ExpTreeNode('-', new ExpTreeNode(0)); 391 | t.right = new ExpTreeNode('/', this.dao(node.right)); 392 | t.right.right = new ExpTreeNode('*', new ExpTreeNode('sin', null, node.right), new ExpTreeNode('sin', null, node.right)); 393 | break; 394 | case 'arcsin': 395 | //arcsin(r)' = r' / (1-r*r)^0.5 396 | t = new ExpTreeNode('/', this.dao(node.right)) 397 | t.right = new ExpTreeNode('^', null, new ExpTreeNode(0.5)); 398 | t.right.left = new ExpTreeNode('-', new ExpTreeNode(1), new ExpTreeNode('*', node.right, node.right)); 399 | break; 400 | case 'arccos': 401 | t = new ExpTreeNode('/', this.dao(node.right)) 402 | t.right = new ExpTreeNode('^', null, new ExpTreeNode(0.5)); 403 | t.right.left = new ExpTreeNode('-', new ExpTreeNode(1), new ExpTreeNode('*', node.right, node.right)); 404 | t = new ExpTreeNode('-', new ExpTreeNode(0), t); 405 | //arccos(r)' = 0 - (r' / (1-r*r)^0.5) 406 | break; 407 | case 'arctan': 408 | t = new ExpTreeNode('/', this.dao(node.right)) 409 | t.right = new ExpTreeNode('+', new ExpTreeNode(1), new ExpTreeNode('*', node.right, node.right)); 410 | //arcsin(r)' = r' / (1+r*r) 411 | break; 412 | case 'ln': 413 | //ln(r)' = r'/r 414 | t = new ExpTreeNode('/', this.dao(node.right), node.right); 415 | break; 416 | case 'log': 417 | //log(l,r) = r'/(r*ln(l)) 418 | t = new ExpTreeNode('/', this.dao(node.right)); 419 | t.right = new ExpTreeNode('*', node.right, new ExpTreeNode('ln', null, node.left)); 420 | break; 421 | case 'x': 422 | //x' = 1 423 | t = new ExpTreeNode(1); 424 | break; 425 | default: 426 | //常量的导数为0 427 | t = new ExpTreeNode(0); 428 | break; 429 | } 430 | return t; 431 | }; 432 | 433 | function cal(exp) { 434 | console.log(exp); 435 | var t = new ExpTree(); 436 | t.build(exp); 437 | console.log('infix:', t.toInFix()); 438 | var dtn = t.dao(t.root); 439 | var dt = new ExpTree(); 440 | dt.root = dtn; 441 | console.log('postfix:', dt.toPostFix()); 442 | var x0 = 1, 443 | x1 = 2, 444 | i = 0; 445 | while (Math.abs(x0 - x1) > 10E-9) { 446 | x0 = x1; 447 | x1 = x0 - (t.cal(t.root, x0) / dt.cal(dt.root, x0)); 448 | ++i; 449 | if (i > 10E6) { 450 | break; 451 | } 452 | } 453 | if (Math.abs(x1 - Math.round(x1)) < 10E-8) { 454 | x1 = Math.round(x1); 455 | } 456 | if (isNaN(x1)) { 457 | console.log('无解或牛顿法对此不收敛。'); 458 | } 459 | else { 460 | console.log('x = ',x1); 461 | } 462 | return x1; 463 | } 464 | 465 | cal('5x^4-9x^2+9'); 466 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Tim Wu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /LinkedList/LinkedList.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Tim Wu (吴智炜) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | /** 24 | * Linked List implementation in JavaScript 25 | */ 26 | function LinkedList () { 27 | this.head = null; 28 | //use tail for efficiency 29 | this.tail = null; 30 | }; 31 | 32 | function ListNode ( data ) { 33 | this.data = data; 34 | this.next = null; 35 | }; 36 | 37 | LinkedList.prototype.addFirst = function( element ) { 38 | var h = new ListNode( element ); 39 | h.next = this.head 40 | if( this.head == null ) { 41 | this.tail = h; 42 | } 43 | this.head = h; 44 | }; 45 | 46 | LinkedList.prototype.append = function( element ) { 47 | if( this.head == null ) { 48 | this.addFirst( element ); 49 | this.tail = this.head; 50 | } 51 | else { 52 | this.insertAfterNode( element, this.tail ); 53 | } 54 | }; 55 | 56 | LinkedList.prototype.insertAfterNode = function( element, node ) { 57 | if( node == null ) return; 58 | var n = new ListNode( element ); 59 | n.next = node.next; 60 | node.next = n; 61 | if( node == this.tail ) { 62 | this.tail = n; 63 | } 64 | }; 65 | 66 | LinkedList.prototype.insertBeforeNode = function( element, node ) { 67 | if( node == null ) return; 68 | if( node === this.head ) { 69 | this.addFirst( element ); 70 | return; 71 | } 72 | var prev = null; 73 | var cur = this.head; 74 | while( cur != null && cur !== node ) { 75 | prev = cur; 76 | cur = cur.next; 77 | } 78 | if( cur != null ) { 79 | var n = new ListNode( element ); 80 | prev.next = n; 81 | n.next = cur; 82 | } 83 | }; 84 | 85 | LinkedList.prototype.insertAfter = function( element, data ) { 86 | this.insertAfterNode( element, this.find(data) ); 87 | }; 88 | 89 | LinkedList.prototype.insertBefore = function( element, data ) { 90 | if( this.head == null ) return; 91 | if( this.head.data === data ) { 92 | this.addFirst( element ); 93 | return; 94 | } 95 | var p = this.findPrevious( data ); 96 | var prev = p[0]; 97 | var cur = p[1]; 98 | if( cur != null ) { 99 | var n = new ListNode( element ); 100 | prev.next = n; 101 | n.next = cur; 102 | } 103 | }; 104 | 105 | LinkedList.prototype.delete = function( element ) { 106 | if( this.head.data == element ) { 107 | this.head = this.head.next; 108 | return; 109 | } 110 | var p = this.findPrevious( element ); 111 | var prev = p[0]; 112 | var cur = p[1]; 113 | if( prev != null && cur != null ) { 114 | prev.next = cur.next; 115 | } 116 | }; 117 | 118 | LinkedList.prototype.find = function( element ) { 119 | var p = this.head; 120 | while( p != null && p.data != element ) { 121 | p = p.next; 122 | } 123 | return p; 124 | }; 125 | 126 | LinkedList.prototype.findPrevious = function( element ) { 127 | var prev = null; 128 | var cur = this.head; 129 | while( cur != null && cur.data != element ) { 130 | prev = cur; 131 | cur = cur.next; 132 | } 133 | return [prev, cur]; 134 | }; 135 | 136 | LinkedList.prototype.reverse = function() { 137 | var p = this.head; 138 | if( p == null ) return null; 139 | this.tail = p; 140 | var tmp, q = p.next; 141 | while( q != null ) { 142 | tmp = q.next; 143 | q.next = p; 144 | p = q; 145 | q = tmp; 146 | } 147 | this.head.next = null; 148 | this.head = p; 149 | return this; 150 | }; 151 | 152 | LinkedList.prototype.toString = function() { 153 | var p = this.head; 154 | var str = ""; 155 | while( p != null ) { 156 | str += JSON.stringify(p.data) + (p.next == null ? "" : "->"); 157 | p = p.next; 158 | } 159 | return str; 160 | }; -------------------------------------------------------------------------------- /LinkedList/Poly.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Tim Wu (吴智炜) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | /** 24 | * Polynomial implementation in JavaScript based on LinkedList.js 25 | */ 26 | function Poly( cofs, exps ) { 27 | this.head = null; 28 | this.tail = null; 29 | var node; 30 | for (var i = 0; i < cofs.length; ++i) { 31 | node = new PolyData( cofs[i], exps[i] ); 32 | this.append( node ); 33 | }; 34 | }; 35 | 36 | Poly.prototype = new LinkedList(); 37 | Poly.prototype.constructor = Poly; 38 | 39 | function PolyData ( cof, exp ) { 40 | this.cof = cof; 41 | this.exp = exp; 42 | }; 43 | 44 | Poly.prototype.add = function( p ) { 45 | var sum = new Poly( [], [] ); 46 | var p1 = this.head; 47 | var p2 = p.head; 48 | var node; 49 | while( p1 != null && p2 != null ) { 50 | if( p1.data.exp == p2.data.exp ) { 51 | node = new PolyData( p1.data.cof + p2.data.cof, p1.data.exp ); 52 | p1 = p1.next; 53 | p2 = p2.next; 54 | } 55 | else if( p1.data.exp < p2.data.exp ) { 56 | node = new PolyData( p1.data.cof, p1.data.exp ); 57 | p1 = p1.next; 58 | } 59 | else { 60 | node = new PolyData( p2.data.cof, p2.data.exp ); 61 | p2 = p2.next; 62 | } 63 | sum.append( node ); 64 | } 65 | while( p1 != null ) { 66 | node = new PolyData( p1.data.cof, p1.data.exp ); 67 | p1 = p1.next; 68 | sum.append( node ); 69 | } 70 | while( p2 != null ) { 71 | node = new PolyData( p2.data.cof, p2.data.exp ); 72 | p2 = p2.next; 73 | sum.append( node ); 74 | } 75 | return sum; 76 | }; 77 | 78 | Poly.prototype.mult = function( p ) { 79 | var mult = new Poly( [], [] ); 80 | var tmp = new Poly( [], [] ); 81 | var p1 = this.head; 82 | var p2 = p.head; 83 | var node; 84 | while( p2 != null ) { 85 | p1 = this.head; 86 | while( p1 != null ) { 87 | node = new PolyData( p1.data.cof * p2.data.cof, p1.data.exp + p2.data.exp ); 88 | tmp.append( node ); 89 | p1 = p1.next; 90 | } 91 | p2 = p2.next; 92 | mult = mult.add( tmp ); 93 | var tmp = new Poly( [], [] ); 94 | } 95 | return mult; 96 | }; 97 | 98 | Poly.prototype.toString = function( symbol ) { 99 | symbol = symbol || 'x'; 100 | var p = this.head; 101 | var str = ""; 102 | var cof; 103 | var exp; 104 | while( p != null ) { 105 | cof = p.data.cof; 106 | exp = p.data.exp; 107 | if( cof != 0 ) { 108 | str += (cof > 0 ? '+' : '-') + (Math.abs( cof ) == 1 ? (exp == 0 ? '1' : '') : Math.abs( cof )) + (exp == 0 ? '' : (exp == 1 ? symbol : symbol+'^'+exp)); 109 | } 110 | p = p.next; 111 | } 112 | if( str.length && str[0] === '+' ) { 113 | return str.slice(1); 114 | } 115 | return str == "" ? "0" : str; 116 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 数据结构与算法的JavaScript实现及应用 2 | ===== 3 | ## 文章 4 | * [数据结构与算法的JavaScript实现及应用 – 单链表](https://wuzhiwei.net/ds_app_linkedlist/) 5 | * [数据结构与算法的JavaScript实现及应用 – 栈 递归 汉诺塔](https://wuzhiwei.net/ds_app_stack/) 6 | * [数据结构与算法的JavaScript实现及应用 – 二叉查找树](https://wuzhiwei.net/ds_app_bst/) 7 | * [牛顿法与自动求解器](https://wuzhiwei.net/newton_method_auto_solver/) 8 | * [利用向量运算解决圆线碰撞问题](https://wuzhiwei.net/vector_circle_line_collide/) 9 | 10 | ## DEMO in HTML5 11 | * [简易多项式运算](https://jsfiddle.net/timwzw/ZFprM/) 12 | * [简易计算器](https://jsfiddle.net/timwzw/66GDv/) 13 | * [汉诺塔](https://jsfiddle.net/timwzw/S7mYF/) 14 | * [二叉查找树测试](https://jsfiddle.net/timwzw/B3fh5/) 15 | * [自动求解器](https://jsfiddle.net/timwzw/0efcfubd/) 16 | * [圆线相交检测](https://jsfiddle.net/timwzw/xJj5z/) 17 | * [圆线碰撞](https://jsfiddle.net/timwzw/bLGC6/) 18 | 19 | 欢迎关注公众号: 20 | ![image](https://user-images.githubusercontent.com/1621110/215088294-fc24b001-23d3-40e4-be2e-1f50a0d6d936.png) 21 | -------------------------------------------------------------------------------- /Stack/InfixExpression.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014 Tim Wu (吴智炜) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | /** 24 | * 栈的应用-简易表达式求值 25 | */ 26 | var prioty = 27 | { 28 | "+":1, 29 | "-":1, 30 | "%":2, 31 | "*":2, 32 | "/":2, 33 | "^":3, 34 | "(":0, 35 | ")":0, 36 | "`":-1, 37 | }; 38 | 39 | function doop( op, opn1, opn2 ) { 40 | switch( op ) { 41 | case "+": 42 | return opn1 + opn2; 43 | case "-": 44 | return opn1 - opn2; 45 | case "*": 46 | return opn1 * opn2; 47 | case "/": 48 | return opn1 / opn2; 49 | case "%": 50 | return opn1 % opn2; 51 | case "^": 52 | return Math.pow( opn1, opn2 ); 53 | default: 54 | return 0; 55 | } 56 | } 57 | 58 | function opcomp( a, b ) { 59 | return prioty[a] - prioty[b]; 60 | } 61 | 62 | //支持 加+ 减- 乘* 除/ 乘方^ 求余% 63 | function calInfixExpression( exp ) { 64 | var cs = []; 65 | var ns = []; 66 | var exp = exp.replace( /\s/g, "" ); 67 | exp += '`'; 68 | var c; 69 | var op; 70 | var opn1; 71 | var opn2; 72 | for (var i = 0; i < exp.length; ++i) { 73 | c = exp[i]; 74 | if( c in prioty ) { 75 | //op 76 | while( c != '(' && cs.length && opcomp( cs[cs.length-1], c ) >= 0 ) { 77 | op = cs.pop(); 78 | if( op != '(' && op != ')' ){ 79 | opn2 = ns.pop(); 80 | opn1 = ns.pop(); 81 | ns.push( doop( op, opn1, opn2 ) ); 82 | } 83 | } 84 | if( c != ')' ) cs.push( c ); 85 | } 86 | else { 87 | while( !(exp[i] in prioty) ) { 88 | i++; 89 | c += exp[i]; 90 | } 91 | ns.push( parseFloat(c) ); 92 | i--; 93 | } 94 | } 95 | return ns.length ? ns[0] : NaN; 96 | } 97 | --------------------------------------------------------------------------------