日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

父子结构查询_Java面试准备(5)之数据结构与算法——红黑树

發(fā)布時(shí)間:2025/3/12 java 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 父子结构查询_Java面试准备(5)之数据结构与算法——红黑树 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

歡迎點(diǎn)贊評論+關(guān)注~~~~~~~

如上圖,二叉查找樹極端情況下可能會(huì)變成一個(gè)單鏈表,這種查詢時(shí)間復(fù)雜度就變成O(n)了,紅黑樹在二叉查找樹的基礎(chǔ)上進(jìn)行了自平衡。

1.原理分析

如上圖,紅黑樹具有以下特征:

1. 每個(gè)節(jié)點(diǎn)要么是黑色,要么是紅色

2. 根節(jié)點(diǎn)是黑色

3. 每個(gè)葉子節(jié)點(diǎn)都是黑色的空結(jié)點(diǎn)(NIL結(jié)點(diǎn))

4. 如果一個(gè)節(jié)點(diǎn)是紅色的,則它的子節(jié)點(diǎn)必須是黑色的

5. 從任一結(jié)點(diǎn)到其每個(gè)葉子的所有路徑都包含相同數(shù)目的黑色結(jié)點(diǎn)

6.新插入節(jié)點(diǎn)默認(rèn)為紅色,插入后需要校驗(yàn)紅黑樹是否符合規(guī)則,不符合則需要進(jìn)行平衡

紅黑樹節(jié)點(diǎn)的增加刪除都需要保證滿足以上特征,紅黑樹采取多種方式來維護(hù)這些特征,從而維持平衡。

主要包括:左旋轉(zhuǎn)、右旋轉(zhuǎn)、顏色反轉(zhuǎn)

1.1左旋(RotateLeft )

逆時(shí)針旋轉(zhuǎn)紅黑樹的兩個(gè)結(jié)點(diǎn),使得父結(jié)點(diǎn)被自己的右孩子取代,而自己成為自己的左孩子。

如圖所示,以X為基點(diǎn)逆時(shí)針旋轉(zhuǎn),X的父節(jié)點(diǎn)被x原來的右孩子Y取代,c保持不變 ,Y節(jié)點(diǎn)原來的左孩子c變成X的右孩子,這樣的旋轉(zhuǎn)仍然保證了二叉查找樹的特征,左節(jié)點(diǎn)比父節(jié)點(diǎn)小,右節(jié)點(diǎn)仍然比父節(jié)點(diǎn)大,即a

1.2 右旋(RotateRight)

順時(shí)針旋轉(zhuǎn)紅黑樹的兩個(gè)結(jié)點(diǎn),使得父結(jié)點(diǎn)被自己的左孩子取代,而自己成為自己的右孩子。

如圖所示,以X為基點(diǎn)順時(shí)針旋轉(zhuǎn) ,X的父節(jié)點(diǎn)被x原來的左孩子Y取代 ,b保持不變 ,Y節(jié)點(diǎn)原來的右孩子c變成X的左孩子 。這樣的旋轉(zhuǎn)仍然保證了二叉查找樹的特征,左節(jié)點(diǎn)比父節(jié)點(diǎn)小,右節(jié)點(diǎn)仍然比父節(jié)點(diǎn)大,即b

1.3顏色反轉(zhuǎn)

新增節(jié)點(diǎn)默認(rèn)為紅色,而父節(jié)點(diǎn)和叔叔節(jié)點(diǎn)也為紅色,這種情況就違反了紅黑樹的規(guī)則(父與子不能同時(shí)為紅色),需要將紅色往祖輩上傳,父節(jié)點(diǎn)和叔叔節(jié)點(diǎn)變?yōu)楹谏?#xff0c;這樣來保證每條葉子結(jié)點(diǎn)到根節(jié)點(diǎn)的黑色節(jié)點(diǎn)數(shù)量并未發(fā)生變化。

插入新節(jié)點(diǎn)的時(shí)候存在如下幾種情況:

1.插入節(jié)點(diǎn)位于樹根,沒有父節(jié)點(diǎn),這種情況,直接讓新結(jié)點(diǎn)變色為黑色.

2.插入節(jié)點(diǎn)的父節(jié)點(diǎn)是黑色,新插入的紅色節(jié)點(diǎn)并沒有打破紅黑樹的規(guī)則,所以不需要做任何調(diào)整

3.插入節(jié)點(diǎn)的父節(jié)點(diǎn)和叔叔節(jié)點(diǎn)是紅色

此種情況,不滿足父子節(jié)點(diǎn)不能同時(shí)為紅色的規(guī)則,先讓B變成黑色,這樣又不滿足某一個(gè)節(jié)點(diǎn)到每個(gè)葉子節(jié)點(diǎn)經(jīng)過的黑色節(jié)點(diǎn)數(shù)相同的規(guī)則,先讓A變成紅色,

此時(shí),A、C父子節(jié)點(diǎn)又變成了紅色,所以將C調(diào)整為黑色,來使這一局部滿足紅黑樹的規(guī)則

4.插入節(jié)點(diǎn)的父節(jié)點(diǎn)是紅色,叔叔節(jié)點(diǎn)是黑色或者沒有叔叔,且插入節(jié)點(diǎn)是父節(jié)點(diǎn)的右孩子,父節(jié)點(diǎn)是祖父節(jié)點(diǎn)點(diǎn)的左孩子,

以結(jié)點(diǎn)B為軸,做一次左旋轉(zhuǎn),使得新結(jié)點(diǎn)D成為父結(jié)點(diǎn),原來的父結(jié)點(diǎn)B成為D的左孩子 ,變成父節(jié)點(diǎn)是紅色,叔叔節(jié)點(diǎn)是黑色或者沒有叔叔,新插入節(jié)點(diǎn)是父結(jié)點(diǎn)的左孩子,父節(jié)點(diǎn)是祖父節(jié)點(diǎn)的左孩子,以結(jié)點(diǎn)A為軸,做一次右旋轉(zhuǎn),使得結(jié)點(diǎn)B成為祖父結(jié)點(diǎn),結(jié)點(diǎn)A成為結(jié)點(diǎn)B的右孩子,

接著,將B變成黑色,A變成紅色,就局部滿足紅黑樹的規(guī)則了。

小結(jié):旋轉(zhuǎn)和顏色反轉(zhuǎn)都是為了是樹滿足紅黑樹的5個(gè)特性,從而達(dá)到自平衡的效果。

變化規(guī)律總結(jié):根節(jié)點(diǎn)必黑,新增是紅色,只能黑連黑,不能紅連紅; 爸叔通紅就變色,爸紅叔黑就旋轉(zhuǎn),哪邊黑往哪邊轉(zhuǎn)。

2.代碼實(shí)現(xiàn)

2.1 紅黑樹節(jié)點(diǎn)結(jié)構(gòu)

class RBTreeNode{private int value;//數(shù)據(jù) private boolean isBlack;//紅黑標(biāo)記 private RBTreeNode left;//左節(jié)點(diǎn) private RBTreeNode right;//右邊節(jié)點(diǎn) private RBTreeNode parent;//父節(jié)點(diǎn) public RBTreeNode(int value) { this.isBlack=false;//默認(rèn)節(jié)點(diǎn)為紅色 this.value = value; } setter,getter....}

2.2 遍歷節(jié)點(diǎn)方法

為了方便獲取節(jié)點(diǎn)數(shù)據(jù),編寫一個(gè)遍歷樹的方法,使用遞歸的方式實(shí)現(xiàn),在樹中遞歸很常用。

public void traverse(RBTreeNode node){ if (node==null) return; //遞歸終止條件 if (node.getLeft()==null&& node.getRight()==null){ //葉子節(jié)點(diǎn) System.out.println(node.getValue()); return ; } //先遍歷左節(jié)點(diǎn),后遍歷右節(jié)點(diǎn) traverse(node.getLeft()); traverse(node.getRight()); }

2.3 新增節(jié)點(diǎn)

主要是從根節(jié)點(diǎn)依次對比 data和node.getValue的大小,一直找到被掛載得節(jié)點(diǎn),在進(jìn)行掛載。

public void insert(int data){ RBTreeNode node=new RBTreeNode(data); if (root==null){ //插入根節(jié)點(diǎn) root=node; root.setBlack(true);//樹根是黑色 return; } RBTreeNode parent=root; RBTreeNode son; if (data

2.4 自平衡方法

自平衡方法按照需求可以拆分為:左旋轉(zhuǎn)、右旋轉(zhuǎn)、設(shè)置黑色、設(shè)置紅色方法

2.4.1 左旋轉(zhuǎn)方法

結(jié)合圖查看代碼邏輯,更清晰旋轉(zhuǎn)過程。

/** * 左旋轉(zhuǎn)(逆時(shí)針旋轉(zhuǎn)) * @param node */ private void leftRotate(RBTreeNode node) { RBTreeNode right = node.getRight(); RBTreeNode parent = node.getParent(); if (parent==null){ //root節(jié)點(diǎn),將 root=right; right.setParent(null); }else{ if (parent.getLeft()!=null&&parent.getLeft()==node){ // 左右 //node是左子節(jié)點(diǎn),左旋轉(zhuǎn)直接將node右子節(jié)點(diǎn)旋轉(zhuǎn)掛到父節(jié)點(diǎn)左節(jié)點(diǎn) parent.setLeft(right); }else { //右右的情況 //node是子節(jié)點(diǎn),直接將node的右子節(jié)點(diǎn)旋轉(zhuǎn)掛到父節(jié)點(diǎn)的右子節(jié)點(diǎn) 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 右旋轉(zhuǎn)方法

/** * 右旋轉(zhuǎn)(順時(shí)針) * @param node */ private void rightRotate(RBTreeNode node) { RBTreeNode left = node.getLeft(); RBTreeNode parent = node.getParent(); if (parent == null) { //根節(jié)點(diǎn) root = left; left.setParent(null); } else { if (parent.getLeft() != null && parent.getLeft() == node) { //將node的左子節(jié)點(diǎn)掛父節(jié)點(diǎn) parent.setLeft(left); } else { //將node的左節(jié)點(diǎn)掛父節(jié)點(diǎn)的右節(jié)點(diǎn) 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 設(shè)置紅黑色方法

private void setRed(RBTreeNode node) { node.setBlack(false); } private void setBlack(RBTreeNode node) { node.setBlack(true); }

2.4.4 自平衡方法

/** * 節(jié)點(diǎn)自平衡 * @param node */ private void nodeBanlance(RBTreeNode node) { RBTreeNode father,gFather;//父節(jié)點(diǎn),祖父節(jié)點(diǎn) //父節(jié)點(diǎn)是紅色的 while((father=node.getParent())!=null && father.isBlack()==false){ gFather=father.getParent(); if (gFather.getLeft()==father){ //父節(jié)點(diǎn)在祖父節(jié)點(diǎn)的左側(cè) RBTreeNode uncle = gFather.getRight();//叔叔節(jié)點(diǎn) if (uncle!=null&& !uncle.isBlack()){ //叔叔節(jié)點(diǎn)不為空 且是紅色的 //這種情況需要反色,父節(jié)點(diǎn)和叔叔節(jié)點(diǎn)變黑,爺爺節(jié)點(diǎn)變紅 setBlack(father); setBlack(uncle); setRed(gFather); //繼續(xù)循環(huán) 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 總結(jié)

查詢時(shí)間復(fù)雜度 O(logn)

應(yīng)用場景:

在JDK1.8中HashMap使用數(shù)組+鏈表+紅黑樹的數(shù)據(jù)結(jié)構(gòu)。內(nèi)部維護(hù)著一個(gè)數(shù)組table,該數(shù)組保存著每個(gè)鏈表的表頭結(jié)點(diǎn)或者樹的根節(jié)點(diǎn)。

總結(jié)

以上是生活随笔為你收集整理的父子结构查询_Java面试准备(5)之数据结构与算法——红黑树的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。