红黑树(Red Black Tree) 是一种平衡二叉搜索树。
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 节点是红色或黑色。
性质2. 根节点是黑色。
性质3 每个叶节点(NIL节点,空节点)是黑色的。
性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
了解平衡二叉树的两个基本操作:左旋、右旋。
插入
新插入的节点都是叶节点,如果破坏了树的性质,就从新插入的节点开始,通过左旋右旋操作,来保持树的性质,这样可能会导致上面的节点不满足树的性质,往上冒泡,继续操作,直到所有节点都满足树的性质。
红黑树插入元素时,只会破坏性质2或者性质4,破坏性质2的时候,节点肯定是根节点,直接把节点的颜色设置为Black就行,麻烦的是破坏性质4的时候的处理。
破坏性质4的时候,分成3种情况,1、叔节点是红色;2、叔节点是黑色,当前节点是父节点的左(右)孩子,父节点是祖父节点的右(左)孩子,(当前节点、父节点、祖父节点不在一条线上);3、叔节点是黑色,当前节点是父节点的左(右)孩子,父节点是祖父节点的左(右)孩子,(当前节点、父节点、祖父节点在一条线上);
处理方式:1、改变父节点、叔节点、祖父节点的颜色,祖父节点作为当前节点;(会变成2或3)
2、父节点作为当前节点,然后左旋(右旋),(会变成3)
3、改变父节点、祖父节点颜色,右旋(左旋)。
删除
删除时,和普通搜索二叉树一样,只是删除过程中需要额外记录一些信息,删除之后,可能会破坏性质1、2、4,需要通过改变颜色,左旋右旋来保持红黑树的性质。
有4种情况:1、x的兄弟节点w是红色的;2、x的兄弟节点w是黑色的,而且w的两个子节点都是黑色的;3、x的兄弟节点w是黑色的,w的左孩子是红色的,w的有孩子是黑色的;4、x的兄弟节点w是黑色的,w的右孩子是红色的。
处理方式:
实现代码如下:
<canvas id = "canvas" width="1000" height="600" style="width:1000px;height:600px;"></canvas> <script> // 用canvas将树可视化 var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); function drawTree(t){ ctx.clearRect(0,0,1000,600); drawNode(t.root, 30, 30); } function drawNode(node, x, y){ if(!node.key){ return {x: x, w: 30}; } var o1 = drawNode(node.left, x, y + 30); var o2 = drawNode(node.right, o1.x + o1.w / 2, y + 30); var x = (o1.x + o2.x) / 2; ctx.strokeStyle=node.color=='black'?"#000000":"#ff0000"; ctx.beginPath(); ctx.arc(x,y,10,0,2*Math.PI); ctx.stroke(); ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(o1.x, y+30); ctx.moveTo(x, y); ctx.lineTo(o2.x, y+30); ctx.stroke(); ctx.strokeText(node.key+"",x,y); return {x: x, w: o1.w+o2.w}; } </script> <script> // 测试红黑树 var t = {}; t.nil = {color:'black'}; t.root = t.nil; var a = []; console.log(a); var n = 20; for(var i = 0; i < n; i++){ var k = 1 + Math.floor(Math.random()*n); a.push(k); // console.log('插入:',k); // rbInsert(t, {key: k}); } // drawTree(t); // console.log(t); // console.log("中序遍历:"); // inOrderTreeWalk(t.root); // 删除测试 /*while(a.length){ var o = treeSearch(t.root, a.pop()); rbDelete(t, o); console.log("中序遍历:"); inOrderTreeWalk(t.root); }*/ // 插入 var i = 0; var delay = 1000; setTimeout(charu, delay); function charu(){ var k = a[i]; console.log('插入:',k); rbInsert(t, {key: k}); drawTree(t); i++; if(i<n){ setTimeout(charu, delay); } else { i=0; console.log("中序遍历:"); inOrderTreeWalk(t.root); setTimeout(shanchu, delay); } } function shanchu(){ var o = treeSearch(t.root, a[i]); rbDelete(t, o); drawTree(t); i++; if(i<n){ setTimeout(shanchu, delay); } } /* // 找BUG a = [6, 3, 1, 8, 4, 8, 9, 3, 9, 6]; test(); function test(){ t = {}; t.nil={color:'black'}; t.root = t.nil; var n = 0; let loop = ()=>{ rbInsert(t,{key:a[n]}); n++; if(n<a.length){ setTimeout(loop, 3000); }; drawTree(t); }; setTimeout(loop, 3000); }*/ </script> <script type="text/javascript"> function inOrderTreeWalk(x){ if(x != t.nil){ inOrderTreeWalk(x.left); console.log(x.key); inOrderTreeWalk(x.right); } } function preOrderTreeWalk(x){ if(x != t.nil){ console.log(x.key); preOrderTreeWalk(x.left); preOrderTreeWalk(x.right); } } function postOrderTreeWalk(x){ if(x != t.nil){ postOrderTreeWalk(x.left); postOrderTreeWalk(x.right); console.log(x.key); } } function treeSearch(x, k){ if(!x || x.key == k){ return x; } if(k < x.key){ return treeSearch(x.left, k); } return treeSearch(x.right, k); } function leftRotate(t, x){ var y = x.right; x.right = y.left; if(y.left!=t.nil){ y.left.parent = x; } y.parent = x.parent; if(x.parent == t.nil){ t.root = y; } else if(x == x.parent.left){ x.parent.left = y; } else { x.parent.right = y; } y.left = x; x.parent = y; } function rightRotate(t, y){ var x = y.left; y.left = x.right; if(x.right!=t.nil){ x.right.parent = y; } x.parent = y.parent; if(y.parent == t.nil){ t.root = x; } else if(y == y.parent.left){ y.parent.left = x; } else { y.parent.right = x; } x.right = y; y.parent = x; } function rbInsert(t, z){ var y = t.nil; var x = t.root; while(x!=t.nil){ y = x; if(z.key < x.key){ x = x.left; } else { x = x.right; } } z.parent = y; if(y == t.nil){ t.root = z; } else if(z.key < y.key){ y.left = z; } else { y.right = z; } z.left = t.nil; z.right = t.nil; z.color = 'red'; rbInsertFixUp(t, z); } function rbInsertFixUp(t, z){ while(z.parent.color=='red'){ if(z.parent == z.parent.parent.left){ var y = z.parent.parent.right; if(y.color=='red'){ z.parent.color = 'black'; y.color = 'black'; z.parent.parent.color = 'red'; z = z.parent.parent; } else{ if(z == z.parent.right){ z = z.parent; leftRotate(t, z); } z.parent.color = 'black'; z.parent.parent.color = 'red'; rightRotate(t, z.parent.parent); } } else { var y = z.parent.parent.left; if(y.color == 'red'){ z.parent.color = 'black'; y.color = 'black'; z.parent.parent.color = 'red'; z = z.parent.parent; } else { if(z == z.parent.left){ z = z.parent; rightRotate(t, z); } z.parent.color = 'black'; z.parent.parent.color = 'red'; leftRotate(t, z.parent.parent); } } } t.root.color = 'black'; } function rbTransplant(t, u, v){ if(u.parent==t.nil){ t.root = v; } else if(u==u.parent.left){ u.parent.left = v; } else { u.parent.right = v; } v.parent = u.parent; } function treeMinimum(x){ while(x.left != t.nil){ x = x.left; } return x; } function treeMaximum(x){ while(x.right != t.nil){ x = x.right; } return x; } function rbDelete(t, z){ var y = z; y.originalColor = y.color; if(z.left == t.nil){ x = z.right; rbTransplant(t, z, z.right); } else if(z.right == t.nil){ x = z.left; rbTransplant(t, z, z.left); } else { y = treeMinimum(z.right); y.originalColor = y.color; x = y.right; if(y.parent == z){ x.parent = y; } else { rbTransplant(t, y, y.right); y.right = z.right; y.right.parent = y; } rbTransplant(t, z, y); y.left = z.left; y.left.parent = y; y.color = z.color; } if(y.originalColor == 'black'){ rbDeleteFixUp(t, x); } } function rbDeleteFixUp(t, x){ while(x!=t.root && x.color=='black'){ if(x == x.parent.left){ var w = x.parent.right; if(w.color=='red'){ w.color = 'black'; x.parent.color = 'red'; leftRotate(t, x.parent); w = x.parent.right; }if(!w.right||!w.left)debugger if(w.left.color == 'black' && w.right.color == 'black'){ w.color = 'red'; x = x.parent; } else{ if(w.right.color == 'black'){ w.left.color = 'black'; w.color = 'red'; rightRotate(t, w); w = x.parent.right; } w.color = x.parent.color; x.parent.color = 'black'; w.right.color = 'black'; leftRotate(t, x.parent); x = t.root; } } else { var w = x.parent.left; if(w.color=='red'){ w.color = 'black'; x.parent.color = 'red'; rightRotate(t, x.parent); w = x.parent.left; }if(!w.right||!w.left)debugger if(w.right.color == 'black' && w.left.color == 'black'){ w.color = 'red'; x = x.parent; } else{ if(w.left.color == 'black'){ w.right.color = 'black'; w.color = 'red'; leftRotate(t, w); w = x.parent.left; } w.color = x.parent.color; x.parent.color = 'black'; w.left.color = 'black'; rightRotate(t, x.parent); x = t.root; } } } x.color = 'black'; } </script>
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。