06
2018
09

红黑树

红黑树(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>

https://github.com/hanyeah/lianxi/tree/master/算法导论/13

« 上一篇下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。