红黑树4----红黑树总结以及图形化显示

红黑树的演变过程:

二叉排序树-----2-3-4树-----2-3树------红黑树

二叉排序树BST

二叉排序树是一颗二叉树,对于每个节点的值有   左节点  <  根节点  <  右节点

为了便于理解,这里使用递归来实现其基本操作方法

查找方法

        private Value get(Node x, Key key){
		if(x == null) return null;
		int cmp = key.compareTo(x.key);
		if(cmp>0)
			return get(x.right, key);
		else if(cmp<0)
			return get(x.left, key);
		else
			return x.value;
	}

插入方法

        private Node put(Node x, Key key, Value val) {
		if(x == null)
			return new Node(key, val, 1);
		int cmp = key.compareTo(x.key);
		if(cmp>0)
			x.right = put(x.right, key, val);
		else if(cmp<0)
			x.left = put(x.left, key, val);
		else
			x.value = val;
		x.N = size(x.left) + size(x.right) + 1;
		return x;		
	}

删除方法   基本思想用待删除节点的右子树的最小值来代替该节点

        private Node delete(Node x, Key key){
		if(x==null) return null;
		int cmp = key.compareTo(x.key);
		if(cmp<0) x.left = delete(x.left, key);
		if(cmp>0) x.right = delete(x.right, key);
		if(x.left==null) return x.right;
		if(x.right==null) return x.left;
		Node t = x;
		x = min(t.right);
		x.right = deleteMin(t.right);
		x.left = t.left;
		x.N = size(x.left) + size(x.right) + 1;
		return x;
	}

二叉排序树当输入序列有序时,树的高度会急剧增加,从而使得基本操作时间复杂度为O(N)。

2-3-4树以及LLRB

由二叉树出发,进一步定义2-3-4树,每个节点有三种类型2节点,3节点以及4节点。

定义2-3树,每个节点有三种类型2节点,3节点。

2-3-4树的插入算法

查找到待插入节点合适位置,如果节点为2,3节点则直接插入即可

                            

                              

如果节点为4节点,则不能直接插入,此时应该将4节点分解,然后执行插入,由于4节点的插入可能导致4节点的父节点结构破坏,因此需要依次向上判断处理父节点是否满足基本性质。

对于4节点的分解过程,有两种思路:

由上到下,在向下寻找带插入位置时,遇见4节点即进行变换;

由下到上,找到待插入位置,如果是4节点则进行分解,插入新节点,依次向上检查父节点是否满足性质,否则继续分解父节点

                   

4节点的分解

对于4节点的分解,分为两种情况:

4节点的父节点为2节点:  将4节点中间值放到父节点中

                                                  

                                 

4节点的父节点为3节点:  将4节点中间值提取到父节点  其余分解为2个2节点

                               

                                  

利用由上到下思想,对于搜索路径的每一个4节点使用上述变换,易得最终插入位置必为非4节点,则直接插入即可。

红黑树

进一步演化为红黑树,使用节点颜色,连接颜色来等价性表示2-3-4树。

左倾红黑树LLRB:将2-3-4树中的3节点,4节点使用红连接来表示。

从红连接角度定义红黑树:

每个节点颜色由指向该节点的连接颜色决定,红色或者黑色;

所有红连接均为左连接

一个节点不能同时与两个红连接相连

完美黑色平衡:空连接到根节点的路径上黑连接数目相同

     

红黑树的基本变换

1.左旋转 

将红连接左旋转  不会破坏完美黑色平衡

                                  

        // 左旋转h的右连接
	private Node rotateLeft(Node h) {
		// System.out.println("rotateleft before " + h);
		// 修改连接
		Node x = h.right;
		h.right = x.left;
		x.left = h;
		// 修改颜色
		x.color = h.color;
		h.color = RED;
		// 修改数量
		x.N = h.N;
		h.N = size(h.left) + size(h.right) + 1;
		// System.out.println("rotateleft after " + x);
		return x;
	}

2.右旋转

                                         

       // 右旋转h的左连接
	private Node rotateRight(Node h) {
		Node x = h.left;
		h.left = x.right;
		x.right = h;
		x.color = h.color;
		h.color = RED;
		x.N = h.N;
		h.N = size(h.left) + size(h.right) + 1;
		return x;
	}

3.颜色变换  分解4节点

                         

        // 分解4节点
	private void flipColors(Node h) {
		h.color = RED;
		h.left.color = BLACK;
		h.right.color = BLACK;
	}

4.颜色变换   合并生成4节点   分解4节点的逆过程

        // 生成4节点
	private void flipColorsD(Node h) {
		h.color = BLACK;
		h.left.color = RED;
		h.right.color = RED;
	}

5.由下到上调整,保持红连接均为左连接  分解4节点

        private Node fixUp(Node h) {
		if (isRed(h.right))
			h = rotateLeft(h);
		if (isRed(h.left) && isRed(h.left.left))
			h = rotateRight(h);
		if (isRed(h.left) && isRed(h.right))
			flipColors(h);
		return h;
	}

6.将某节点的右子2节点转换为非2节点  此时可以直接删除右子节点

      

        private Node moveRedRight(Node h) {
		// 与兄弟节点合并
		flipColorsD(h);
		// 从兄弟节点借一个值
		if (isRed(h.left.left)) {
			h = rotateRight(h);
			flipColors(h);
		}
		return h;
	}

7.将某节点的左子2节点转换为非2节点  此时可以直接删除左子节点

   

        private Node moveRedLeft(Node h) {
		flipColorsD(h);
		if (isRed(h.right.left)) {
			h.right = rotateRight(h.right);
			h = rotateLeft(h);
			flipColors(h);
		}
		return h;
	}

红黑树插入算法实现

插入算法基本思路两种:

2-3树  由下到上  插入新节点  左旋  右旋  分解四节点

2-3-4树  由上到下   分解4节点  插入新节点  左旋 右旋

利用BST插入算法思想,执行节点插入,将新节点设定为红色,利用红连接与父节点进行相连,需要进行调整使其满足红黑树的定义。

该方式可以认为是由下到上方式,先插入新节点,然后进行调整。

插入新节点  左旋  右旋  颜色变换分解4节点  其实此时红黑树等价于一颗2-3树,只有2节点或者3节点

        private Node put(Node h, Key key, Value val) {
		if (h == null)
			return new Node(key, val, 1, RED);
		int cmp = key.compareTo(h.key);
		if (cmp > 0)
			h.right = put(h.right, key, val);
		else if (cmp < 0)
			h.left = put(h.left, key, val);
		else
			h.val = val;
		if (!isRed(h.left) && isRed(h.right))
			h = rotateLeft(h);
		if (isRed(h.left) && isRed(h.left.left))
			h = rotateRight(h);
		if (isRed(h.left) && isRed(h.right))
			flipColors(h);
		h.N = size(h.left) + size(h.right) + 1;
		return h;
	}

	private boolean isRed(Node x) {
		if (x == null)
			return false;
		return x.color;
	}

也可以先进行分解4节点,然后进行节点插入,进行基本调整。

4节点是如何分解的

当父节点是2节点以及3节点时,具体变换规则如下所示,颜色变换分解4节点,左旋,右旋即可。此时红黑树等价于一颗2-3-4树,有2,3,4节点类型。

       

      

下面是由上到下思路在2-3-4树中插入新节点代码:

        

红黑树的删除算法实现

删除算法基本思路:

从根节点出发,寻找待删除节点位置;

如果遇见2节点,则利用基本变换将2节点转换为非2节点;

找到待删除节点,将待删除节点右子树节点替换为该节点,然后删除右子树的最小值节点;

删除最小值

删除最小值   往左子树中寻找最小值,当遇见2节点时,将2节点转换为非2节点,从父节点借一个节点或者从兄弟节点中借一个节点

        public void deleteMin() {
		root = deleteMin(root);
		root.color = BLACK;
	}

	private Node deleteMin(Node h) {
		if (h.left == null)
			return null;
		if (!isRed(h.left) && !isRed(h.left.left))
			h = moveRedLeft(h);
		h.left = deleteMin(h.left);
		return fixUp(h);
	}

删除最大值

删除最大值   往右子树寻找  如果遇见2节点,从父节点中借一个节点或者从兄弟节点中借一个节点

        private void deleteMax() {
		root = deleteMax(root);
		root.color = BLACK;
	}

	private Node deleteMax(Node h) {
		// 父节点为非2节点 从父节点借一个值
		if (isRed(h.left))
			h = rotateRight(h);
		if (h.right == null)
			return null;
		// 从兄弟节点处理,借一个值或者直接与兄弟节点进行合并
		if (!isRed(h.right) && !isRed(h.right.left))
			h = moveRedRight(h);
		h.right = deleteMax(h.right);
		return fixUp(h);
	}

删除任意值

        public void delete(Key key) {
		root = delete(root, key);
		root.color = BLACK;
	}

	private Node delete(Node h, Key key) {
		int cmp = key.compareTo(h.key);
		//往左子树寻找待删除节点
		if (cmp < 0) {
			//如果该节点为2节点,将2节点转换为非2节点,从父节点借或者从兄弟节点中借
			if (!isRed(h.left) && !isRed(h.left.left))
				h = moveRedLeft(h);
			h.left = delete(h.left, key);
		} else {
			//在右子树中  如果出现2节点转换为非2节点
			if (isRed(h.left))
				h = rotateRight(h);
			//上述右旋转会改变h指针,因此需要重新判断
			if (key.compareTo(h.key) == 0 && h.right == null)
				return null;
			if (!isRed(h.right) && !isRed(h.right.left))
				h = moveRedRight(h);
			//如果找到待删除节点,则将待删除节点的右子树最小值替换为该节点,然后删除右子树的最小值
			//上述2节点转换操作会改变h指针  因此需要重新判断
			if (key.compareTo(h.key) == 0) {
				h.key = min(h.right).key;
				h.val = get(h.right, h.key);
				h.right = deleteMin(h.right);
			} else {
				h.right = delete(h.right, key);
			}
		}
		return fixUp(h);
	}

红黑树的性能

1.一颗大小为N的红黑树高度不会超过2lgN

2.一颗大小为N的红黑树,根节点到任意节点平均路径长度为 1.00lgN

3.在红黑树中,以下操作都是对数级别的:查找  插入  删除

红黑树的图形化展示

参考之前BST图形化显示:https://blog.csdn.net/u014106644/article/details/89670061

将红黑树转换为完全二叉树,计算相对坐标,利用SWT来绘制红黑树

红黑树层序遍历转换为完全二叉树

        public List<List<Node>> levelOrder2(){
  		if(root == null) return null;
		List<List<Node>> ll = new ArrayList<>();
		Queue<Node> q = new LinkedList<>();
		q.add(root);
		int h = height();
		int depth = 1;
		while(q.size()>0){
			int s = q.size();
			List<Node> lk = new ArrayList<>();
			while(s>0){
				Node tmp = q.poll();
				lk.add(tmp);
				if(tmp.left!=null){
					q.add(tmp.left);
				}else{
					q.add(new Node((Key)specialChar, (Value)specialChar, 1));
				}
				if(tmp.right!=null){
					q.add(tmp.right);
				}else{
					q.add(new Node((Key)specialChar, (Value)specialChar, 1));
				}
				s--;
			}
			ll.add(lk);
			if(depth>h-1)
				break;
			depth++;			
		}
		return ll;
	}

计算红黑树每一层的起始坐标以及每一层节点间隔距离

        public List<List<Node>> calculateNodeCoordinate2(){
		int h = height();		
		List<List<Node>> ll = levelOrder2(); 
		for(int i=1; i<=h; i++){			
			int lineStart = pow2(h-i+1)-2;
			int offset = pow2(h-i+2);
			int t = lineStart;
			for(int j=0; j<ll.get(i-1).size(); j++){
				Node n = ll.get(i-1).get(j);
				n.x = t;
				n.y = i;
				t = t + offset;
			}
		}
		return ll;	
	}

利用SWT来绘制红黑树节点以及链接

        private void showRBTNode(Group group){
		rbtll = rbt.calculateNodeCoordinate2();
		rbth = rbt.height();
		//绘制节点
		for(int i=1; i<=rbth; i++){
			for(int j=0; j<rbtll.get(i-1).size(); j++){	
				RBTCoordinate<Integer, String>.Node nc = rbtll.get(i-1).get(j);
				if(String.valueOf(nc.key).equals(RBTCoordinate.specialChar))
					continue;
				Label lname = new Label(group, SWT.NONE|SWT.CENTER);
				lname.setBounds(getNodeLabelStartX(nc.x), getNodeLableStartY(nc.y), labelW, labelH);
				lname.setText(String.valueOf(nc.key));
				//System.out.println("i = " + i + " x = " + nc.getX() + " y = " + nc.getY());
			}
		}
	}
        private void showRBTLink(Group group) {
		// group.redraw();
		// 绘制连接
		gc = new GC(group);
		gc.setLineWidth(2);
		for (int i = 1; i <= rbth; i++) {
			for (int j = 0; j < rbtll.get(i - 1).size(); j++) {
				RBTCoordinate<Integer, String>.Node nc = rbtll.get(i - 1).get(j);
				if (nc.left != null) {
					setGCColorByNode(nc.left);
					gc.drawLine(getNodeLabelStartX(nc.x) + labelW / 2, 
							getNodeLableStartY(nc.y) + labelH,
							getNodeLabelStartX(nc.left.x) + labelW / 2,
							getNodeLableStartY(nc.left.y));
				}
				if (nc.right != null) {
					setGCColorByNode(nc.right);
					gc.drawLine(getNodeLabelStartX(nc.x) + labelW / 2, 
							getNodeLableStartY(nc.y) + labelH,
							getNodeLabelStartX(nc.right.x) + labelW / 2,
							getNodeLableStartY(nc.right.y));

				}
			} // end for
		}
	}
	
	private void setGCColorByNode(RBTCoordinate<Integer, String>.Node nc){
		 if(nc.color)
			 gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_RED));
		 else
			 gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
	}

最终显示效果如下:

数组初始化  增加节点  删除节点

理解红黑树插入以及删除的变换操作,可以从2-3-4树的插入以及删除操作来入手理解,核心是插入时如何分解4节点,删除时如何构造4节点。

代码位置:https://github.com/ChenWenKaiVN/TreeSWT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值