父子结构查询_Java面试准备(5)之数据结构与算法——红黑树
歡迎點贊評論+關注~~~~~~~
如上圖,二叉查找樹極端情況下可能會變成一個單鏈表,這種查詢時間復雜度就變成O(n)了,紅黑樹在二叉查找樹的基礎上進行了自平衡。
1.原理分析
如上圖,紅黑樹具有以下特征:
1. 每個節點要么是黑色,要么是紅色
2. 根節點是黑色
3. 每個葉子節點都是黑色的空結點(NIL結點)
4. 如果一個節點是紅色的,則它的子節點必須是黑色的
5. 從任一結點到其每個葉子的所有路徑都包含相同數目的黑色結點
6.新插入節點默認為紅色,插入后需要校驗紅黑樹是否符合規則,不符合則需要進行平衡
紅黑樹節點的增加刪除都需要保證滿足以上特征,紅黑樹采取多種方式來維護這些特征,從而維持平衡。
主要包括:左旋轉、右旋轉、顏色反轉
1.1左旋(RotateLeft )
逆時針旋轉紅黑樹的兩個結點,使得父結點被自己的右孩子取代,而自己成為自己的左孩子。
如圖所示,以X為基點逆時針旋轉,X的父節點被x原來的右孩子Y取代,c保持不變 ,Y節點原來的左孩子c變成X的右孩子,這樣的旋轉仍然保證了二叉查找樹的特征,左節點比父節點小,右節點仍然比父節點大,即a
1.2 右旋(RotateRight)
順時針旋轉紅黑樹的兩個結點,使得父結點被自己的左孩子取代,而自己成為自己的右孩子。
如圖所示,以X為基點順時針旋轉 ,X的父節點被x原來的左孩子Y取代 ,b保持不變 ,Y節點原來的右孩子c變成X的左孩子 。這樣的旋轉仍然保證了二叉查找樹的特征,左節點比父節點小,右節點仍然比父節點大,即b
1.3顏色反轉
新增節點默認為紅色,而父節點和叔叔節點也為紅色,這種情況就違反了紅黑樹的規則(父與子不能同時為紅色),需要將紅色往祖輩上傳,父節點和叔叔節點變為黑色,這樣來保證每條葉子結點到根節點的黑色節點數量并未發生變化。
插入新節點的時候存在如下幾種情況:
1.插入節點位于樹根,沒有父節點,這種情況,直接讓新結點變色為黑色.
2.插入節點的父節點是黑色,新插入的紅色節點并沒有打破紅黑樹的規則,所以不需要做任何調整
3.插入節點的父節點和叔叔節點是紅色
此種情況,不滿足父子節點不能同時為紅色的規則,先讓B變成黑色,這樣又不滿足某一個節點到每個葉子節點經過的黑色節點數相同的規則,先讓A變成紅色,
此時,A、C父子節點又變成了紅色,所以將C調整為黑色,來使這一局部滿足紅黑樹的規則
4.插入節點的父節點是紅色,叔叔節點是黑色或者沒有叔叔,且插入節點是父節點的右孩子,父節點是祖父節點點的左孩子,
以結點B為軸,做一次左旋轉,使得新結點D成為父結點,原來的父結點B成為D的左孩子 ,變成父節點是紅色,叔叔節點是黑色或者沒有叔叔,新插入節點是父結點的左孩子,父節點是祖父節點的左孩子,以結點A為軸,做一次右旋轉,使得結點B成為祖父結點,結點A成為結點B的右孩子,
接著,將B變成黑色,A變成紅色,就局部滿足紅黑樹的規則了。
小結:旋轉和顏色反轉都是為了是樹滿足紅黑樹的5個特性,從而達到自平衡的效果。
變化規律總結:根節點必黑,新增是紅色,只能黑連黑,不能紅連紅; 爸叔通紅就變色,爸紅叔黑就旋轉,哪邊黑往哪邊轉。
2.代碼實現
2.1 紅黑樹節點結構
class RBTreeNode{private int value;//數據 private boolean isBlack;//紅黑標記 private RBTreeNode left;//左節點 private RBTreeNode right;//右邊節點 private RBTreeNode parent;//父節點 public RBTreeNode(int value) { this.isBlack=false;//默認節點為紅色 this.value = value; } setter,getter....}2.2 遍歷節點方法
為了方便獲取節點數據,編寫一個遍歷樹的方法,使用遞歸的方式實現,在樹中遞歸很常用。
public void traverse(RBTreeNode node){ if (node==null) return; //遞歸終止條件 if (node.getLeft()==null&& node.getRight()==null){ //葉子節點 System.out.println(node.getValue()); return ; } //先遍歷左節點,后遍歷右節點 traverse(node.getLeft()); traverse(node.getRight()); }2.3 新增節點
主要是從根節點依次對比 data和node.getValue的大小,一直找到被掛載得節點,在進行掛載。
public void insert(int data){ RBTreeNode node=new RBTreeNode(data); if (root==null){ //插入根節點 root=node; root.setBlack(true);//樹根是黑色 return; } RBTreeNode parent=root; RBTreeNode son; if (data2.4 自平衡方法
自平衡方法按照需求可以拆分為:左旋轉、右旋轉、設置黑色、設置紅色方法
2.4.1 左旋轉方法
結合圖查看代碼邏輯,更清晰旋轉過程。
/** * 左旋轉(逆時針旋轉) * @param node */ private void leftRotate(RBTreeNode node) { RBTreeNode right = node.getRight(); RBTreeNode parent = node.getParent(); if (parent==null){ //root節點,將 root=right; right.setParent(null); }else{ if (parent.getLeft()!=null&&parent.getLeft()==node){ // 左右 //node是左子節點,左旋轉直接將node右子節點旋轉掛到父節點左節點 parent.setLeft(right); }else { //右右的情況 //node是子節點,直接將node的右子節點旋轉掛到父節點的右子節點 parent.setRight(right); } right.setParent(parent); } node.setParent(right); node.setRight(right.getLeft()); if (right.getLeft()!=null){ right.getLeft().setParent(node); } right.setLeft(node); }2.4.2 右旋轉方法
/** * 右旋轉(順時針) * @param node */ private void rightRotate(RBTreeNode node) { RBTreeNode left = node.getLeft(); RBTreeNode parent = node.getParent(); if (parent == null) { //根節點 root = left; left.setParent(null); } else { if (parent.getLeft() != null && parent.getLeft() == node) { //將node的左子節點掛父節點 parent.setLeft(left); } else { //將node的左節點掛父節點的右節點 parent.setRight(left); } left.setParent(parent); } node.setParent(left); node.setLeft(left.getRight()); if (left.getRight() != null) { left.getRight().setParent(node); } left.setRight(node); }2.4.3 設置紅黑色方法
private void setRed(RBTreeNode node) { node.setBlack(false); } private void setBlack(RBTreeNode node) { node.setBlack(true); }2.4.4 自平衡方法
/** * 節點自平衡 * @param node */ private void nodeBanlance(RBTreeNode node) { RBTreeNode father,gFather;//父節點,祖父節點 //父節點是紅色的 while((father=node.getParent())!=null && father.isBlack()==false){ gFather=father.getParent(); if (gFather.getLeft()==father){ //父節點在祖父節點的左側 RBTreeNode uncle = gFather.getRight();//叔叔節點 if (uncle!=null&& !uncle.isBlack()){ //叔叔節點不為空 且是紅色的 //這種情況需要反色,父節點和叔叔節點變黑,爺爺節點變紅 setBlack(father); setBlack(uncle); setRed(gFather); //繼續循環 node=gFather; continue; } if (node==father.getRight()){ //需要左旋 leftRotate(father); RBTreeNode tmp=node; node=father; father=tmp; } setBlack(father); setRed(gFather); //右旋 rightRotate(gFather); }//父為祖右孩子 else { RBTreeNode uncle = gFather.getLeft(); if (uncle != null && !uncle.isBlack()) { setBlack(father); setBlack(uncle); setRed(gFather); node = gFather; continue; } if (node == father.getLeft()) { //右旋 rightRotate(father); RBTreeNode tmp = node; node = father; father = tmp; } setBlack(father); setRed(gFather); //左旋 leftRotate(gFather); } } setBlack(root); }2.5 總結
查詢時間復雜度 O(logn)
應用場景:
在JDK1.8中HashMap使用數組+鏈表+紅黑樹的數據結構。內部維護著一個數組table,該數組保存著每個鏈表的表頭結點或者樹的根節點。
總結
以上是生活随笔為你收集整理的父子结构查询_Java面试准备(5)之数据结构与算法——红黑树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 想要成为软件开发中的王者,需要明白的 2
- 下一篇: java string类的方法_Java