自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java關(guān)于數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn):樹(shù)

新聞 開(kāi)發(fā)
本篇文章從 數(shù)據(jù)結(jié)構(gòu)的基本原理出發(fā),逐步去分析Java集合里數(shù)據(jù)結(jié)構(gòu)的應(yīng)用與實(shí)現(xiàn)。

文章目錄`

一 樹(shù)的概念與應(yīng)用場(chǎng)景

1.1 二叉查找樹(shù)

1.2 AVL樹(shù)

1.3 紅黑樹(shù)

1.4 B樹(shù)

二 樹(shù)的操作與源碼實(shí)現(xiàn)

2.1 TreeMap/TreeSet實(shí)現(xiàn)原理

寫(xiě)在前面 

之前在網(wǎng)上看到過(guò)很多關(guān)于Java集合框架實(shí)現(xiàn)原理文章,但大都在講接口的作用與各類(lèi)集合的實(shí)現(xiàn),對(duì)其中數(shù)據(jù)結(jié)構(gòu)的闡述的不多,例如紅黑樹(shù)的染色和旋轉(zhuǎn)是怎么進(jìn)行的等等,本篇文章從 數(shù)據(jù)結(jié)構(gòu)的基本原理出發(fā),逐步去分析Java集合里數(shù)據(jù)結(jié)構(gòu)的應(yīng)用與實(shí)現(xiàn)。

一 樹(shù)的概念與應(yīng)用場(chǎng)景

樹(shù)是一種抽象數(shù)據(jù)類(lèi)型(ADT)或是實(shí)作這種抽象數(shù)據(jù)類(lèi)型的數(shù)據(jù)結(jié)構(gòu),用來(lái)模擬具有樹(shù)狀結(jié)構(gòu)性質(zhì)的數(shù)據(jù)集合。它是由n(n>0)個(gè)有限節(jié)點(diǎn)組成一個(gè)具有層次關(guān)系的集合。把 它叫做“樹(shù)”是因?yàn)樗雌饋?lái)像一棵倒掛的樹(shù),也就是說(shuō)它是根朝上,而葉朝下的。

樹(shù)有以下特點(diǎn):

每個(gè)節(jié)點(diǎn)有零個(gè)或多個(gè)子節(jié)點(diǎn);
沒(méi)有父節(jié)點(diǎn)的節(jié)點(diǎn)稱(chēng)為根節(jié)點(diǎn);
每一個(gè)非根節(jié)點(diǎn)有且只有一個(gè)父節(jié)點(diǎn);
除了根節(jié)點(diǎn)外,每個(gè)子節(jié)點(diǎn)可以分為多個(gè)不相交的子樹(shù);
與樹(shù)相關(guān)的概念

節(jié)點(diǎn)的度:一個(gè)節(jié)點(diǎn)含有的子樹(shù)的個(gè)數(shù)稱(chēng)為該節(jié)點(diǎn)的度;
樹(shù)的度:一棵樹(shù)中,最大的節(jié)點(diǎn)的度稱(chēng)為樹(shù)的度;
葉節(jié)點(diǎn)或終端節(jié)點(diǎn):度為零的節(jié)點(diǎn);
非終端節(jié)點(diǎn)或分支節(jié)點(diǎn):度不為零的節(jié)點(diǎn);
父親節(jié)點(diǎn)或父節(jié)點(diǎn):若一個(gè)節(jié)點(diǎn)含有子節(jié)點(diǎn),則這個(gè)節(jié)點(diǎn)稱(chēng)為其子節(jié)點(diǎn)的父節(jié)點(diǎn);
孩子節(jié)點(diǎn)或子節(jié)點(diǎn):一個(gè)節(jié)點(diǎn)含有的子樹(shù)的根節(jié)點(diǎn)稱(chēng)為該節(jié)點(diǎn)的子節(jié)點(diǎn);
兄弟節(jié)點(diǎn):具有相同父節(jié)點(diǎn)的節(jié)點(diǎn)互稱(chēng)為兄弟節(jié)點(diǎn);
節(jié)點(diǎn)的層次:從根開(kāi)始定義起,根為第1層,根的子節(jié)點(diǎn)為第2層,以此類(lèi)推;
深度:對(duì)于任意節(jié)點(diǎn)n, n的深度為從根到n的唯一路徑長(zhǎng),根的深度為0;
高度:對(duì)于任意節(jié)點(diǎn)n, n的高度為從n到一片樹(shù)葉的最長(zhǎng)路徑長(zhǎng),所有樹(shù)葉的高度為0;
堂兄弟節(jié)點(diǎn):父節(jié)點(diǎn)在同一層的節(jié)點(diǎn)互為堂兄弟;
節(jié)點(diǎn)的祖先:從根到該節(jié)點(diǎn)所經(jīng)分支上的所有節(jié)點(diǎn);
子孫:以某節(jié)點(diǎn)為根的子樹(shù)中任一節(jié)點(diǎn)都稱(chēng)為該節(jié)點(diǎn)的子孫。
森林:由m(m>=0)棵互不相交的樹(shù)的集合稱(chēng)為森林;
注:參照親戚關(guān)系,這些概念很好理解,家族譜也是一種樹(shù)結(jié)構(gòu)。:grinning:

樹(shù)的分類(lèi)

無(wú)序樹(shù):樹(shù)中任意節(jié)點(diǎn)的子節(jié)點(diǎn)之間沒(méi)有順序關(guān)系,這種樹(shù)稱(chēng)為無(wú)序樹(shù),也稱(chēng)為自由樹(shù);
有序樹(shù):樹(shù)中任意節(jié)點(diǎn)的子節(jié)點(diǎn)之間有順序關(guān)系,這種樹(shù)稱(chēng)為有序樹(shù);
其中有序樹(shù)又分為:

二叉樹(shù):每個(gè)節(jié)點(diǎn)最多含有兩個(gè)子樹(shù)的樹(shù)稱(chēng)為二叉樹(shù);
完全二叉樹(shù):對(duì)于一顆二叉樹(shù),假設(shè)其深度為d(d>1)。除了第d層外,其它各層的節(jié)點(diǎn)數(shù)目均已達(dá)最大值,且第d層所有節(jié)點(diǎn)從左向右連續(xù)地緊密排列,這樣的二叉樹(shù)被稱(chēng)為完全二叉樹(shù);
滿二叉樹(shù):所有葉節(jié)點(diǎn)都在最底層的完全二叉樹(shù);
AVL樹(shù):當(dāng)且僅當(dāng)任何節(jié)點(diǎn)的兩棵子樹(shù)的高度差不大于1的二叉樹(shù);
二叉查找樹(shù):樹(shù)中的每個(gè)節(jié)點(diǎn),它的左子樹(shù)中所有項(xiàng)小于X中的項(xiàng),它的右子樹(shù)中的所有項(xiàng)大于X中的項(xiàng)。
霍夫曼樹(shù):帶權(quán)路徑最短的二叉樹(shù)稱(chēng)為哈夫曼樹(shù)或最優(yōu)二叉樹(shù);
B樹(shù):一種對(duì)讀寫(xiě)操作進(jìn)行優(yōu)化的自平衡的二叉查找樹(shù),能夠保持?jǐn)?shù)據(jù)有序,擁有多余兩個(gè)子樹(shù)。
1.1 二叉查找樹(shù)

二叉查找樹(shù)是一種有序二叉樹(shù),它的查找、插入的時(shí)間復(fù)雜度為O(logN)

二叉查找是是一種有序二叉樹(shù).

主要特點(diǎn)

若任意節(jié)點(diǎn)的左子樹(shù)不空,則左子樹(shù)上所有節(jié)點(diǎn)的值均小于它的根節(jié)點(diǎn)的值。
若任意節(jié)點(diǎn)的右子樹(shù)不空,則右子樹(shù)上所有節(jié)點(diǎn)的值均大于它的根節(jié)點(diǎn)的值。
任意節(jié)點(diǎn)的左右子樹(shù)葉為二叉查找樹(shù)
沒(méi)有鍵值相等的節(jié)點(diǎn)。
就就是說(shuō)二叉查找樹(shù)上的節(jié)點(diǎn)是排好序的:左子樹(shù) < 根節(jié)點(diǎn) < 右子樹(shù)。

性能分析

在最壞的情況下,即當(dāng)先后插入的關(guān)鍵字有序時(shí),構(gòu)造成的二叉查找樹(shù)蛻變?yōu)閱沃?shù),輸?shù)纳疃葹閚,其平均查找長(zhǎng)度為(n+1)/2。
在最好的情況下,二叉查找樹(shù)的形態(tài)和折半查找的判定樹(shù)相同,其時(shí)間復(fù)雜度為O(log2(N))。
我們來(lái)看看二叉查找樹(shù)上的相關(guān)操作。

構(gòu)造

當(dāng)我們用一組數(shù)值構(gòu)造一棵二叉查找樹(shù)時(shí),就相當(dāng)于對(duì)這組數(shù)值進(jìn)行了排序,在最壞情況下,即該組數(shù)值是從小到達(dá)排好序的,構(gòu)造出來(lái)的二叉樹(shù)的所有節(jié)點(diǎn)都 沒(méi)有左子樹(shù),這種情況下的時(shí)間復(fù)雜度為O(N2)。(N的平方)

另外,樹(shù)排序的問(wèn)題使得CPU Cache性能較差,特別是當(dāng)節(jié)點(diǎn)是動(dòng)態(tài)內(nèi)存分配時(shí)。而堆排序的CPU Cache性能較好。另一方面,樹(shù)排序是最優(yōu)的增量排序(incremental sorting)算法, 保持一個(gè)數(shù)值序列的有序性。

查找

由于二叉查找樹(shù)具有左子樹(shù) < 根節(jié)點(diǎn) < 右子樹(shù)的特點(diǎn),因此在二叉查找樹(shù)b中查找x的過(guò)程如下:

若b是空樹(shù),則查找失敗,否則:
若x等于根節(jié)點(diǎn)的值,則查找成功,否則:
若x小于b根節(jié)點(diǎn)的值,則查找左子樹(shù),否則
若x大于b根節(jié)點(diǎn)的值,則查找右子樹(shù)。
整個(gè)流程是一個(gè)遞歸的過(guò)程。

插入

插入的過(guò)程也是查找的過(guò)程,在二叉查找樹(shù)中插入節(jié)點(diǎn)x的過(guò)程如下:

若b是空樹(shù),則x作為根節(jié)點(diǎn)插入,否則:
若x的值等于根節(jié)點(diǎn)的值,則直接返回,否則:
若x的值小于根節(jié)點(diǎn)的值,則將x插入當(dāng)該根節(jié)點(diǎn)的左子樹(shù)中,否則
將x插入該根節(jié)點(diǎn)的右子樹(shù)中。
這也是一個(gè)遞歸的過(guò)程,這里我們要關(guān)注兩點(diǎn):

插入的過(guò)程也是查找的過(guò)程

二叉查找樹(shù)不允許有相同值的節(jié)點(diǎn)

刪除

在二叉查找樹(shù)上刪除一個(gè)節(jié)點(diǎn),分為三種情況:

若刪除的是葉子節(jié)點(diǎn),則不會(huì)破壞樹(shù)的結(jié)構(gòu),只需要修改其雙親節(jié)點(diǎn)的指針即可。
若刪除的節(jié)點(diǎn)只有一個(gè)孩子節(jié)點(diǎn),則用它的孩子節(jié)點(diǎn)代替它的位置即可,如此也不會(huì)破壞紅黑樹(shù)的結(jié)構(gòu)。
若刪除的節(jié)點(diǎn)有兩個(gè)孩子節(jié)點(diǎn),這種情況復(fù)雜一下,我們通常會(huì)找到要?jiǎng)h除節(jié)點(diǎn)X的左子樹(shù)里的最大元素或者右子樹(shù)里的最小元素,然后用M替換掉X,再刪除節(jié)點(diǎn),因?yàn)榇藭r(shí)M最多只會(huì)有一個(gè)
節(jié)點(diǎn)(如果左子樹(shù)最大元素則沒(méi)有右子節(jié)點(diǎn),若是右子樹(shù)最小元素則沒(méi)有左子節(jié)點(diǎn)),若M沒(méi)有孩子節(jié)點(diǎn),直接進(jìn)入情況1處理,若M只有一個(gè)孩子,則直接進(jìn)入情況2處理。
另外,如果刪除的次數(shù)不多,可以采用 懶惰刪除 的方式,即當(dāng)一個(gè)元素刪除時(shí),它仍然留在樹(shù)中,只是被比較為已刪除,這種方式在有重復(fù)項(xiàng)是特別有用, 另外如果刪除的元素又重新插入,這種方式可以避免新單元的創(chuàng)建開(kāi)銷(xiāo)。

1.2 AVL樹(shù)

AVL樹(shù)是帶有平衡條件的二叉查找樹(shù)。

主要特點(diǎn)

AVL樹(shù)中的任何階段的兩棵子樹(shù)高度最大差別為1.
AVL樹(shù)還有個(gè)平衡因子的概念,平衡因子 = 左子樹(shù)高度 - 右子樹(shù)高度,因此平衡因子為-1,0,1的為平衡二叉樹(shù),其他的都是不平衡的。

另外,把一棵不平衡的二叉查找樹(shù)變成一棵平衡二叉樹(shù),我們稱(chēng)之為 AVL旋轉(zhuǎn) 。

我們來(lái)看看不同情況下AVL旋轉(zhuǎn)如何進(jìn)行。

左左情況:右旋
右右情況:左旋
左右情況:先左旋,再右旋
右左情況:先右旋,再左旋
注:所謂左左指的是左邊的左子樹(shù)多了一個(gè),其他的依此類(lèi)推。

具體操作如下所示,我們可以看到左左情況和右右情況只需要單旋轉(zhuǎn)就可以完成,左右情況與右左情況需要先把它們變成左左情況與右右情況 再進(jìn)行旋轉(zhuǎn),因此這兩種情況需要雙旋轉(zhuǎn)才能完成。

性能分析

查找、插入與刪除在平均和最壞的情況下的時(shí)間復(fù)雜度為O(logN)。

AVL樹(shù)也是二叉查找樹(shù)的一種,它的很多操作都可以向我們上面描述的二叉查找樹(shù)的操作那樣進(jìn)行。刪除操作有點(diǎn)例外,我們?cè)谶M(jìn)行刪除操作

時(shí)可以把要?jiǎng)h除的節(jié)點(diǎn)向下旋轉(zhuǎn)形成一個(gè)葉子節(jié)點(diǎn),然后直接刪除這個(gè)葉子節(jié)點(diǎn),因?yàn)樾D(zhuǎn)成葉子節(jié)點(diǎn)期間,做多有l(wèi)ogN個(gè)節(jié)點(diǎn)被旋轉(zhuǎn),每次 AVL旋轉(zhuǎn)花費(fèi)的事件固定,所以刪除操作的時(shí)間復(fù)雜度是O(logN)。

1.3 紅黑樹(shù)

紅黑樹(shù)是平衡二叉樹(shù)的變種,它的操作的時(shí)間復(fù)雜度是O(logN).

紅黑樹(shù)是一種具有著色性質(zhì)的樹(shù),具有以下特點(diǎn):

每個(gè)節(jié)點(diǎn)被著成紅色或者黑色
根是黑色的
葉子節(jié)點(diǎn)都是黑色的,葉子節(jié)點(diǎn)指的是NULL節(jié)點(diǎn),有些紅黑樹(shù)圖中可能沒(méi)有標(biāo)記出來(lái)。
如果一個(gè)節(jié)點(diǎn)是紅色的,那么他額子節(jié)點(diǎn)必須是黑色的,也就是不會(huì)存在兩個(gè)紅色節(jié)點(diǎn)毗鄰。
從一個(gè)節(jié)點(diǎn)到一個(gè)葉子節(jié)點(diǎn)(NULL節(jié)點(diǎn))的每一條路徑必須包含相同數(shù)目的黑色節(jié)點(diǎn)。
紅黑樹(shù)也是一種二叉查找樹(shù),查找操作與二叉查找樹(shù)相同,插入與刪除操作有所不同。

1.4 B樹(shù)

B樹(shù)是一種自平衡的樹(shù),能夠保持?jǐn)?shù)據(jù)有序,B樹(shù)為系統(tǒng)大塊數(shù)據(jù)的讀寫(xiě)操作做了優(yōu)化,通常用在數(shù)據(jù)庫(kù)與文件系統(tǒng)的實(shí)現(xiàn)上。

我們前面講解了二叉查找樹(shù)、AVL樹(shù),紅黑樹(shù),這三種都是典型的二叉查找樹(shù)結(jié)構(gòu),其查找的事件復(fù)雜度O(logN)與樹(shù)的深度有關(guān),考慮這么一種情況,如果有 大量的數(shù)據(jù),而節(jié)點(diǎn)存儲(chǔ)的數(shù)據(jù)有限,這種情況下,我們只能去擴(kuò)充樹(shù)的深度,就會(huì)導(dǎo)致查找效率低下。

怎么解決這種問(wèn)題,一個(gè)簡(jiǎn)單的想法就是:二叉變多叉。

這里我們想象一下常見(jiàn)的文件系統(tǒng),它也是一種樹(shù)結(jié)構(gòu),在查找文件時(shí),樹(shù)的深度就決定了查找的效率。因此B樹(shù)就是為了減少數(shù)的深度從而提高查找效率的一種 數(shù)據(jù)結(jié)構(gòu)。

主要特點(diǎn)

一個(gè)階為M的B樹(shù)具有以下特點(diǎn):

注:M階指的是M叉查找樹(shù),例如M = 2,則為二叉查找樹(shù)。

數(shù)據(jù)項(xiàng)存儲(chǔ)在樹(shù)葉上
非葉節(jié)點(diǎn)存儲(chǔ)直到M-1個(gè)關(guān)鍵字以指示搜索方向:關(guān)鍵字代表子樹(shù)i+1中最小的關(guān)鍵字
樹(shù)的根或者是一片樹(shù)葉,或者其兒子數(shù)都在2和M之間。
除根外,所有非樹(shù)葉節(jié)點(diǎn)的兒子樹(shù)在M/2與M之間。
所有的樹(shù)葉都在相同的深度上擁有的數(shù)據(jù)項(xiàng)都在L/2與L之間。
性能分析

B樹(shù)在查找、插入以及刪除等操作中,時(shí)間復(fù)雜度為O(logN)。

二 樹(shù)的操作與源碼實(shí)現(xiàn)

在文章 01Java關(guān)于數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn):表、棧與隊(duì)列 中我們 討論了ArrayList與LinkedList的實(shí)現(xiàn),它們的瓶頸在于查找效率低下。因而Java集合設(shè)計(jì)了Set與Map接口,它們?cè)诓迦搿h除與查找等基本操作都有良好的表現(xiàn)。

2.1 TreeMap/TreeSet實(shí)現(xiàn)原理

TreeSet實(shí)際上是基于TreeMap的NavigableSet的實(shí)現(xiàn),它在功能上完全依賴(lài)于TreeMap,TreeMap是一個(gè)基于紅黑樹(shù)實(shí)現(xiàn)的Map,它在存儲(chǔ)時(shí)對(duì)元素進(jìn)行排序。

因此只要理解了TreeMap實(shí)現(xiàn)即可,TreeSet在功能上完全依賴(lài)于TreeMap。

TreeMap具有以下特點(diǎn):

TreeMap是一個(gè)有序的key-value集合,基于紅黑樹(shù)實(shí)現(xiàn)。
沒(méi)有實(shí)現(xiàn)同步
TreeMap實(shí)現(xiàn)以下接口:

NavigableMap:支持一系列導(dǎo)航方法,比如返回有序的key集合。
Cloneable:可以被克隆。
Serializable:支持序列化。
成員變量

//比較器 

private final Comparator<? super K> comparator;  

//根節(jié)點(diǎn) 

private transient TreeMapEntry<K,V> root = null;  

//集合大小 

private transient int size = 0;  

//修改次數(shù) 

private transient int modCount = 0


構(gòu)造方法

 

public TreeMap() {  

//默認(rèn)比較器 

comparator = null

 

public TreeMap(Comparator<? super K> comparator) {  

//指定比較器 

this.comparator = comparator; 

 

 

public TreeMap(Map<? extends K, ? extends V> m) {  

//默認(rèn)比較器 

comparator = null

putAll(m); 

 

public TreeMap(SortedMap<K, ? extends V> m) {  

//指定比較器 

comparator = m.comparator(); 

try { 

buildFromSorted(m.size(), m.entrySet().iterator(), null, null); 

} catch (java.io.IOException cannotHappen) { 

} catch (ClassNotFoundException cannotHappen) { 


內(nèi)部類(lèi)

TreeMap里面定義了靜態(tài)內(nèi)部類(lèi)TreeMapEntry來(lái)描述節(jié)點(diǎn)信息。

 

static final class TreeMapEntry<K,V> implements Map.Entry<K,V> { 

//鍵 

K key; 

//值 

V value; 

//指向左子樹(shù)的引用 

TreeMapEntry<K,V> left = null

//指向右子樹(shù)的引用 

TreeMapEntry<K,V> right = null

//指向父節(jié)點(diǎn)的引用 

TreeMapEntry<K,V> parent; 

//節(jié)點(diǎn)顏色,默認(rèn)為黑色 

boolean color = BLACK

 

/** 

* Make a new cell with given key, value, and parent, and with 

* {@code null} child links, and BLACK color. 

*/ 

TreeMapEntry(K key, V value, TreeMapEntry<K,V> parent) { 

this.key = key; 

this.value = value; 

this.parent = parent; 

 

/** 

* Returns the key. 

* @return the key 

*/ 

public K getKey() { 

return key; 

 

/** 

* Returns the value associated with the key. 

* @return the value associated with the key 

*/ 

public V getValue() { 

return value; 

 

/** 

* Replaces the value currently associated with the key with the given 

* value. 

* @return the value associated with the key before this method was 

* called 

*/ 

public V setValue(V value) { 

oldValue = this.value; 

this.value = value; 

return oldValue; 

 

public boolean equals(Object o) { 

if (!(o instanceof Map.Entry)) 

return false; 

Map.Entry<?,?> e = (Map.Entry<?,?>)o; 

 

return valEquals(key,e.getKey()) && valEquals(value,e.getValue()); 

 

public int hashCode() { 

int keyHash = (key==null ? 0 : key.hashCode()); 

int valueHash = (value==null ? 0 : value.hashCode()); 

return keyHash ^ valueHash; 

 

public String toString() { 

return key + "=" + value; 


操作方法

在正式介紹TreeMap里的增、刪、改、查操作之前,我們先來(lái)看看TreeMop里關(guān)于節(jié)點(diǎn)染色,樹(shù)的旋轉(zhuǎn)等操作的實(shí)現(xiàn),它們是TreeMap實(shí)現(xiàn)的基礎(chǔ)。

節(jié)點(diǎn)染色


在介紹染色規(guī)則之前,我們先來(lái)回顧一下紅黑樹(shù)的特點(diǎn):

每個(gè)節(jié)點(diǎn)被著成紅色或者黑色
根是黑色的
葉子節(jié)點(diǎn)都是黑色的,葉子節(jié)點(diǎn)指的是NULL節(jié)點(diǎn),有些紅黑樹(shù)圖中可能沒(méi)有標(biāo)記出來(lái)。
如果一個(gè)節(jié)點(diǎn)是紅色的,那么他額子節(jié)點(diǎn)必須是黑色的,也就是不會(huì)存在兩個(gè)紅色節(jié)點(diǎn)毗鄰。
從一個(gè)節(jié)點(diǎn)到一個(gè)葉子節(jié)點(diǎn)(NULL節(jié)點(diǎn))的每一條路徑必須包含相同數(shù)目的黑色節(jié)點(diǎn)。
關(guān)于節(jié)點(diǎn)染色,我們有多種情況需要考慮。

首先說(shuō)明

若新節(jié)點(diǎn)位于樹(shù)的根上,沒(méi)有父節(jié)點(diǎn),直接將其染成黑色即可。這個(gè)在代碼中無(wú)需操作,因?yàn)楣?jié)點(diǎn)默認(rèn)就是黑色的。
若新節(jié)點(diǎn)的父節(jié)點(diǎn)是黑色,這個(gè)時(shí)候樹(shù)依然滿足紅黑樹(shù)的性質(zhì),并不需要額外的處理。
以上兩種情況無(wú)需額外的處理,我們?cè)賮?lái)考慮需要處理的情況。

情況1:如果新節(jié)點(diǎn)N的父節(jié)點(diǎn)P是紅色,且其叔父節(jié)點(diǎn)U也為紅色,我們可以將父節(jié)點(diǎn)P與叔父節(jié)點(diǎn)U染成黑色,祖父節(jié)點(diǎn)G染成紅色。
情況2:如果新節(jié)點(diǎn)N的父節(jié)點(diǎn)P是紅色,且其叔父節(jié)點(diǎn)U為黑色或者沒(méi)有叔父節(jié)點(diǎn),P為其父節(jié)點(diǎn)P的右子節(jié)點(diǎn),P為為其父節(jié)點(diǎn)G的左子節(jié)點(diǎn)。這種情況我們針對(duì)祖父節(jié)點(diǎn)G做一次右旋。并將P染成黑色,染成紅色
情況3:如果新節(jié)點(diǎn)N的父節(jié)點(diǎn)P是紅色,且其叔父節(jié)點(diǎn)U為黑色或者沒(méi)有叔父節(jié)點(diǎn),P為其父節(jié)點(diǎn)P的右子節(jié)點(diǎn),P為為其父節(jié)點(diǎn)G的左子節(jié)點(diǎn)。這種情況下我們對(duì)做一次左旋調(diào)換,調(diào)換P與N的位置,這樣情況3變成了 情況2,剩下的按照情況2處理即可。
我們來(lái)看看具體的源碼實(shí)現(xiàn)。

 

public class TreeMap<K,V>  

extends AbstractMap<K,V> 

implements NavigableMap<K,V>, Cloneable, java.io.Serializable{ 

 

//染色 

private void fixAfterInsertion(TreeMapEntry<K,V> x) { 

x.color = RED

 

while (x != null && x != root && x.parent.color == RED) { 

 

//新節(jié)點(diǎn)N(即x)在其祖父節(jié)點(diǎn)的左子樹(shù)上,叔父節(jié)點(diǎn)在左子樹(shù)上。 

if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { 

TreeMapEntry<K,V> y = rightOf(parentOf(parentOf(x))); 

//情況1:如果新節(jié)點(diǎn)N的父節(jié)點(diǎn)P是紅色,且其叔父節(jié)點(diǎn)U也為紅色,我們可以將父節(jié)點(diǎn)P與叔父節(jié)點(diǎn)U染成黑色,祖父節(jié)點(diǎn)G染成紅色。 

if (colorOf(y) == RED) { 

setColor(parentOf(x), BLACK); 

setColor(y, BLACK); 

setColor(parentOf(parentOf(x)), RED); 

x = parentOf(parentOf(x)); 

}  

//情況2:如果新節(jié)點(diǎn)N的父節(jié)點(diǎn)P是紅色,且其叔父節(jié)點(diǎn)U為黑色或者沒(méi)有叔父節(jié)點(diǎn),P為其父節(jié)點(diǎn)P的右子節(jié)點(diǎn),P為為其父節(jié)點(diǎn)G的左 

//子節(jié)點(diǎn)。這種情況我們針對(duì)祖父節(jié)點(diǎn)G做一次右旋。并將P染成黑色,染成紅色 

else { 

//情況3:x為其父節(jié)點(diǎn)的右子節(jié)點(diǎn),先對(duì)其父節(jié)點(diǎn)進(jìn)行左旋,調(diào)換兩者的位置 

if (x == rightOf(parentOf(x))) { 

x = parentOf(x); 

rotateLeft(x); 

setColor(parentOf(x), BLACK); 

setColor(parentOf(parentOf(x)), RED); 

rotateRight(parentOf(parentOf(x))); 

}  

//情況1:新節(jié)點(diǎn)N(即x)在其祖父節(jié)點(diǎn)的右子樹(shù)上,叔父節(jié)點(diǎn)在左子樹(shù)上,這種情況和在右子節(jié)點(diǎn)的情況相似,知識(shí)旋轉(zhuǎn)方向相反罷了。 

else { 

TreeMapEntry<K,V> y = leftOf(parentOf(parentOf(x))); 

//如果新節(jié)點(diǎn)N的父節(jié)點(diǎn)P是紅色,且其叔父節(jié)點(diǎn)U也為紅色,我們可以將父節(jié)點(diǎn)P與叔父節(jié)點(diǎn)U染成黑色,祖父節(jié)點(diǎn)G染成紅色。 

if (colorOf(y) == RED) { 

setColor(parentOf(x), BLACK); 

setColor(y, BLACK); 

setColor(parentOf(parentOf(x)), RED); 

x = parentOf(parentOf(x)); 

}  

//情況2:如果新節(jié)點(diǎn)N的父節(jié)點(diǎn)P是紅色,且其叔父節(jié)點(diǎn)U為黑色或者沒(méi)有叔父節(jié)點(diǎn),P為其父節(jié)點(diǎn)P的右子節(jié)點(diǎn),P為為其父節(jié)點(diǎn)G的左 

//子節(jié)點(diǎn)。這種情況我們針對(duì)祖父節(jié)點(diǎn)G做一次右旋。并將P染成黑色,染成紅色 

else { 

//情況3:x為其父節(jié)點(diǎn)的左子節(jié)點(diǎn),先對(duì)其父節(jié)點(diǎn)進(jìn)行右旋,調(diào)換兩者位置。 

if (x == leftOf(parentOf(x))) { 

x = parentOf(x); 

rotateRight(x); 

setColor(parentOf(x), BLACK); 

setColor(parentOf(parentOf(x)), RED); 

rotateLeft(parentOf(parentOf(x))); 

root.color = BLACK


節(jié)點(diǎn)旋轉(zhuǎn)

左旋

之前在網(wǎng)上看到一組關(guān)于左旋、右旋的動(dòng)態(tài)圖,很形象,這里也貼出來(lái)。


找到要旋轉(zhuǎn)節(jié)點(diǎn)p的右節(jié)點(diǎn)r,然后將r的左子節(jié)點(diǎn)賦值給p的右子節(jié)點(diǎn),如果r的左子節(jié)點(diǎn)為空,則直接將r節(jié)點(diǎn)設(shè)置為P的父節(jié)點(diǎn)。
將原來(lái)p的父節(jié)點(diǎn)設(shè)置成r的父節(jié)點(diǎn),如果原來(lái)p的父節(jié)點(diǎn)為空,則直接接r設(shè)置成根節(jié)點(diǎn),如果根節(jié)點(diǎn)不空且其做子節(jié)點(diǎn)為p,則將r設(shè)置為新的左子節(jié)點(diǎn),如果根節(jié)點(diǎn)不空且其右子節(jié)點(diǎn)為p,則將r設(shè)置為新的右子節(jié)點(diǎn),
再講r的左子節(jié)點(diǎn)設(shè)為p,p的父節(jié)點(diǎn)設(shè)置為r,左旋完成。
右旋


找到要旋轉(zhuǎn)節(jié)點(diǎn)p的左子節(jié)點(diǎn)l,然后將l的右子節(jié)點(diǎn)賦值給p的左子節(jié)點(diǎn),如果l的右子節(jié)點(diǎn)為空,則直接將l節(jié)點(diǎn)設(shè)置為P的父節(jié)點(diǎn)。
將原來(lái)p的父節(jié)點(diǎn)設(shè)置成l的父節(jié)點(diǎn),如果原來(lái)p的父節(jié)點(diǎn)為空,則直接接l設(shè)置成根節(jié)點(diǎn),如果根節(jié)點(diǎn)不空且其做子節(jié)點(diǎn)為p,則將l設(shè)置為新的左子節(jié)點(diǎn),如果根節(jié)點(diǎn)不空且其右子節(jié)點(diǎn)為p,則將l設(shè)置為新的右子節(jié)點(diǎn),
再講l的右子節(jié)點(diǎn)設(shè)為p,p的父節(jié)點(diǎn)設(shè)置為l,右旋完成。
我們來(lái)看看具體的源碼實(shí)現(xiàn)。

 

public class TreeMap<K,V>  

extends AbstractMap<K,V> 

implements NavigableMap<K,V>, Cloneable, java.io.Serializable{ 

 

//左旋 

private void rotateLeft(TreeMapEntry<K,V> p) { 

if (p != null) { 

//找到要旋轉(zhuǎn)節(jié)點(diǎn)p的右節(jié)點(diǎn)r 

TreeMapEntry<K,V> r = p.right; 

//然后將r的左子節(jié)點(diǎn)賦值給p的右子節(jié)點(diǎn) 

p.right = r.left; 

//如果r的左子節(jié)點(diǎn)為空,則直接將r節(jié)點(diǎn)設(shè)置為P的父節(jié)點(diǎn) 

if (r.left != null) 

r.left.parent = p; 

//將原來(lái)p的父節(jié)點(diǎn)設(shè)置成r的父節(jié)點(diǎn) 

r.parent = p.parent; 

//如果原來(lái)p的父節(jié)點(diǎn)為空,則直接接r設(shè)置成根節(jié)點(diǎn) 

if (p.parent == null) 

rroot = r; 

//如果根節(jié)點(diǎn)不空且其做子節(jié)點(diǎn)為p,則將r設(shè)置為新的左子節(jié)點(diǎn) 

else if (p.parent.left == p) 

p.parent.left = r; 

//如果根節(jié)點(diǎn)不空且其右子節(jié)點(diǎn)為p,則將r設(shè)置為新的右子節(jié)點(diǎn)  

else 

p.parent.right = r; 

//再講r的左子節(jié)點(diǎn)設(shè)為p,p的父節(jié)點(diǎn)設(shè)置為r,左旋完成 

r.left = p

p.parent = r; 

 

//右旋 

private void rotateRight(TreeMapEntry<K,V> p) { 

if (p != null) { 

//找到要旋轉(zhuǎn)節(jié)點(diǎn)p的左子節(jié)點(diǎn)l 

TreeMapEntry<K,V> l = p.left; 

//然后將l的右子節(jié)點(diǎn)賦值給p的左子節(jié)點(diǎn) 

p.left = l.right; 

//如果l的右子節(jié)點(diǎn)為空,則直接將l節(jié)點(diǎn)設(shè)置為P的父節(jié)點(diǎn) 

if (l.right != null) l.right.parent = p; 

//將原來(lái)p的父節(jié)點(diǎn)設(shè)置成l的父節(jié)點(diǎn) 

l.parent = p.parent; 

//如果原來(lái)p的父節(jié)點(diǎn)為空,則直接接l設(shè)置成根節(jié) 

if (p.parent == null) 

root = l

//如果根節(jié)點(diǎn)不空且其右子節(jié)點(diǎn)為p,則將l設(shè)置為新的右子節(jié)點(diǎn) 

else if (p.parent.right == p) 

p.parent.right = l

//如果根節(jié)點(diǎn)不空且其做子節(jié)點(diǎn)為p,則將l設(shè)置為新的左子節(jié)點(diǎn) 

else p.parent.left = l; 

//再講l的右子節(jié)點(diǎn)設(shè)為p,p的父節(jié)點(diǎn)設(shè)置為l,右旋完成。 

l.right = p

p.parent = l

put 

 

public class TreeMap<K,V>  

extends AbstractMap<K,V> 

implements NavigableMap<K,V>, Cloneable, java.io.Serializable{ 

 

public V put(K key, V value) { 

//找到根節(jié)點(diǎn) 

TreeMapEntry<K,V> t = root

//如果根節(jié)點(diǎn)為空,則設(shè)置該元素為 

if (t == null) { 

if (comparator != null) { 

if (key == null) { 

comparator.compare(key, key); 

} else { 

if (key == null) { 

throw new NullPointerException("key == null"); 

} else if (!(key instanceof Comparable)) { 

throw new ClassCastException( 

"Cannot cast" + key.getClass().getName() + " to Comparable."); 

 

root = new TreeMapEntry<>(key, value, null); 

//集合大小為1 

size = 1

//修改次數(shù)自增 

modCount++; 

return null; 

int cmp; 

TreeMapEntry<K,V> parent; 

//獲取比較器 

Comparator<? super K> cpr = comparator

//如果比較器不空,則用指定的比較器進(jìn)行比較 

if (cpr != null) { 

//循環(huán)遞歸,從根節(jié)點(diǎn)開(kāi)始查找插入的位置,即查找的它的父節(jié)點(diǎn),查找方式和我們上面講的二叉排序樹(shù)的查找方式相同 

do { 

parent = t; 

cmp = cpr.compare(key, t.key); 

//插入值小于當(dāng)前節(jié)點(diǎn),則繼續(xù)在左子樹(shù)上查詢(xún) 

if (cmp < 0

tt = t.left; 

//插入值大于當(dāng)前節(jié)點(diǎn),則繼續(xù)在右子樹(shù)上查詢(xún) 

else if (cmp > 0) 

tt = t.right; 

//如果相等,則替換當(dāng)前的值 

else 

return t.setValue(value); 

} while (t != null); 

//如果比較器為坤寧宮,則使用默認(rèn)的比較器 

else { 

if (key == null) 

throw new NullPointerException(); 

@SuppressWarnings("unchecked") 

Comparable<? super K> k = (Comparable<? super K>) key; 

do { 

parent = t; 

cmp = k.compareTo(t.key); 

if (cmp < 0

tt = t.left; 

else if (cmp > 0) 

tt = t.right; 

else 

return t.setValue(value); 

} while (t != null); 

//根據(jù)查找到的父節(jié)點(diǎn),構(gòu)造節(jié)點(diǎn),并根據(jù)比結(jié)果將其插入到對(duì)應(yīng)的位置 

TreeMapEntry<K,V> e = new TreeMapEntry<>(key, value, parent); 

if (cmp < 0

parent.left = e; 

else 

parent.right = e; 

//給插入的節(jié)點(diǎn)染色 

fixAfterInsertion(e); 

size++; 

modCount++; 

return null; 


插入操作采用了二叉排序樹(shù)的查找算法,整個(gè)流程如下:

如果當(dāng)前TreeMap沒(méi)有根節(jié)點(diǎn),將當(dāng)前節(jié)點(diǎn)作為根節(jié)點(diǎn)插入,否則,
根據(jù)提供的比較器(如果沒(méi)有提供則使用默認(rèn)的比較器)進(jìn)行查找比較,查找該節(jié)點(diǎn)的插入位置,即它的父節(jié)點(diǎn)的位置。
查找到父節(jié)點(diǎn)后,根據(jù)比較結(jié)果插入到對(duì)應(yīng)位置,并進(jìn)行染色處理。
remove

前面我們講了插入操作,刪除操作要比插入操作復(fù)雜一下,我們先來(lái)描述一下刪除操作的大概流程:

將紅黑樹(shù)當(dāng)成一棵二叉查找樹(shù),進(jìn)行節(jié)點(diǎn)刪除。
通過(guò)旋轉(zhuǎn)和著色,使其重新變成一棵復(fù)合規(guī)則的紅黑樹(shù)。
二叉查找樹(shù)時(shí)怎么做刪除的。前面我們已經(jīng)說(shuō)過(guò),在二叉查找樹(shù)上刪除一個(gè)節(jié)點(diǎn),分為三種情況:


若刪除的是葉子節(jié)點(diǎn),則不會(huì)破壞樹(shù)的結(jié)構(gòu),只需要修改其雙親節(jié)點(diǎn)的指針即可。
若刪除的節(jié)點(diǎn)只有一個(gè)孩子節(jié)點(diǎn),則用它的孩子節(jié)點(diǎn)代替它的位置即可,如此也不會(huì)破壞紅黑樹(shù)的結(jié)構(gòu)。
若刪除的節(jié)點(diǎn)有兩個(gè)孩子節(jié)點(diǎn),這種情況復(fù)雜一下,我們通常會(huì)找到要?jiǎng)h除節(jié)點(diǎn)X的左子樹(shù)里的最大元素或者右子樹(shù)里的最小元素,然后用M替換掉X,再刪除節(jié)點(diǎn),因?yàn)榇藭r(shí)M最多只會(huì)有一個(gè)
節(jié)點(diǎn)(如果左子樹(shù)最大元素則沒(méi)有右子節(jié)點(diǎn),若是右子樹(shù)最小元素則沒(méi)有左子節(jié)點(diǎn)),若M沒(méi)有孩子節(jié)點(diǎn),直接進(jìn)入情況1處理,若M只有一個(gè)孩子,則直接進(jìn)入情況2處理。
注:這里的替換指的是值拷貝,值拷貝并不會(huì)破壞紅黑樹(shù)的性質(zhì)。

這樣三種情況都可以當(dāng)做第一種或者第二種情況處理。

在刪除節(jié)點(diǎn)時(shí),我們有兩個(gè)問(wèn)題需要注意:

如果刪除的額是紅色節(jié)點(diǎn),不會(huì)違反紅黑樹(shù)的規(guī)則。
如果刪除的是黑色節(jié)點(diǎn),那么這個(gè)路徑上就少了一個(gè)黑色節(jié)點(diǎn),則違反了紅黑樹(shù)規(guī)則。
這樣我們可以得知只有在插入的節(jié)點(diǎn)是黑色的時(shí)候才需要我們進(jìn)行處理,具體說(shuō)來(lái):

情況1:若刪除節(jié)點(diǎn)N的兄弟節(jié)點(diǎn)B是紅色,這種情況下,先對(duì)父節(jié)點(diǎn)P進(jìn)行左旋操作,結(jié)合對(duì)換P與B的顏色。此時(shí)左子樹(shù)仍然少了一個(gè)黑色節(jié)點(diǎn),此時(shí)進(jìn)入情況3.
情況2:若刪除節(jié)點(diǎn)N的父親節(jié)點(diǎn)P,兄弟節(jié)點(diǎn)B以及B的兒子節(jié)點(diǎn)都是黑色,則將B染成紅色,這樣P到葉子節(jié)點(diǎn)的所有路徑都包含了相同的黑色節(jié)點(diǎn),但是P的父節(jié)點(diǎn)G到葉子節(jié)點(diǎn)的路徑卻少了 一個(gè)黑色節(jié)點(diǎn)。這個(gè)時(shí)候我們要重新按照這套規(guī)則對(duì)P節(jié)點(diǎn)再進(jìn)行一次平衡處理。
情況3:若刪除節(jié)點(diǎn)N的父親節(jié)點(diǎn)P是紅色,兄弟節(jié)點(diǎn)B是黑色,則交換P與B顏色,這樣在B所在路徑上增加了一個(gè)黑色節(jié)點(diǎn),彌補(bǔ)了已經(jīng)刪除的,樹(shù)重新達(dá)到平衡。
情況4: 若刪除節(jié)點(diǎn)N的兄弟節(jié)點(diǎn)B是黑澀,B的左孩子節(jié)點(diǎn)BL是紅色,B的右孩子節(jié)點(diǎn)BR是黑色,P為任意顏色。則減緩B與BL的顏色,右旋節(jié)點(diǎn)B。此時(shí)N所在路徑并沒(méi)有增加黑色節(jié)點(diǎn),沒(méi)有達(dá)到平衡,進(jìn)入情況5.
情況5:若刪除節(jié)點(diǎn)N的兄弟節(jié)點(diǎn)B是黑色,B的右孩子節(jié)點(diǎn)BR是紅色,B的左孩子節(jié)點(diǎn)BL為任意顏色,P為任意顏色。則BR染成黑色,P染成黑色,B染成原來(lái)P的顏色;左旋節(jié)點(diǎn),這樣 N路徑上增加了一個(gè)黑色節(jié)點(diǎn),B路徑上少了一個(gè)黑色節(jié)點(diǎn)B,又增加了一個(gè)黑色節(jié)點(diǎn)BR,剛好達(dá)到平衡。
以上的流程看起來(lái)比較復(fù)雜,本質(zhì)上來(lái)說(shuō)就是我們刪除了一個(gè)黑色節(jié)點(diǎn),破壞了當(dāng)前路徑黑色節(jié)點(diǎn)的個(gè)數(shù),解決的方法要么為這條路徑再添加一個(gè)黑色節(jié)點(diǎn),要么將其他路徑的黑色節(jié)點(diǎn)都去掉一個(gè)。

 

public class TreeMap<K,V>  

extends AbstractMap<K,V> 

implements NavigableMap<K,V>, Cloneable, java.io.Serializable{ 

 

public V remove(Object key) { 

TreeMapEntry<K,V> p = getEntry(key); 

if (p == null) 

return null; 

 

oldValue = p.value; 

deleteEntry(p); 

return oldValue; 

 

private void deleteEntry(TreeMapEntry<K,V> p) { 

//操作記錄自增 

modCount++; 

//集合大小自減 

size--; 

 

///如果要?jiǎng)h除的節(jié)點(diǎn)p的左右子節(jié)點(diǎn)都不為空,則查找其替代節(jié)點(diǎn)并進(jìn)行節(jié)點(diǎn)替換 

if (p.left != null && p.right != null) { 

//查找其替代節(jié)點(diǎn),替代節(jié)點(diǎn)為左子樹(shù)的最大元素或者右子樹(shù)的最小元素 

TreeMapEntry<K,V> s = successor(p); 

p.key = s.key; 

p.value = s.value; 

p = s

} // p has 2 children 

 

//查找替代節(jié)點(diǎn)的孩子節(jié)點(diǎn),replacement指的是我們圖中說(shuō)來(lái)的N節(jié)點(diǎn),p指的是圖中的 

TreeMapEntry<K,V> replacement = (p.left != null ? p.left : p.right); 

 

//刪除p,并重新建立replacement節(jié)點(diǎn)的連接 

if (replacement != null) { 

replacement.parent = p.parent; 

if (p.parent == null) 

root = replacement

else if (p == p.parent.left) 

p.parent.left = replacement

else 

p.parent.right = replacement

 

// Null out links so they are OK to use by fixAfterDeletion. 

pp.left = p.right = p.parent = null

 

//如果刪除的黑色節(jié)點(diǎn),則需要重新平衡樹(shù) 

if (p.color == BLACK) 

fixAfterDeletion(replacement); 

} else if (p.parent == null) { // return if we are the only node. 

root = null

} else { // No children. Use self as phantom replacement and unlink. 

if (p.color == BLACK) 

fixAfterDeletion(p); 

 

if (p.parent != null) { 

if (p == p.parent.left) 

p.parent.left = null

else if (p == p.parent.right) 

p.parent.right = null

p.parent = null

 

//查找其替代節(jié)點(diǎn),替代節(jié)點(diǎn)為左子樹(shù)的最大元素或者右子樹(shù)的最小元素 

static <K,V> TreeMapEntry<K,V> successor(TreeMapEntry<K,V> t) { 

if (t == null) 

return null; 

//查找右子樹(shù)的最小元素,即最左孩子 

else if (t.right != null) { 

TreeMapEntry<K,V> p = t.right; 

while (p.left != null) 

pp = p.left; 

return p; 

}  

//查找左子樹(shù)的最大元素,即最右孩子 

else { 

TreeMapEntry<K,V> p = t.parent; 

TreeMapEntry<K,V> ch = t

while (p != null && ch == p.right) { 

ch = p

pp = p.parent; 

return p; 

 

static <K,V> TreeMapEntry<K,V> predecessor(TreeMapEntry<K,V> t) { 

if (t == null) 

return null; 

else if (t.left != null) { 

TreeMapEntry<K,V> p = t.left; 

while (p.right != null) 

pp = p.right; 

return p; 

} else { 

TreeMapEntry<K,V> p = t.parent; 

TreeMapEntry<K,V> ch = t

while (p != null && ch == p.left) { 

ch = p

pp = p.parent;s 

return p; 


我們?cè)賮?lái)看看deleteEntry()方法的實(shí)現(xiàn)流程:

如果要?jiǎng)h除的節(jié)點(diǎn)p的左右子節(jié)點(diǎn)都不為空,則查找其替代節(jié)點(diǎn)并進(jìn)行節(jié)點(diǎn)替換。
查找替代節(jié)點(diǎn)的孩子節(jié)點(diǎn),replacement指的是我們圖中說(shuō)來(lái)的N節(jié)點(diǎn),p指的是圖中的M,如果p是黑色節(jié)點(diǎn),則刪除p后需要重新進(jìn)行
樹(shù)的平衡處理。
get

 

public class TreeMap<K,V>  

extends AbstractMap<K,V> 

implements NavigableMap<K,V>, Cloneable, java.io.Serializable{ 

 

public V get(Object key) { 

TreeMapEntry<K,V> p = getEntry(key); 

return (p==null ? null : p.value); 

 

final TreeMapEntry<K,V> getEntry(Object key) { 

// Offload comparator-based version for sake of performance 

if (comparator != null) 

return getEntryUsingComparator(key); 

if (key == null) 

throw new NullPointerException(); 

@SuppressWarnings("unchecked") 

Comparable<? super K> k = (Comparable<? super K>) key; 

TreeMapEntry<K,V> p = root

//從根節(jié)點(diǎn)開(kāi)始查找,根據(jù)比較結(jié)果決定從左子樹(shù)開(kāi)始查找還是從右子樹(shù)開(kāi)始查找 

while (p != null) { 

int cmp = k.compareTo(p.key); 

if (cmp < 0

pp = p.left; 

else if (cmp > 0) 

pp = p.right; 

else 

return p; 

return null; 


TreeMap的查找流程和二叉查找樹(shù)的查找流程是一樣的,這里是從根節(jié)點(diǎn)開(kāi)始查找,根據(jù)比較結(jié)果決定是下一步是從左子樹(shù)開(kāi)始查找,還是從右子樹(shù)開(kāi)始查找。

責(zé)任編輯:張燕妮 來(lái)源: 推酷
相關(guān)推薦

2016-10-09 08:57:11

python數(shù)據(jù)結(jié)構(gòu)與算法樹(shù)形結(jié)構(gòu)

2012-05-16 17:05:33

Java數(shù)據(jù)結(jié)構(gòu)

2022-09-21 07:57:33

二叉搜索樹(shù)排序二叉樹(shù)

2022-09-26 07:56:53

AVL算法二叉樹(shù)

2021-03-18 08:44:20

Java數(shù)據(jù)結(jié)構(gòu)算法

2017-11-14 13:48:26

樹(shù)數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)

2017-10-10 16:59:28

Java數(shù)據(jù)結(jié)構(gòu)算法解析

2021-04-07 09:26:37

Java數(shù)據(jù)結(jié)構(gòu)算法

2020-10-30 09:56:59

Trie樹(shù)之美

2021-01-07 08:12:47

數(shù)據(jù)結(jié)構(gòu)二叉樹(shù)樹(shù)

2021-05-21 08:31:09

數(shù)據(jù)結(jié)構(gòu)二叉樹(shù)樹(shù)

2021-03-24 10:41:04

Java數(shù)據(jù)結(jié)構(gòu)算法

2021-04-20 08:37:14

數(shù)據(jù)結(jié)構(gòu)二叉樹(shù)樹(shù)

2022-09-14 07:59:27

字典樹(shù)Trie基數(shù)樹(shù)

2023-09-25 12:23:18

Python

2009-08-13 18:34:49

C#數(shù)據(jù)結(jié)構(gòu)和算法

2019-09-03 10:40:23

數(shù)據(jù)結(jié)構(gòu)HTML編程

2013-01-30 10:34:02

數(shù)據(jù)結(jié)構(gòu)

2011-07-04 10:32:37

JAVA

2023-09-22 11:17:50

紅黑樹(shù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)