紅黑樹插入時的自平衡

紅黑樹插入時的自平衡

紅黑樹實質上是一棵自平衡的二叉查找樹,引入帶顏色的節點也是為了方便在進行插入或刪除操作時,如果破壞了二叉查找樹的平衡性能通過一系列變換保持平衡。

紅黑樹的性質

  1. 每個節點要么是紅色,要么是黑色
  2. 根節點必須是黑色
  3. 兩個紅色節點不能相連
  4. 從根節點出發到達任意葉子節點經過的黑色節點個數相同

紅黑樹的數據結構

紅黑樹實質上是一顆二叉查找樹,左子樹的值小于根節點的值,右子樹的值大于根節點的值。

在這里插入圖片描述

public class RedBlackTree {private static int BLACK = 1;private static final int RED = 0;private static Node root;private static class Node {private int color = RED;private int data;private Node left;private Node right;private Node parent;Node(int data) {this.data = data;}}
}

紅黑樹的插入

插入的節點默認是紅色的(要不然全是黑色節點它也滿足紅黑樹的定義,不過就沒意義了);

由于紅黑樹是一顆二叉查找樹,所以它的插入可以使用遞歸(先不考慮破壞紅黑樹的結構)

    /*** 通過遞歸往紅黑樹中插入一個新節點* @param root 要插入的樹的根節點* @param data 新節點的值*/private void insert(Node root, int data) {if(root.data > data) {if(root.left == null) {Node node = new Node(data);root.left = node;node.parent = root;} else {insert(root.left, data);}}else {if(root.right == null) {Node node = new Node(data);root.right = node;node.parent = root;} else {insert(root.right, data);}}}

調整結構

新插入節點后,可能破壞紅黑樹的定義,雖然紅黑樹的定義有四條,前兩條都是確定了的,不會因為新添加節點而被破壞,只需要關注第三條就可以了(滿足前三條第四條就會自然滿足)

    /*** 判斷插入新節點后紅黑樹結構是否需要變化* 根據紅黑樹的定義,兩個紅色節點不能連接* @param root 插入的新節點* @return 返回true表示插入新節點后破壞了紅黑樹的結構,*         需要通過旋轉變色來糾正,否則不需要修改。*/private boolean checkStruct(Node root) {return root.color == RED && root.parent.color == RED;}

所以只要新插入的節點的父節點是紅色,就需要調整結構。調整結構的辦法有三種:

1. 變色

就是把紅色變為黑色,黑色變為紅色

2. 左旋

在這里插入圖片描述

以節點C為軸左旋的步驟:

  1. 將C的父節點A沉下來,C升上去作為新的父節點
  2. 將原來C的左子樹掛到A的右子樹上
  3. 其他不變

在這里插入圖片描述

        /*** 左旋*   - 旋轉前的右子節點變成旋轉后的父節點*   - 旋轉前的父節點(軸)變為旋轉后父節點的左子節點*   - 旋轉前軸的右子節點的右子節點旋轉后變為軸的右子節點*   - 旋轉前右子節點的左子樹變成旋轉后左子節點的右子樹*   - 其他不變* @param node 以該節點為軸旋轉*/private static void leftSpin(Node node) {Node nextFather = node.right;nextFather.parent = node.parent;node.right = node.right.left;nextFather.left = node;connectParent(node, nextFather);}

3. 右旋

右旋和左旋正好相反

以B為軸右旋的步驟:

  1. 將B的父節點A沉下來,B升上去作為新的父節點
  2. 將原來B的右子樹接到新的A的左子樹的位置

        /*** 將變換后的樹和它上面的節點連接* @param node 變換前的軸* @param nextFather 變換后的子樹*/private static void connectParent(Node node, Node nextFather) {// 如果變換的是根節點,就把root賦值成變換后的節點if(node.parent != null) {if(node.parent.data > node.data) {node.parent.left = nextFather;} else {node.parent.right = nextFather;}} else {RedBlackTree.root = nextFather;}node.parent = nextFather;}        /*** 右旋*   - 旋轉前的左子節點變成旋轉后的父節點*   - 旋轉前左子節點的右子樹變成旋轉后右子節點的左子樹* @param node 旋轉軸。*/private static void rightSpin(Node node) {Node nextFather = node.left;nextFather.parent = node.parent;node.left = node.left.right;nextFather.right = node;connectParent(node, nextFather);}

根據新插入節點位置的不同情況,節點調整有五種不同的方案:

1. 父節點和叔叔節點均為紅色

如果新插入節點的父節點和叔叔節點都是紅色,只需要將父節點和叔叔節點變為黑色,祖父節點變為紅色即可。

如果祖父節點是根節點,祖父節點保持黑色。

ONLY_CHANGE_COLOR {/*** 適用于:*   - 父節點和叔叔節點都為紅色的情況;* 具體方法:*   - 把父節點和叔叔節點的顏色變為黑色,*   - 爺爺節點的顏色變為紅色*   - 如果爺爺節點為根節點,爺爺節點顏色恢復黑色* @param node 當前新修改的節點*/@Overridepublic void way(Node node) {node.parent.parent.left.color = BLACK;node.parent.parent.right.color = BLACK;if(node.parent.parent.parent != null){node.parent.parent.color = RED;}}},

2. 叔叔節點不存在或為黑色,父節點位于祖父節點的左子樹

2.1 當前節點位于左子樹

調整辦法:

  1. 將父節點設置為黑色
  2. 經祖父節點設置為紅色
  3. 對祖父節點進行右旋

 RIGHT_SPIN_CHANGE_COLOR {/*** 適用于:*   - 無叔叔節點或叔叔節點為黑色*   - 父節點位于祖父節點的左子樹*   - 新節點位于父節點左子樹的情況* 具體方法:*   - 將當前節點的父節點設置為黑色*   - 將當前節點的祖父節點設置為紅色*   - 對祖父節點進行右旋* @param node 當前節點*/@Overridepublic void way(Node node) {node.parent.color = BLACK;node.parent.parent.color = RED;Solution.rightSpin(node.parent.parent);}},
2.2 當前節點位于右子樹

如果當前節點位于右子樹,需要先對它的父節點進行左旋得到2.1的情況,再按2.1進行變換

        LEFT_SPIN_WITH_RIGHT_SPIN {/*** 適用于:*   - 無叔叔節點或叔叔節點為黑色*   - 父節點位于祖父節點的左子樹*   - 新節點位于父節點右子樹的情況* 具體方法:*   - 對當前節點的父節點進行左旋*   - 執行 RIGHT_SPIN_CHANGE_COLOR* @param node 當前節點*/@Overridepublic void way(Node node) {Solution.leftSpin(node.parent);RIGHT_SPIN_CHANGE_COLOR.way(node.left);}},

3.叔叔節點不存在或為黑色,父節點在祖父節點的右子樹

與上面的情況正好相反

3.1 當前節點位于父節點的右子樹上

調整步驟:

  1. 將父節點變為黑色
  2. 將祖父節點變為紅色
  3. 對祖父節點左旋

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-GtHma7PH-1586137087069)(image/紅黑樹/image-20200406091144198.png)]

        LEFT_SPIN_CHANGE_COLOR {/*** 適用于:*   - 無叔叔節點或叔叔節點為黑色*   - 父節點位于祖父節點的右子樹*   - 新節點位于父節點右子樹的情況* 具體方法:*   - 將當前節點的父節點設置為黑色*   - 將當前節點的祖父節點設置為紅色*   - 對祖父節點進行左旋* @param node 當前節點*/@Overridepublic void way(Node node) {node.parent.color = BLACK;node.parent.parent.color = RED;Solution.leftSpin(node.parent.parent);}},

3.2 當前節點位于左子樹

調整步驟:

  1. 對當前節點的父節點進行右旋
  2. 執行3.1的步驟

        RIGHT_SPIN_LEFT_SPIN {/*** 適用于:*   - 無叔叔節點或叔叔節點為黑色*   - 父節點位于祖父節點的右子樹*   - 新節點位于父節點左子樹的情況* 具體方法:*   - 對當前節點的父節點進行右旋*   - 執行 LEFT_SPIN_CHANGE_COLOR* @param node 當前節點*/@Overridepublic void way(Node node) {Solution.rightSpin(node.parent);LEFT_SPIN_CHANGE_COLOR.way(node.right);}},

完整代碼

package Note.cistern;public class RedBlackTree {private static int BLACK = 1;private static final int RED = 0;private static Node root;private static class Node {private int color = RED;private int data;private Node left;private Node right;private Node parent;Node(int data) {this.data = data;}@Overridepublic String toString() {return "Node{" +"data=" + data +", color=" + color +", left=" + left +", right=" + right +'}';}}interface SolutionInterface {void way(Node node);}enum Solution implements SolutionInterface{ONLY_CHANGE_COLOR {/*** 適用于:*   - 父節點和叔叔節點都為紅色的情況;* 具體方法:*   - 把父節點和叔叔節點的顏色變為黑色,*   - 爺爺節點的顏色變為紅色*   - 如果爺爺節點為根節點,爺爺節點顏色恢復黑色* @param node 當前新修改的節點*/@Overridepublic void way(Node node) {node.parent.parent.left.color = BLACK;node.parent.parent.right.color = BLACK;if(node.parent.parent.parent != null){node.parent.parent.color = RED;}}},RIGHT_SPIN_LEFT_SPIN {/*** 適用于:*   - 無叔叔節點或叔叔節點為黑色*   - 父節點位于祖父節點的右子樹*   - 新節點位于父節點左子樹的情況* 具體方法:*   - 對當前節點的父節點進行右旋*   - 執行 LEFT_SPIN_CHANGE_COLOR* @param node 當前節點*/@Overridepublic void way(Node node) {Solution.rightSpin(node.parent);LEFT_SPIN_CHANGE_COLOR.way(node.right);}},LEFT_SPIN_CHANGE_COLOR {/*** 適用于:*   - 無叔叔節點或叔叔節點為黑色*   - 父節點位于祖父節點的右子樹*   - 新節點位于父節點右子樹的情況* 具體方法:*   - 將當前節點的父節點設置為黑色*   - 將當前節點的祖父節點設置為紅色*   - 對祖父節點進行左旋* @param node 當前節點*/@Overridepublic void way(Node node) {node.parent.color = BLACK;node.parent.parent.color = RED;Solution.leftSpin(node.parent.parent);}},LEFT_SPIN_WITH_RIGHT_SPIN {/*** 適用于:*   - 無叔叔節點或叔叔節點為黑色*   - 父節點位于祖父節點的左子樹*   - 新節點位于父節點右子樹的情況* 具體方法:*   - 對當前節點的父節點進行左旋*   - 執行 RIGHT_SPIN_CHANGE_COLOR* @param node 當前節點*/@Overridepublic void way(Node node) {Solution.leftSpin(node.parent);RIGHT_SPIN_CHANGE_COLOR.way(node.left);}},RIGHT_SPIN_CHANGE_COLOR {/*** 適用于:*   - 無叔叔節點或叔叔節點為黑色*   - 父節點位于祖父節點的左子樹*   - 新節點位于父節點左子樹的情況* 具體方法:*   - 將當前節點的父節點設置為黑色*   - 將當前節點的祖父節點設置為紅色*   - 對祖父節點進行右旋* @param node 當前節點*/@Overridepublic void way(Node node) {node.parent.color = BLACK;node.parent.parent.color = RED;Solution.rightSpin(node.parent.parent);}},PASS {/*** 異常情況* @param node 當前節點*/@Overridepublic void way(Node node) {}};/*** 左旋*   - 旋轉前的右子節點變成旋轉后的父節點*   - 旋轉前的父節點(軸)變為旋轉后父節點的左子節點*   - 旋轉前軸的右子節點的右子節點旋轉后變為軸的右子節點*   - 旋轉前右子節點的左子樹變成旋轉后左子節點的右子樹*   - 其他不變* @param node 以該節點為軸旋轉*/private static void leftSpin(Node node) {Node nextFather = node.right;nextFather.parent = node.parent;node.right = node.right.left;nextFather.left = node;connectParent(node, nextFather);}/*** 將變換后的樹和它上面的節點連接* @param node 變換前的軸* @param nextFather 變換后的子樹*/private static void connectParent(Node node, Node nextFather) {// 如果變換的是根節點,就把root賦值成變換后的節點if(node.parent != null) {if(node.parent.data > node.data) {node.parent.left = nextFather;} else {node.parent.right = nextFather;}} else {RedBlackTree.root = nextFather;}node.parent = nextFather;}/*** 右旋*   - 旋轉前的左子節點變成旋轉后的父節點*   - 旋轉前左子節點的右子樹變成旋轉后右子節點的左子樹* @param node 旋轉軸。*/private static void rightSpin(Node node) {Node nextFather = node.left;nextFather.parent = node.parent;node.left = node.left.right;nextFather.right = node;connectParent(node, nextFather);}}public RedBlackTree(int rootData){root = new Node(rootData);root.color = BLACK;}/*** 通過一個整形數組快速構建紅黑樹* @param data 要創建樹的元素*/public RedBlackTree(int [] data) {assert data.length >= 1: "data的長度至少為1";root = new Node(data[0]);root.color = BLACK;for (int i = 1; i < data.length; i++) {this.append(data[i]);}}/*** 判斷插入新節點后紅黑樹結構是否需要變化* 根據紅黑樹的定義,兩個紅色節點不能連接* @param root 插入的新節點* @return 返回true表示插入新節點后破壞了紅黑樹的結構,*         需要通過旋轉變色來糾正,否則不需要修改。*/private boolean checkStruct(Node root) {return root.color == RED && root.parent.color == RED;}/*** 確定該以哪種方式變換當前樹,使之滿足紅黑樹的條件* @param node 當前新加入的,使紅黑樹結構錯誤的節點* @return 返回解決辦法*/private Solution determineSolution(Node node) {int fatherColor = node.parent.color;int uncleColor;// 如果父親是爺爺的右子樹if(node.parent.data > node.parent.parent.data){uncleColor = node.parent.parent.left == null? BLACK: node.parent.parent.left.color;if(fatherColor == RED && uncleColor == BLACK){if(node.data < node.parent.data){return Solution.RIGHT_SPIN_LEFT_SPIN;} else {return Solution.LEFT_SPIN_CHANGE_COLOR;}}} else {uncleColor = node.parent.parent.right == null? BLACK: node.parent.parent.right.color;if(fatherColor == RED && uncleColor == BLACK){// 插入的節點是父節點的左子節點if(node.data < node.parent.data){return Solution.RIGHT_SPIN_CHANGE_COLOR;} else {return Solution.LEFT_SPIN_WITH_RIGHT_SPIN;}}}// 如果父節點和叔叔節點都是紅色if(fatherColor == RED && uncleColor == RED){return Solution.ONLY_CHANGE_COLOR;}return Solution.PASS;}private void changeTree(Node node) {Solution solution = determineSolution(node);solution.way(node);if(node.parent != null && node.parent.parent != null && checkStruct(node.parent.parent)) {changeTree(node.parent.parent);}}/*** 通過遞歸往紅黑樹中插入一個新節點* @param root 要插入的樹的根節點* @param data 新節點的值*/private void insert(Node root, int data) {if(root.data > data) {if(root.left == null) {Node node = new Node(data);root.left = node;node.parent = root;if(checkStruct(node)) {changeTree(node);}} else {insert(root.left, data);}}else {if(root.right == null) {Node node = new Node(data);root.right = node;node.parent = root;if(checkStruct(node)) {changeTree(node);}} else {insert(root.right, data);}}}public Node append(int data) {insert(root, data);return root;}public Node append(int[] data) {for (int datum : data) {append(datum);}return root;}public Node getRoot(){return root;}public static void main(String[] args) {int[] data = {16, 8, 11, 13, 15, 17, 22, 25, 27};RedBlackTree redBlackTree = new RedBlackTree(data);System.out.println(redBlackTree.getRoot());}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457476.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457476.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457476.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

說一下自己對于 Linux 哲學的理解

查閱了一些資料&#xff0c;官方的哲學思想貌似是&#xff1a; 一切皆文件由眾多單一目的的小程序&#xff0c;一個程序只實現一個功能&#xff0c;多個程序組合完成復雜任務文本文件保存配置信息盡量避免與用戶交互什么&#xff0c;你問我的理解&#xff1f;哲學思想&#xff…

UWP學習記錄

微軟{X:Bind}、{Binding}資料網站 &#xff1a; https://msdn.microsoft.com/windows/uwp/xaml-platform/x-bind-markup-extension在View的ItemTemplate中綁定ViewModel的方法&#xff1a;1 <ItemsControl Name"XX" ItemsSource"{x:Bind VM.XXModels,ModeOne…

dw1000信標碼_DW1000方案工牌型UWB標簽,助力10厘米高精度室內定位!

DW1000方案工牌型UWB標簽&#xff0c;助力10厘米高精度室內定位&#xff01;發布日期&#xff1a;2019-04-01 瀏覽次數&#xff1a;244次微能信息(95power)推出一款工牌型UWB標簽VDU1510 &#xff0c;廣泛應用于超寬帶UWB定位系統&#xff0c;最高可實現10cm高精度人員定位。工…

【Java】HashMap源碼(1.7)

Life is not a ridiculous number of life, the meaning of life lies in life itself HashMap源碼 散列集 數組和鏈表可以保持元素插入的順序&#xff0c;對數組來說&#xff0c;他的優點是擁有連續的存儲空間&#xff0c;因此可以使用元素下標快速訪問&#xff0c;但缺點在…

Docker 基本用法

1.安裝&#xff1a; wget http://mirrors.yun-idc.com/epel/6/i386/epel-release-6-8.noarch.rpm rpm -ivh epel-release-6-8.noarch.rpm yum install docker-io -y2.獲取鏡像 pull docker pull ubuntu docker pull ubuntu:14.043.運行這個鏡像&#xff0c;在其中運行bash應用…

畫刷的使用

1.畫刷的定義&#xff1a; HBRUSH hBrush; windows 自定義的畫刷&#xff1a; WHITE_BRUSH、LTGRAY_BRUSH、GRAY_BRUSH、DKGRAY_BRUSH、BLACK_BRUSH和NULL_BRUSH &#xff08;也叫HOLLOW_BRUSH&#xff09; 獲取方法如下&#xff1a; hBrush (HBRUSH) GetStockObject (GRAY_BR…

dataframe 控對象_iOS知識 - 常用小技巧大雜燴

1&#xff0c;打印View所有子視圖po [[self view]recursiveDescription]2&#xff0c;layoutSubviews調用的調用時機* 當視圖第一次顯示的時候會被調用。* 添加子視圖也會調用這個方法。* 當本視圖的大小發生改變的時候是會調用的。* 當子視圖的frame發生改變的時候是會調用的。…

【Java】jdk 1.8 新特性——Lambda表達式

Lambda表達式 jdk 1.8 新加入的特性&#xff0c;簡化了簡單接口的實現 函數式接口 函數式中只有一個待實現的方法&#xff0c;可以使用FunctionalInterface注解標注函數式接口.這個接口中只能有一個待實現的方法&#xff0c;但可以包含默認方法&#xff0c;靜態方法以及Obje…

【Todo】Java8新特性學習

參考這篇文章吧&#xff1a; http://blog.csdn.net/vchen_hao/article/details/53301073 還有一個系列轉載于:https://www.cnblogs.com/charlesblc/p/6123380.html

jsp調整字體大小font_html font標簽如何設置字體大小?

首先我們先來看看htmlfont標簽是如何來設置字體大小的&#xff1a;都只到htmlfont標簽是個專門用來設置字體的標簽&#xff0c;雖然在html5中用的會很少(因為都用css樣式來設置font標簽里面的屬性)&#xff0c;但是個人覺得font標簽還是相當強大的標簽的&#xff0c;為什么這么…

runtime官方文檔

OC是一種面向對象的動態語言&#xff0c;作為初學者可能大多數人對面向對象這個概念理解的比較深&#xff0c;而對OC是動態語言這一特性了解的比較少。那么什么是動態語言&#xff1f;動態語言就是在運行時來執行靜態語言的編譯鏈接的工作。這就要求除了編譯器之外還要有一種運…

【Java】synchronized關鍵字筆記

Java Synchronized 關鍵字 壹. Java并發編程存在的問題 1. 可見性問題 可見性問題是指一個線程不能立刻拿到另外一個線程對共享變量的修改的結果。 如&#xff1a; package Note.concurrency;public class Demo07 {private static boolean s true;public static void mai…

sql語句分析是否走索引_MySql 的SQL執行計劃查看,判斷是否走索引

在select窗口中&#xff0c;執行以下語句&#xff1a;set profiling 1; -- 打開profile分析工具show variables like %profil%; -- 查看是否生效show processlist; -- 查看進程use cmc; -- 選擇數據庫show PROFILE all; -- 全部分析的類型show index from t_log_account; ##查看…

SQL Server-數據類型(七)

前言 前面幾篇文章我們講解了索引有關知識&#xff0c;這一節我們再繼續我們下面內容講解&#xff0c;簡短的內容&#xff0c;深入的理解&#xff0c;Always to review the basics。 數據類型 SQL Server支持兩種字符數據類型&#xff0c;一種是常規&#xff0c;另外一種則是Un…

【隨記】SQL Server連接字符串參數說明

廢話不多說&#xff0c;請參見 SqlConnection.ConnectionString 。 轉載于:https://www.cnblogs.com/xiesong/p/5749037.html

【設計模式 00】設計模式的六大原則

設計模式的六大原則 參考&#xff1a; 設計模式六大原則 1. 單一職責原則 一個類只負責一個明確的功能 優點&#xff1a; 降低類的復雜度&#xff0c;提高代碼可讀性和可維護性降低變更時對其他功能的影響 2. 里氏替換原則 **原則一&#xff1a;**若 o1 是 C1 的一個實例化…

pb retrieve時停止工作_大佬們掛在嘴邊的PE、PB是什么?

在緊鑼密鼓地準備科創50ETF的發行工作間隙&#xff0c;今天小夏先帶你讀懂最簡單的PE、PB估值指標這兩大指標。01、什么是PE&#xff08;市盈率&#xff09;PE&#xff0c;也就是市價盈利比率&#xff0c;簡稱市盈率。市盈率是指股票價格與每股收益&#xff08;每股收益&#x…

EF CodeFirst 如何通過配置自動創建數據庫當模型改變時

最近悟出來一個道理&#xff0c;在這兒分享給大家&#xff1a;學歷代表你的過去&#xff0c;能力代表你的現在&#xff0c;學習代表你的將來。 十年河東十年河西&#xff0c;莫欺少年窮 學無止境&#xff0c;精益求精 本篇為進階篇&#xff0c;也是彌補自己之前沒搞明白的地方,…

對AutoIt中控件和窗口的理解

經過嘗試&#xff0c;對AutoIt中Control和Window有了新的認識&#xff0c;分享一下 1.Control 現在我想對一個WinForm架構的應用程序進行自動化操作&#xff0c;得到控件Advanced Mode屬性為[Name:XXX]。 然而在該窗口中有多個相同屬性的Control&#xff0c;而依該屬性只能操作…

【設計模式 01】簡單工廠模式(Simple factory pattern)

簡單工廠模式 可以根據參數的不同返回不同類的實例 參考&#xff1a; CSDN|簡單工廠模式 簡單工廠通過傳給工廠類的參數的不同&#xff0c;返回不同的對象&#xff0c;包括三部分組成&#xff1a; 具體的”產品“工廠類&#xff08;實例化并返回”產品“&#xff09;客戶端&am…