国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找

這篇具有很好參考價(jià)值的文章主要介紹了考研數(shù)據(jù)結(jié)構(gòu):第七章 查找。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。


一、查找的基本概念

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

ps:查找表可以是線性結(jié)構(gòu)、樹狀結(jié)構(gòu)、圖狀結(jié)構(gòu)等等

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

評價(jià)一個(gè)查找算法的優(yōu)劣:主要看算法的平均查找長度ASL
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

舉個(gè)例子,我們現(xiàn)在有如下二叉排序樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果你要查的是50,那么從根節(jié)點(diǎn)出發(fā)只需要對比一次關(guān)鍵字就可以了,所以第一項(xiàng)是1 * 1

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果你要查的是第二層的26或者66,你找26總共需要進(jìn)行兩輪對比,找66又需要兩輪對比,所以查第二層元素共需要是2 * 2次對比
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第三層同理,長度為3,共4個(gè)數(shù)據(jù),所以3 * 4

第四層同理,長度為4,共1個(gè)數(shù)據(jù),所以4 * 1

然后累和除8,這里8是指一共8個(gè)數(shù)據(jù),平均到每個(gè)數(shù)據(jù)身上是1/8

我們之前還討論過二叉排序樹查找失敗的情況,所以在評價(jià)一個(gè)查找算法的查找效率時(shí),我們通常會(huì)分開考慮查找成功和查找失敗兩種情況下的平均查找長度

比如同樣的二叉排序樹,查找成功和查找失敗的ASL是不同的:
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

ASL的數(shù)量級可以直接反映出你的查找算法的時(shí)間復(fù)雜度
ASL的計(jì)算也是考研中的重點(diǎn),請務(wù)必掌握

二、順序查找和折半查找

2.1順序查找

順序查找顧名思義,就是按照順序一個(gè)個(gè)找,你可以從前往后,也可以從后往前。這個(gè)是最無腦的查找。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
代碼如下:

typedef struct{//查找表的數(shù)據(jù)結(jié)構(gòu)(順序表)
    ElemType *elem;//動(dòng)態(tài)數(shù)組基址
    int TableLen;//表的長度
}SSTable;

//順序查找
int Search_Seq(SSTable ST,ElemType key){
    int i;
    for(i=0;i<ST.TableLen&&ST.elem[i]!=key;i++);
    //查找成功,則返回元素下標(biāo),查找失敗則返回-1
    return i==ST.TableLen?-1:i;
}

還有一種順序查找就是帶哨兵位的,也就是0號位置空出來,放你要找的那個(gè)東西
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
這種哨兵就是從后往前找,如果找到哨兵的下標(biāo),也就是這里的0,就說明查找失敗了。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

我們前面說過,當(dāng)我們在評價(jià)一個(gè)查找算法時(shí),我們通常是算它的平均查找長度ASL。而平均查找長度又分為查找成功和查找失敗兩種情況。

查找成功情況:
如果是帶哨兵的實(shí)現(xiàn)方式,我們是從最后的位置開始往前掃描。

如果是找最后一個(gè)關(guān)鍵字,那你所需要對比關(guān)鍵字的次數(shù)就是1。
如果假設(shè)我們找任何一個(gè)關(guān)鍵字概率都是相同的,那么找最后一個(gè)關(guān)鍵字的概率就是1/n
查找長度為1 * (1/n)

如果是找倒數(shù)第二個(gè)關(guān)鍵字,那要對比關(guān)鍵字次數(shù)就是2
查找長度為2 * (1/n)

下面以此類推…

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

如果是查找失敗,那就是把所有的n個(gè)元素對比完了,還要再和哨兵對比一下。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
總之,不管是查找成功還是查找失敗,時(shí)間復(fù)雜度都是O(n)

順序查找的優(yōu)化(對有序表)
如果要查的表本來就是有序的,那我們查找可以進(jìn)一步優(yōu)化

舉個(gè)例子
你要在如下的順序表中進(jìn)行查找21的操作
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
其實(shí)不用遍歷完所有元素,當(dāng)你遍歷到29發(fā)現(xiàn)還沒找到21,就已經(jīng)可以不用找了,因?yàn)楹竺嫒潜?1大的元素。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

順序查找的優(yōu)化(被查概率不相等情況)
如果是被查概率不同的,我們可以把被查找概率更大的元素放在查找相對靠前的位置
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果采取這種策略,可以減少查找成功的平均查找長度
但是對于查找失敗的情況,我們只能從頭掃到尾,來確定是不是查找失敗。
所以,如果你實(shí)際運(yùn)用中如果查找成功更多,用這種方法會(huì)更好一些。

2.3折半查找

2.3.1算法思想

折半查找又稱為二分查找,它只適用于有序的順序表

先看一個(gè)查找成功的例子:
比如現(xiàn)在要在下面的順序表中找33
我們會(huì)先用兩個(gè)指針low和high分別指向我們目前要搜索的區(qū)間范圍
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第一輪要檢查的元素是low和high中間的一個(gè)元素,我們用指針mid來指向它
mid的計(jì)算方式也很簡單,就是(low+high)/2
比如這里high=10,low=0,mid=(low+high)/2=5

所以我們第一輪要檢查的元素就是29,對比mid指向的元素29和我們要查的33
發(fā)現(xiàn)29<33,如果33存在,那么肯定是在我們mid的右邊

low=mid+1,
mid=(low+high)/2
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

第二輪
對比mid指向的元素37和要查找的元素33
33<37,如果33存在,那應(yīng)該在mid左邊

high=mid-1
mid=(low+high)/2
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第三輪
對比mid指向的元素32和要查找的元素33
32<33
那么33肯定在mid右邊
low=mid+1
mid=(low+high)/2
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

下一輪檢查發(fā)現(xiàn)剛好mid指向值等于33,那么查找成功。

再看一個(gè)查找失敗的例子:

還是剛才那個(gè)表,我們現(xiàn)在要查找一個(gè)不存在的元素12

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
剛開始還是一樣的,low和high指向順序表開頭和結(jié)尾元素
mid=(low+high)/2
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第一輪對比,12<29,那么12只會(huì)出現(xiàn)在mid左邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

第二輪對比12<13,如果存在只會(huì)在mid左邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

第三輪對比,7<12,如果存在,12在mid右邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第四輪對比,10<12,如果存在,12在mid右邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
此時(shí)出現(xiàn)了low>high的情況,那么查找失敗

2.3.2代碼實(shí)現(xiàn)

typedef struct{//查找表的數(shù)據(jù)結(jié)構(gòu)
    ElemType *elem;//動(dòng)態(tài)數(shù)組的基址
    int TableLen;//表的長度
}SSTable;

//折半查找-升序(降序需要對下面的判斷條件進(jìn)行相應(yīng)更改)
int Binary_Search(SSTable L,ElemType key){
    int low=0,high=L.TableLen-1,mid;
    while(low<=high){
       mid=(low+high)/2;
       if(L.elem[mid]==key)
          return mid;
       else if(L.elem[mid]>key)
          high=mid-1;
       else
          low=mid+1;
    }
    return -1;
}

2.3.3查找效率分析

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

2.3.4折半查找判定樹的構(gòu)造

考研中還是比較喜歡喜歡考察折半查找判斷樹的構(gòu)造,下面我們來進(jìn)行介紹

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

2.3.5折半查找效率

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

2.3.6小結(jié)

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
ps:只能說大部分情況折半查找比順序查找效率更高。
因?yàn)槿绻阋榈脑鼐褪琼樞虮淼谝粋€(gè)元素,順序查找是要更快的。

2.4分塊查找

分塊查找我們主要是介紹算法思想,考試中很少考代碼題,大家會(huì)用手算模擬即可。

比如下面這個(gè)數(shù)組,看起來元素都是亂的
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果仔細(xì)觀察,我們可以把這些元素分成一個(gè)個(gè)小區(qū)間
第一個(gè)區(qū)間:<=10
第二個(gè)區(qū)間:<=20
第三個(gè)區(qū)間:<=30

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果單獨(dú)看元素是亂序的,但是如果我們把它們分成一個(gè)個(gè)小區(qū)間后,可以發(fā)現(xiàn)各個(gè)區(qū)間之間其實(shí)是有序的,我們可以給這個(gè)查找表建立上一級索引。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
看上圖,很好理解。索引表中保存的是每一個(gè)分塊中最大的關(guān)鍵字,還有分塊的存儲區(qū)間。

比如說我們索引表中有個(gè)30,那就說明其中出現(xiàn)最大的關(guān)鍵字是30,然后這個(gè)分塊的存儲區(qū)間是6-8,也就是第三個(gè)分塊下標(biāo)起始到末尾是6到8
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

所以分塊查找的特點(diǎn):快內(nèi)無序,快間有序

該索引表數(shù)據(jù)結(jié)構(gòu)如下:

//索引表
typedef struct{
    ElemType maxValue;//每個(gè)表項(xiàng)最大關(guān)鍵字
    int low,high;//分塊的區(qū)間范圍
}Index;

//順序表存儲實(shí)際元素
ElemType List[100];

先來看一個(gè)查找成功的例子
比如現(xiàn)在查22
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
先查找索引表,從索引表第一個(gè)元素依次往后找,
第一個(gè)元素10<22,繼續(xù)往下
第二個(gè)元素20<22,繼續(xù)往下
第三個(gè)元素30>=22,如果22存在,一定在此區(qū)間
接下來就從分塊的起始位置,也就是6號下標(biāo)往8號下標(biāo)里找
我們在7號下標(biāo)找到22
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

再來看一個(gè)查找失敗的例子
現(xiàn)在要查29,依次遍歷索引表,確定如果29存在是在第三個(gè)分塊內(nèi)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
然后我們從第三個(gè)分塊開始查找,第一個(gè)27不滿足
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第二個(gè)22不滿足
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第三個(gè)30不滿足
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
再往后,發(fā)現(xiàn)已經(jīng)超出第三個(gè)分塊范圍了,就說明29不存在
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

剛才在查找索引表時(shí),我們是按順序查找的方式,也就是從索引表的第一個(gè)元素一個(gè)個(gè)往后找

但是由于索引表中保存的元素是有序的,并且我們索引表是用數(shù)組的方式,也就是順序存儲的方式來實(shí)現(xiàn)的。所以,針對索引表,我們可以采用折半查找,而對每個(gè)分塊內(nèi)的元素(亂序),只能順序查找。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
ps:分塊查找又稱為索引順序查找,先索引表,然后順序查找分塊內(nèi)元素嘛

三、樹形查找

3.1二叉排序樹

3.1.1二叉排序樹定義

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
二叉樹又稱二叉查找樹BST
二叉排序樹的左子樹結(jié)點(diǎn)值<根節(jié)點(diǎn)值<右子樹值,如果用中序遍歷(左根右),就可以得到一個(gè)遞增的序列。

二叉排序樹的左子樹和右子樹也是一棵二叉排序樹

3.1.2查找操作

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

先看一個(gè)查找成功的例子:
假設(shè)我們現(xiàn)在要在一棵二叉排序樹上找30
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
從19出發(fā),19<30,如果30存在,則在19的右子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
50>30,如果30存在,則在50的左子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
26<30,如果30存在,則在26的右子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
成功找到30

再看一個(gè)查找失敗的例子:
比如現(xiàn)在要查12

從19出發(fā),如果12存在,則在19左子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
13>12,如果12存在,則在13左子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
11<12,如果12存在則在11右子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
但是我們發(fā)現(xiàn),11沒有右子樹了,說明查找失敗了
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
查找代碼如下:

//二叉排序樹結(jié)點(diǎn)
typedef struct BSTNode{
    int key;
    struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;

//在二叉排序樹中查找值為key的結(jié)點(diǎn)
BSTNode *BST_Search(BSTree T,int key){
    while(T!=NULL&&key!=T->key){//若樹空或等于根節(jié)點(diǎn)值,則結(jié)束循環(huán)
        if(key < T->key){
           T=T->lchild;//key小于當(dāng)前值,如果key存在,在左子樹上
		}
		else{
		   T=T->rchild;//key大于當(dāng)前值,如果key存在,在右子樹上
		}
    }
    return T;
}

上面的算法是用一個(gè)while循環(huán),來找到目標(biāo)結(jié)點(diǎn)(非遞歸)

下面的算法則是由于二叉排序樹的遞歸特性,我們用遞歸來實(shí)現(xiàn)(遞歸)

//在二叉排序樹中查找值為key的結(jié)點(diǎn)(遞歸實(shí)現(xiàn))
BSTNode *BSTSearch(BSTree T,int key){
   if(T==NULL){
	 return NULL;//查找失敗
   }
   if(key==T->key){
	 return T;//查找成功
   }
   else if(key < T->key){
	 return BSTSearch(T->lchild,key);//在左子樹查找
   }
   else{
     return BSTSearch(T->rchild,key);//在右子樹查找
   }
}

如果采用非遞歸
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
顯然,如果采用非遞歸算法,我們只需要常數(shù)級的輔助空間。非遞歸實(shí)現(xiàn)空間復(fù)雜度為O(1)

但是如果用遞歸實(shí)現(xiàn)的話,一棵樹有多高,它就有可能要往下遞歸幾次。而每次遞歸都需要在函數(shù)調(diào)用棧中分配一片空間。遞歸實(shí)現(xiàn)空間復(fù)雜度為O(h)

3.1.3插入操作

對于插入操作,插入新結(jié)點(diǎn)后,我們也需要保證二叉排序樹的一個(gè)特性,所以我們首先也需要用到查找操作的那種邏輯找到我們應(yīng)該插入的具體位置。

比如我們現(xiàn)在要插入12這個(gè)數(shù)據(jù)

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
12<19,應(yīng)該插到12左子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
12<13,應(yīng)該插到13的左子樹

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
12>11,應(yīng)該插到11的右子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
需要注意,如果我們要插入的這個(gè)關(guān)鍵字本來就存在了,那么不該再插入值重復(fù)的結(jié)點(diǎn)了。另外,每次插入的新結(jié)點(diǎn)必定會(huì)成為一個(gè)葉子結(jié)點(diǎn)。

我們這里采用的是遞歸實(shí)現(xiàn),所以這種算法的最壞空間復(fù)雜度為O(h),h為樹的高度

顯然,插入操作也可以用循環(huán)的方式來實(shí)現(xiàn),也就是用非遞歸的方式實(shí)現(xiàn),非遞歸算法空間復(fù)雜度會(huì)更低一些。這個(gè)大家自己去練習(xí)

3.1.4二叉排序樹的構(gòu)造

其實(shí)就是不斷插入結(jié)點(diǎn)的過程,插入結(jié)點(diǎn)你調(diào)用剛才介紹的插入函數(shù)就行了

//按照str[]中的關(guān)鍵字序列建立二叉排序樹
void Create_BST(BSTree &T,int str[],int n){
    T=NULL;//初始時(shí)T為空樹
    int i=0;
    while(i<n){//依次將每個(gè)關(guān)鍵字插入到二叉排序樹中
	    BST_Insert(T,str[i]);
	    i++;
	}
}

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

同樣的數(shù)據(jù),如果我們把插入順序換一下,也會(huì)得到不同的二叉排序樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
這也是一個(gè)??嫉膬?nèi)容,就是給你一個(gè)二叉排序樹序列,讓你構(gòu)造一個(gè)二叉排序樹。

3.1.5二叉排序樹的刪除

也就是要?jiǎng)h除某個(gè)指定值的結(jié)點(diǎn),那第一步肯定是要找到那個(gè)要?jiǎng)h除的結(jié)點(diǎn)。
接下來要分下面幾種情況:

1.要?jiǎng)h除的結(jié)點(diǎn)是葉子結(jié)點(diǎn)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
刪除葉子結(jié)點(diǎn)后依然可以保證二叉排序樹的特性,你直接刪就行了

2.要?jiǎng)h除的結(jié)點(diǎn)只有左子樹或者右子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

比如13這個(gè)結(jié)點(diǎn),它只有左子樹,那你把13刪了之后,
把原先13的左孩子接到原先13的位置就行了。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
3.要?jiǎng)h除的結(jié)點(diǎn)有左子樹,有右子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
比如50這個(gè)結(jié)點(diǎn)

你要?jiǎng)h50,還得保證刪除50后的二叉排序樹還是一棵二叉排序樹

法1:從要?jiǎng)h除的結(jié)點(diǎn)的右子樹中,找出一個(gè)最小的結(jié)點(diǎn)——右子樹中最左邊的結(jié)點(diǎn)

所以,如果要?jiǎng)h50,我們可以把50右子樹中的最小結(jié)點(diǎn)60換上來,如下圖:
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

法2:從要?jiǎng)h除的結(jié)點(diǎn)的左子樹中,找出一個(gè)最大的結(jié)點(diǎn)——左子樹中最右邊的結(jié)點(diǎn)

所以,要?jiǎng)h50,我們可以讓50左子樹中最大結(jié)點(diǎn)30換上來
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

3.1.6查找效率分析

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

3.1.7小結(jié)

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

3.2平衡二叉樹的插入

3.2.1引子

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

我們在上小節(jié)說過,如果要讓二叉排序樹保持平衡,就可以保證這棵二叉排序樹的查找效率可以達(dá)到O(Log2n)這個(gè)數(shù)量級

那我們下面要研究的就是,如何在插入一個(gè)新結(jié)點(diǎn)后保持二叉樹的平衡?

如下圖,我們插入67這個(gè)結(jié)點(diǎn)后,這個(gè)新插入結(jié)點(diǎn)的所有祖先的平衡因子都受到了影響:
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

要讓新插入結(jié)點(diǎn)后的二叉樹恢復(fù)平衡的方法:我們從新插入的結(jié)點(diǎn)往上找,找到第一個(gè)不平衡結(jié)點(diǎn)。在上圖中就是70這個(gè)結(jié)點(diǎn)。

我們把以70為根的這棵子樹稱為最小的不平衡子樹
為什么稱為最?。恳?yàn)槟闳绻麖?0繼續(xù)往上找,66為根的子樹也是不平衡的,但是70為根的樹是60為根的樹的子樹,所以70為根的子樹是最小的不平衡子樹。

所以,我們只要調(diào)整最小的不平衡子樹就可以了。調(diào)整效果如下:
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

下面我們就是討論如何調(diào)整最小的不平衡子樹,讓它恢復(fù)平衡
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

3.2.2LL

在A結(jié)點(diǎn)的左孩子的左子樹中插入了一個(gè)新結(jié)點(diǎn)導(dǎo)致不平衡

我們用一些方形的框來抽象的表示各個(gè)部分子樹,H表示子樹的高度
(當(dāng)前各個(gè)子樹是平衡的)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
現(xiàn)在在B結(jié)點(diǎn)的左子樹插入了一個(gè)新結(jié)點(diǎn),導(dǎo)致了A結(jié)點(diǎn)不平衡。其實(shí)就是因?yàn)锽結(jié)點(diǎn)的左子樹長高了。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
原先A左子樹高度為H+1,現(xiàn)在左子樹長高1,那么左子樹高度變?yōu)镠+2

但是A右子樹高度還是H,則A平衡因子變成了(H+2)-(H)=2,也就是A不平衡了

可能有同學(xué)會(huì)問:“你憑啥就認(rèn)為A右子樹高度是H,我認(rèn)為A右子樹高度為H+1,一開始A這個(gè)樹不也是平衡的嗎?”

解釋如下:
如果A右子樹高度為H+1,那么A原本平衡因子應(yīng)該是0,你左邊是H+1,右邊也是H+1嘛。然后你
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
你要是設(shè)一開始A右子樹高度是H+1。那么你A左子樹左孩子高度+1,A左子樹高度其實(shí)就變成了H+2。A的平衡因子是(H+2)-(H+1)=1,其實(shí)這還是平衡的。

但是我們討論的是加了結(jié)點(diǎn)后不平衡的情況,所以這種情況不考慮。

有同學(xué)又要問了:“A右子樹高度是H+1不可以,A右子樹高度為H-1可以不”

這其實(shí)也是不可以的,因?yàn)槟阕笞訕涓叨仁荋+1,你右子樹高度是H-1,那么一開始就是不平衡的。

我們要求的是一開始平衡,加了個(gè)結(jié)點(diǎn)之后不平衡。所以這種情況也不可以。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

有同學(xué)又要問了:“那我一開始設(shè)B的右子樹高度為H-1可以不?”
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

如果是這樣的話,那么B一開始的平衡因子是(H)-(H-1)=1,

你在B左子樹上高度+1,那么B左子樹高度就變成了H+1,B右子樹高度還是H-1,B的平衡因子就變成了(H+1)-(H-1)=2,那么B就成了最小不平衡子樹了。

我們要探討的是A是最小不平衡子樹,所以B的右子樹高度也只能是H

下面回到主線劇情,如何調(diào)整這個(gè)最小的不平衡子樹
那調(diào)整后我們要得到的樹需要滿足:1.恢復(fù)平衡,2仍然是一棵二叉排序樹

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
顯然,B左子樹的值<B<B右子樹的值<A<A右子樹的值
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
那我們具體做法,就是讓B結(jié)點(diǎn)右上旋轉(zhuǎn),代替A成為根結(jié)點(diǎn),
然后A結(jié)點(diǎn)要成為B的右子樹的根節(jié)點(diǎn),也就是A要成為B的右孩子
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
然后BL<B,所以只能把BL放B左邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
BR原先在B右邊的,但是你B右邊已經(jīng)掛了A了,所以BL不能掛B右邊了
BL原先是在A左邊,也就是BL<A,我們把BL放A左邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
AR>A,然后以前的AR繼續(xù)掛A右邊就行了
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

這樣我們就可以讓加入結(jié)點(diǎn)后不平衡的的子樹繼續(xù)平衡,且保證二叉排序樹的特性。

LL小結(jié):在A左孩子的左子樹插入一個(gè)新結(jié)點(diǎn)導(dǎo)致不平衡,我們采用A左孩子右旋一次

3.2.3RR

如果在A的右孩子的右子樹加入一個(gè)結(jié)點(diǎn)導(dǎo)致不平衡
(我們這里也是假設(shè)了在進(jìn)行加結(jié)點(diǎn)操作后,A是最小的不平衡子樹,所以AL=BL=BR=H)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
對于這種情況,我們采用的是左旋一次
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
也就是讓B左上旋轉(zhuǎn),讓A左下旋轉(zhuǎn)
B變成根,A變成B左孩子
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
然后就是把剩余的AL,BL,BR掛上去
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
BR是大于B的,我們放B右邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
AL是小于A的,我們放A左邊即可
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
還剩一個(gè)BL,BL小于B但是大于A,我們就放A右邊就行
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
RR小結(jié):在A右孩子的右子樹插入一個(gè)新結(jié)點(diǎn)導(dǎo)致不平衡,我們采用A右孩子左旋一次

LL和RR的代碼思路
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

3.2.4LR

LR就是在A結(jié)點(diǎn)的左孩子的右子樹插入一個(gè)結(jié)點(diǎn)導(dǎo)致A結(jié)點(diǎn)變成最小不平衡子樹的根節(jié)點(diǎn)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
為了方便討論,我們需要把BR進(jìn)行展開。我們假設(shè)B的右孩子是叫C的結(jié)點(diǎn),C的左子樹稱為CL,右子樹稱為CR。由于B右子樹整個(gè)高度是H,所以CL和CR高度我們可以假設(shè)H-1

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
那么現(xiàn)在C這個(gè)子樹長高了,我們可以假設(shè)把新結(jié)點(diǎn)插入在CR,導(dǎo)致CR高度為H
你假設(shè)插在CL也是一樣的,處理方法都一樣
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

對于這種情況,我們要先讓C左旋頂替B的位置;接下來再讓C右旋頂替A的位置

如何把結(jié)點(diǎn)左旋/右旋,方法和前面講LL和RR是一樣的

首先是讓C左旋替代B,B<C,那么B成為C的左孩子
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
然后由于BL<B,所以BL在B左邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
CR>C,就放C右邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
B<CL<C,就放B右邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
所以,把C左旋后如下圖,但是此時(shí)A仍然不平衡
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
接下來就是把C右旋替換A的位置
就是把C右孩子變成A左孩子,
A變成C右孩子
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
LR小結(jié):在A左孩子的右子樹插入一個(gè)新結(jié)點(diǎn)導(dǎo)致不平衡,我們采用A左孩子的右孩子左旋一次,再右旋一次

3.2.5RL

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
最后RL思路同理LR
RL小結(jié):在A右孩子的左子樹插入一個(gè)新結(jié)點(diǎn)導(dǎo)致不平衡,我們采用A右孩子的左孩子右旋一次,再左旋一次

3.2.5小結(jié)

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

3.3平衡二叉樹的刪除

平衡二叉樹的第一個(gè)特性就是具有排序樹的特性,也就是左<中<右

第二個(gè)特性就是:在二叉排序的基礎(chǔ)上,要保證每個(gè)結(jié)點(diǎn)都是平衡的,也就是左右子樹高度之差不超過1

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
在上一節(jié)介紹二叉排序樹插入一個(gè)結(jié)點(diǎn)導(dǎo)致不平衡的四種情況如何解決。
該小節(jié)則是介紹二叉排序樹刪除一個(gè)結(jié)點(diǎn)導(dǎo)致不平衡的四種情況如何解決。
這四種情況和上節(jié)對應(yīng),依然是LL,RR,LR,RL

先來看一下刪除的規(guī)則,具體怎么用我們下面會(huì)舉例給大家詳細(xì)講解:
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
對于5.不平衡向上傳導(dǎo),如果出現(xiàn)這種情況那么題目給的樹就一定非常復(fù)雜,并且會(huì)出現(xiàn)多種解決方案,解題過于繁瑣,所以考試中百分之99是不會(huì)考的,我們這里只介紹一些較為簡單的例子

例1:現(xiàn)在刪除下面樹的9
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
單純刪除結(jié)點(diǎn)操作和二叉排序樹是一樣的,由于9這個(gè)階段是葉子結(jié)點(diǎn),我們直接刪除就可以了。

接下來要判斷,刪除這個(gè)元素之后,它上面的祖先結(jié)點(diǎn)有沒有出現(xiàn)不平衡的現(xiàn)象。
所以下面要做的就是“一路向北”,一路向上看是否有不平衡子樹出現(xiàn),如果沒有不平衡子樹,那么就可以結(jié)束算法了。

刪除9之后,對于13,平衡因子是-1,仍然平衡
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
對于25,平衡因子是1,仍然平衡

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
對于50,平衡因子是-1,仍然平衡

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
這就說明,剛才的刪除操作并沒有導(dǎo)致它的任何一個(gè)祖先出現(xiàn)不平衡現(xiàn)象,到此為止,刪除操作結(jié)束。

例2:現(xiàn)在刪除下面樹的55

55這個(gè)結(jié)點(diǎn)是二叉排序樹的葉子,我們可以直接刪掉

接下來就是要看55的祖先有沒有出現(xiàn)不平衡的現(xiàn)象

那么我們可以發(fā)現(xiàn),55刪了之后,75是出現(xiàn)了不平衡現(xiàn)象,平衡因子-2

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
由于產(chǎn)生了不平衡現(xiàn)象,我們就要進(jìn)入第三步,找到最小不平衡子樹下,個(gè)頭最高的兒子、孫子

75有兩個(gè)兒子,60高度為1,80高度為3,所以最高的兒子是80

再從最高的兒子往下找最高的孫子,77高度為1,90高度為2,所以最高的孫子是90

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
接下來進(jìn)入第四步,根據(jù)孫子位置,調(diào)整平衡

顯然,孫子是在爺爺RR的位置,也就是90在75RR的位置

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
當(dāng)孫子在RR的位置,我們就是要讓兒子左旋一次
左旋右旋在上節(jié)平衡二叉樹講過了,忘記了自己去回顧一下

下面就是讓80左旋到75的位置
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

接下來第五步需要檢查不平衡是否向上傳導(dǎo)
考試基本不可能出現(xiàn)向上傳導(dǎo)的情況,我們這里是為了介紹,防止大家有疑問。

什么叫檢查不平衡向上傳導(dǎo)?在調(diào)整平衡前,這個(gè)不平衡的子樹高度是4
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
調(diào)整后原先那棵不平衡子樹高度從4變成了3
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
而這棵子樹高度出現(xiàn)變化,就有可能導(dǎo)致它上面的樹也產(chǎn)生不平衡。這就是所謂的不平衡向上傳導(dǎo)。
顯然這里是沒有出現(xiàn)不平衡向上傳導(dǎo)的。

例3:現(xiàn)在刪除下面樹的32
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

首先根據(jù)二叉排序樹的刪除方法進(jìn)行操作,32是葉子結(jié)點(diǎn)我們可以直接刪除

刪除32之后,可以發(fā)現(xiàn)44是出現(xiàn)了不平衡現(xiàn)象
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
找到了最小不平衡子樹,接下來就是找最高的兒子和孫子
可以確定78是最高的兒子,50是最高的孫子
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
然后50是44的RL,那我們就讓50右旋再左旋
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

發(fā)現(xiàn)沒有出現(xiàn)不平衡向上傳導(dǎo),刪除操作結(jié)束

(基本不可能考不平衡向上傳導(dǎo),如果你感興趣可以自己去研究,會(huì)發(fā)現(xiàn)如果出現(xiàn)不平衡向上傳導(dǎo),那個(gè)樹將非常非常復(fù)雜,并且答案有很多種。我這里就介紹考試會(huì)考的幾個(gè)例子)

當(dāng)然會(huì)有人好奇,出現(xiàn)不平衡向上傳導(dǎo)的例子,就比如下面的樹,要?jiǎng)h除32
有時(shí)間,感興趣自己研究
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

四、B樹和B+樹

4.1B樹

考試中對B樹的考察一般側(cè)重考性質(zhì)、插入、刪除、查找操作,代碼基本不會(huì)出現(xiàn)。

所以我們下面將重點(diǎn)介紹B樹的性質(zhì)還有手算方法。

先來回顧一下我們前面學(xué)的二叉查找樹,也叫二叉排序樹的特點(diǎn)。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果我們要查的目標(biāo)比當(dāng)前結(jié)點(diǎn)值更小,我們會(huì)往左子樹這邊查找,如果比當(dāng)前結(jié)點(diǎn)更大,就往右子樹查找。

所以二叉查找樹無非就是用一個(gè)關(guān)鍵字,把一個(gè)我們有可能搜索到的目標(biāo)關(guān)鍵字范圍分割成兩份。

比如剛開始我們要搜索的范圍是(-∞,∞),
那么根結(jié)點(diǎn)29就把(-∞,∞)劃分為了(-∞,29)和(29,∞)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
(-∞,29)就去左子樹找,(29,∞)就去右子樹找

問題來了:能否把二叉查找樹拓展成m叉查找樹?

舉個(gè)例子:現(xiàn)在有圖示的5叉查找樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
其實(shí)我們雖然把它拓展成了5叉查找樹,但是原理和2叉查找樹一樣的。

第一個(gè)根節(jié)點(diǎn)是22
如果當(dāng)前要查找的元素比22小,就往左走
如果當(dāng)前要查找的元素比22大,就往右走

假設(shè)我們要找的那個(gè)值比22更大,接下來就應(yīng)該在22的右子樹去找,即(29,∞)里面找
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
往下找發(fā)現(xiàn)當(dāng)前結(jié)點(diǎn)又出現(xiàn)了兩個(gè)關(guān)鍵字:36和45,
就相當(dāng)于我們在這個(gè)區(qū)間內(nèi)又插入了兩個(gè)隔板。又把(29,∞)劃分為3個(gè)區(qū)間
(29,36)、(36,45)、(45,∞)

綜上,5叉排序樹里面這些關(guān)鍵字還有關(guān)鍵字之間的指針,信息其實(shí)和二叉查找樹是非常類似的。數(shù)據(jù)結(jié)構(gòu)定義如下:
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
每個(gè)結(jié)點(diǎn)最多可以包含4個(gè)關(guān)鍵字,最多5個(gè)孩子,也就是5棵子樹。然后用一個(gè)num變量記錄當(dāng)前結(jié)點(diǎn)一共多少個(gè)關(guān)鍵字。

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
像上圖這種5叉查找樹,在一個(gè)結(jié)點(diǎn)中,我們最少可以允許有1個(gè)關(guān)鍵字,2個(gè)分叉;最多允許4個(gè)關(guān)鍵字,5個(gè)分叉
一個(gè)關(guān)鍵字會(huì)把一個(gè)查找區(qū)間分割成兩個(gè)部分

圖中給出的紫色結(jié)點(diǎn),其是就查找失敗的情況,比如下面圈出的失敗結(jié)點(diǎn),就是對應(yīng)的(15,22)這個(gè)范圍
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

下面我們進(jìn)行5叉查找樹的查找成功的舉例:

假設(shè)我們現(xiàn)在要查找9
根節(jié)點(diǎn)中第一個(gè)被保存的關(guān)鍵字是22
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
顯然9要比22更小,所以下面要選22左邊的路
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

到新的結(jié)點(diǎn),我們會(huì)順序掃描一個(gè)結(jié)點(diǎn)中的各個(gè)關(guān)鍵字
第一個(gè)關(guān)鍵字5要比9更小,往右檢查下一個(gè)關(guān)鍵字
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第二個(gè)關(guān)鍵字11又要比9大,所以9在11左邊的路線上
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
到了新的結(jié)點(diǎn),我們還是一樣的從左往右依次掃描,然后掃描3次找到9
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
ps:剛才在查找每個(gè)結(jié)點(diǎn)中的數(shù)據(jù)時(shí),是用的順序查找。
但是由于每個(gè)結(jié)點(diǎn)中數(shù)據(jù)是有序排放的,所以在查找每個(gè)階段數(shù)據(jù)時(shí),你也可以用折半查找

下面我們進(jìn)行5叉查找樹的查找失敗的舉例:

比如我們要找41,現(xiàn)在從根節(jié)點(diǎn)出發(fā)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

22要比41更小,我們指針右移,發(fā)現(xiàn)當(dāng)前指針?biāo)肝恢靡呀?jīng)超出這個(gè)結(jié)點(diǎn)關(guān)鍵字個(gè)數(shù)了,所以如果41存在,我們應(yīng)該往回找這個(gè)指針,然后指針往下走

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
來到下一層結(jié)點(diǎn),第一個(gè)元素36<41,指針往右

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第二個(gè)元素45>41,如果41存在,則在45左邊的指針?biāo)附Y(jié)點(diǎn)中
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
到新的結(jié)點(diǎn),第一個(gè)元素40<41,指針右移
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第二個(gè)元素42要比41更大,如果41存在,則在42左邊指針指向的結(jié)點(diǎn)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
但是你發(fā)現(xiàn),如果沿著42左邊指針往下走,居然是一個(gè)失敗結(jié)點(diǎn),那么就說明查找失敗
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
ps:雖說是失敗結(jié)點(diǎn),其實(shí)就是一個(gè)NULL

剛才我們提出的5叉排序樹,我們只是規(guī)定了每一個(gè)結(jié)點(diǎn)中最多有5個(gè)分叉。

那么如果我們每個(gè)結(jié)點(diǎn)只保留一個(gè)關(guān)鍵字,也就是每個(gè)結(jié)點(diǎn)只有2個(gè)分叉的情況。

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如上圖所示,這個(gè)5叉查找樹就退化成了2叉查找樹。
在這種情況下,由于每個(gè)結(jié)點(diǎn)的關(guān)鍵字?jǐn)?shù)量變少,
所以,如果關(guān)鍵字總數(shù)相同,這個(gè)樹會(huì)變成“細(xì)狗”,也就是又細(xì)又高的樹。
而查找樹越高,我們在進(jìn)行查找時(shí)就需要更多層的結(jié)點(diǎn),效率也會(huì)更低

那么如何保證5叉查找樹的效率?
我們可以規(guī)定:在m叉查找樹中,除了根節(jié)點(diǎn),其他任何一個(gè)結(jié)點(diǎn)都至少有?m/2?個(gè)分叉

比如5叉查找樹,m/2向上取整應(yīng)該是?5/2?=3
所以,對于5叉查找樹,我們可以規(guī)定每個(gè)階段至少3個(gè)分叉,也就是2個(gè)關(guān)鍵字
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

這樣就可以保證每個(gè)階段中關(guān)鍵字個(gè)數(shù)不會(huì)太少,就可以保證這棵樹不會(huì)變的很高,層數(shù)不會(huì)太多,相應(yīng)的查找效率也可以得到保證

這里可能大家會(huì)有疑惑,為什么除了根結(jié)點(diǎn)之外呢?我們讓根結(jié)點(diǎn)也有3個(gè)分叉不行?

想法不錯(cuò),但是實(shí)際是做不到的。
比如現(xiàn)在5叉查找樹,剛開始只有1個(gè)元素,1個(gè)關(guān)鍵字。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
那么這種情況下,就只有一個(gè)根節(jié)點(diǎn)(一個(gè)關(guān)鍵字)
就一個(gè)關(guān)鍵字,你那里來能放2個(gè)關(guān)鍵字呢?

再來看另一個(gè)問題:下面這棵二叉樹,你覺得它優(yōu)秀不?
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
上面這個(gè)5叉查找樹已經(jīng)滿足了我們剛才提出的特性:根節(jié)點(diǎn)除外,每個(gè)結(jié)點(diǎn)有3個(gè)或3個(gè)以上的分叉。

但是這棵樹有一個(gè)問題:它不平衡
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
這種不平衡的特性,顯然也會(huì)導(dǎo)致我們的5叉查找樹長的很高。

和剛才一樣,如果樹長得太高,就會(huì)導(dǎo)致我們查找的過程中要查很多層的結(jié)點(diǎn),從而導(dǎo)致效率降低。

我們可以規(guī)定:對于每個(gè)結(jié)點(diǎn),它的左右子樹高度都相同

如果能做到各個(gè)結(jié)點(diǎn)的子樹都沒有高度差,就可以保證我們的多叉查找樹是平衡的,也就保證了它不會(huì)有太多層,從而保證查找效率。

如果能滿足上面提到的兩個(gè)策略,那么就是B樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
我們通常會(huì)把失敗結(jié)點(diǎn)稱為葉子結(jié)點(diǎn),而最下面一層含有實(shí)際數(shù)據(jù)的結(jié)點(diǎn),我們稱為終端結(jié)點(diǎn)。

注:由于平衡要求,我們是讓樹中所有結(jié)點(diǎn)子樹高度都相同。那么就會(huì)導(dǎo)致,失敗結(jié)點(diǎn)(葉子結(jié)點(diǎn))一定會(huì)出現(xiàn)在最下層(同一層)

B樹中所有結(jié)點(diǎn)孩子個(gè)數(shù)的最大值稱為B數(shù)的階,也就是這個(gè)B樹你最多看到多少個(gè)分叉
如下圖,最多是有5個(gè)分叉。
所以,所謂的5階B樹其實(shí)就是一個(gè)5叉查找樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

然后,根節(jié)點(diǎn)如果不是終端結(jié)點(diǎn),則至少兩個(gè)子樹。
這個(gè)特性就是保證每個(gè)結(jié)點(diǎn)都平衡,所以對于根節(jié)點(diǎn)來說,如果這個(gè)根節(jié)點(diǎn)不是終端結(jié)點(diǎn),那么它必須是要有兩棵子樹,不然它自己不平衡啊。

還有一個(gè)特性,就是所有非葉子結(jié)點(diǎn)的結(jié)構(gòu)如下:其實(shí)就是我們剛才給出的那個(gè)數(shù)據(jù)結(jié)構(gòu)的定義。

如下圖:
p0,p1,p2…pn指的是結(jié)點(diǎn)當(dāng)中的一個(gè)個(gè)指針,總共有n+1個(gè)指針
而k1,k2,…kn指的是結(jié)點(diǎn)的關(guān)鍵字,總共是n個(gè)關(guān)鍵字
n記錄實(shí)際關(guān)鍵字是幾個(gè)

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
pi-1的所指結(jié)點(diǎn)的所有關(guān)鍵字<ki<pi的所指結(jié)點(diǎn)的所有關(guān)鍵字
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

下面是對B樹特性的總結(jié):
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

下面我們根據(jù)B樹的特性來看一下B樹有多高
注:大多數(shù)學(xué)校在計(jì)算B樹高度時(shí),都是不包括最下面的失敗結(jié)點(diǎn)的,因?yàn)槭〗Y(jié)點(diǎn)本質(zhì)就是一個(gè)NULL

先來看一下最小高度是多少

如果要讓樹高最小,那么在關(guān)鍵字?jǐn)?shù)量不變的情況下,我們應(yīng)該盡可能讓每一個(gè)結(jié)點(diǎn)都填滿關(guān)鍵字。

那么對于m階B樹,每個(gè)結(jié)點(diǎn)最多有m-1個(gè)關(guān)鍵字,有m個(gè)分叉。
最上層的根節(jié)點(diǎn)只有一個(gè)(注意,我這里說的是結(jié)點(diǎn)數(shù)量是1,不是關(guān)鍵字?jǐn)?shù)量是1),根結(jié)點(diǎn)有m個(gè)分叉。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

由于第一層下來m個(gè)分叉,所以第二層有m個(gè)結(jié)點(diǎn)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第三層,又會(huì)由第二層m個(gè)結(jié)點(diǎn)下來m2個(gè)結(jié)點(diǎn)(每個(gè)結(jié)點(diǎn)m個(gè)分叉)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果有h層,那么就應(yīng)該是mh-1
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
也就是有h層,有(1+m+…+mh-1)個(gè)結(jié)點(diǎn)

而每個(gè)結(jié)點(diǎn)有m-1個(gè)關(guān)鍵字,所以共(m-1)(1+m+…+mh-1)個(gè)關(guān)鍵字

那么n個(gè)關(guān)鍵字的B樹高度為h,那么n的范圍肯定是小于等于我們剛才給出的數(shù)值

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
下面來看一下最大高度是多少
要讓一棵樹長的盡可能的高,就要讓這顆數(shù)的分叉盡可能的少。
對于根節(jié)點(diǎn)最少2個(gè)分叉,對于其他結(jié)點(diǎn)則是最少?m/2?個(gè)分叉

對于m叉樹來說:
在分叉最少的情況下,第一層只有1個(gè)根節(jié)點(diǎn),2個(gè)分叉
第二層有2個(gè)結(jié)點(diǎn),每個(gè)結(jié)點(diǎn)?m/2?個(gè)分叉,也就是2[m/2]個(gè)分叉
第三層有2[m/2]個(gè)結(jié)點(diǎn),每個(gè)結(jié)點(diǎn)?m/2?個(gè)分叉,也就是2[m/2]2個(gè)分叉

第h層2[m/2]h-2個(gè)結(jié)點(diǎn),每個(gè)結(jié)點(diǎn)?m/2?個(gè)分叉,也就是2[m/2]h-1個(gè)分叉

再往下推,葉子結(jié)點(diǎn)(失敗結(jié)點(diǎn))也就是h+1層,至少有2[m/2]h-1個(gè)結(jié)點(diǎn)

補(bǔ)充重要特性:對于n個(gè)關(guān)鍵字的B樹,必然有n+1個(gè)葉子結(jié)點(diǎn)
解釋:B樹中n個(gè)關(guān)鍵字,相當(dāng)于是在(-∞,+∞)這個(gè)區(qū)間內(nèi)插入n個(gè)關(guān)鍵字,這n個(gè)關(guān)鍵字會(huì)把整個(gè)數(shù)值區(qū)間切分為n+1個(gè)部分。

這n+1個(gè)部分就對應(yīng)了n+1種失敗的情況。

所以,n個(gè)關(guān)鍵字的B樹必有n+1個(gè)葉子結(jié)點(diǎn),而m階B樹的葉子結(jié)點(diǎn)下限應(yīng)該是2[m/2]h-1
n+1>=2[m/2]h-1
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
小貼士:關(guān)于為什么B樹叫B樹,B樹這個(gè)數(shù)據(jù)結(jié)構(gòu)的發(fā)明者也沒有說明,個(gè)人覺得是balance,平衡的意思。

4.2B樹的插入刪除

4.2.1插入

下面我們從零開始建立一棵B樹,
并且規(guī)定我們這個(gè)B樹是5階B樹,也就是關(guān)鍵字個(gè)數(shù)最少不能少于2個(gè),最多不能多于4個(gè)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

我們插入第一個(gè)元素25,直接放入根節(jié)點(diǎn)中
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
插38,38>25,放25后面即可
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
插49,49>38,放38后面
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
插60,60>49,放49后面
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
到目前為止,根節(jié)點(diǎn)可以存放的結(jié)點(diǎn)個(gè)數(shù)達(dá)到上限
接下來如果繼續(xù)往里面插入一個(gè)元素,比如80,此時(shí)就導(dǎo)致根節(jié)點(diǎn)中關(guān)鍵字的個(gè)數(shù)超過4
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
對于這種情況,需要把當(dāng)前這個(gè)結(jié)點(diǎn)分裂成兩個(gè)結(jié)點(diǎn)。

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
我們會(huì)以中間位置,也就是?m/2? 位置的關(guān)鍵字,把原有的結(jié)點(diǎn)拆分為左右兩部分,然后?m/2? 位置的關(guān)鍵字提到父節(jié)點(diǎn)中。

該例子中也就是第三個(gè)元素49提到父節(jié)點(diǎn)位置
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

接下來插入90這個(gè)元素
注意:我們每次新插入的元素一定是插入到最底層的終端結(jié)點(diǎn)。
可以用上小節(jié)介紹的查找規(guī)則來確定我們應(yīng)該插入到什么位置。

從根節(jié)點(diǎn)出發(fā),90大于49,而49右邊已經(jīng)沒有元素了,所以順著49右邊指針往下找
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

接下來檢查新結(jié)點(diǎn)關(guān)鍵字,60<90,80<90,所以90應(yīng)該插到80右邊
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
所以,我們其實(shí)是通過一次查找來確定我們新元素應(yīng)該插到什么位置。
再次強(qiáng)調(diào):每次插入的新元素一定是插到最底層的終端結(jié)點(diǎn)中!

接下來再插99
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
接下來插88,我們用肉眼掃一下可以發(fā)現(xiàn),88應(yīng)該插在80和90之間
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
到這里又會(huì)導(dǎo)致當(dāng)前這個(gè)結(jié)點(diǎn)關(guān)鍵字個(gè)數(shù)又超出上限了
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
處理方法和剛才一樣,用?m/2? 位置的關(guān)鍵字,這里是?5/2? =3位置的關(guān)鍵字提到父節(jié)點(diǎn),
然后把3位置的關(guān)鍵字左右兩邊元素分別放到其左右子樹上。具體做法如下:

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
ps:這么做可以保證二叉樹的特性,也就是一個(gè)階段的左子樹結(jié)點(diǎn)值<當(dāng)前結(jié)點(diǎn)值<右子樹結(jié)點(diǎn)值

再接下來插入83,插入87,沒啥好說的,直接插終端結(jié)點(diǎn)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
再接下來插入70,肉眼掃一下應(yīng)該插80和83中間,那么又會(huì)溢出,該結(jié)點(diǎn)又需要分裂

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
把中間元素80提到父節(jié)點(diǎn),兩邊元素放左右子樹
而這里中間元素80>49,80<88,所以我們把80放49和88之間
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果一個(gè)關(guān)鍵字,它因?yàn)樾枰至讯岬礁腹?jié)點(diǎn)中,我們需要把這個(gè)關(guān)鍵字放到它所屬結(jié)點(diǎn)這條指針的右邊

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
這樣做可以保證我們的樹仍然保持B樹的特性
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
接下來再插入4個(gè)新元素92,93,94,99
那么又有結(jié)點(diǎn)需要分裂了
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
還是一樣的辦法,把中間的93提到父節(jié)點(diǎn)中88的右邊
(把93放到指向當(dāng)前結(jié)點(diǎn)這個(gè)指針對應(yīng)點(diǎn)的右邊,也就是88的右邊)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
然后93左右成為左子樹和右子樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果再往樹里面插入73,74,75
那么結(jié)點(diǎn)又需要分裂了,我們需要把73提到指向該結(jié)點(diǎn)指針右邊的位置
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
但是這里我們會(huì)發(fā)現(xiàn),73提上去之后,根節(jié)點(diǎn)又不夠用了

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

這種情況,我們就需要把這個(gè)父節(jié)點(diǎn)繼續(xù)向上分裂,由于根節(jié)點(diǎn)上面已經(jīng)沒有父節(jié)點(diǎn)了,所以我們創(chuàng)一個(gè)新結(jié)點(diǎn)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
這里根節(jié)點(diǎn)是可以只有一個(gè)關(guān)鍵字的,其他結(jié)點(diǎn)關(guān)鍵字個(gè)數(shù)n必須滿足2<=n<=4

下面是B樹插入的總結(jié)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

4.2.2刪除

比如我們現(xiàn)在要?jiǎng)hB樹的60
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
由于60是在終端結(jié)點(diǎn)中,我們直接刪了,然后把60所在結(jié)點(diǎn)的其他關(guān)鍵字左移一下
需要注意的是,你刪了一個(gè)結(jié)點(diǎn)之后,要保證這個(gè)結(jié)點(diǎn)關(guān)鍵字個(gè)數(shù)還是>=?m/2?-1

這里刪完60還剩3個(gè)關(guān)鍵字,沒有低于一個(gè)結(jié)點(diǎn)最少關(guān)鍵字個(gè)數(shù),是合法的
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

再比如,我們現(xiàn)在要?jiǎng)h80,80刪了之后根節(jié)點(diǎn)就空了。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

那我們可以找出80這個(gè)元素的直接前驅(qū)或者直接后繼來頂替80的位置

如果用直接前驅(qū)來頂替
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
80的直接前驅(qū)就是80左子樹中最大的,你就找左子樹中最右邊的元素,也就是77
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
上面的操作,就相當(dāng)于我們把非終端結(jié)點(diǎn)的刪除轉(zhuǎn)換成了終端結(jié)點(diǎn)的刪除。

如果用直接前驅(qū)來頂替
現(xiàn)在要?jiǎng)h根節(jié)點(diǎn)的77,那么我們可以找77右子樹中最小的來頂替77,也就是找右子樹中最左邊的元素82
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

前面我們探討的情況都很簡單,也就是我們刪除一個(gè)終端結(jié)點(diǎn)關(guān)鍵字時(shí),這個(gè)終端結(jié)點(diǎn)關(guān)鍵字?jǐn)?shù)量還沒有低于它的下限,那么我們下面來介紹一些低于下限的情況:

比如刪38
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
你刪了38之后,下圖所指結(jié)點(diǎn)的關(guān)鍵字個(gè)數(shù)小于B樹規(guī)定的最小值2了
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
那么我們需要分為多種情況來考慮

情況1:被刪結(jié)點(diǎn)它的兄弟可以借
什么意思?當(dāng)前結(jié)點(diǎn)關(guān)鍵字?jǐn)?shù)量不夠了,但是它的右兄弟關(guān)鍵字?jǐn)?shù)量還夠。

可能會(huì)有同學(xué)會(huì)想直接把右兄弟的一個(gè)關(guān)鍵字放到當(dāng)前結(jié)點(diǎn),但是這樣是有問題的
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
你直接把70放過去,會(huì)導(dǎo)致70>49,而我們B樹是要求左子樹值<根值<右子樹值
這樣就矛盾了。

解決辦法是先將49拉下來,然后70去頂替49的位置
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
上面的解決方案可以做一個(gè)小總結(jié):如果右兄弟的手頭寬裕,我們可以找到當(dāng)前結(jié)點(diǎn)的后繼、后繼的后繼來填補(bǔ)空缺

舉個(gè)例子,當(dāng)前這個(gè)結(jié)點(diǎn),也就是25這個(gè)元素的后繼是49
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
而25后繼的后繼,也就是49的后繼應(yīng)該是70,即49右邊指針?biāo)B結(jié)點(diǎn)的第一個(gè)元素
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
所以,這就是用當(dāng)前結(jié)點(diǎn)后繼、后繼的后繼去頂替它們所需要頂替的位置

上面例子是借右兄弟的例子,下面我們介紹借左兄弟的例子

比如現(xiàn)在要?jiǎng)h90這個(gè)關(guān)鍵字
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
而刪完90之后,發(fā)現(xiàn)右兄弟已經(jīng)不寬裕了,但是左兄弟還寬裕
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
具體做法:如果左兄弟的手頭寬裕,我們可以找到當(dāng)前結(jié)點(diǎn)的前驅(qū)、前驅(qū)的前驅(qū)來填補(bǔ)空缺

92的直接前驅(qū)是88,也就是順著指向當(dāng)前結(jié)點(diǎn)的指針左邊的元素
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

而92前驅(qū)的前驅(qū)是87,也就是88的前驅(qū),應(yīng)該是88左子樹最右邊的元素

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

我們用88和87填補(bǔ)所需位置

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

上面都是介紹了左兄弟或者右兄弟寬裕的情況,下面我們介紹左兄弟和右兄弟都不寬裕的情況
情況2:被刪結(jié)點(diǎn)它的兄弟不可以借

比如現(xiàn)在要?jiǎng)h49
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
刪完之后當(dāng)前結(jié)點(diǎn)關(guān)鍵字已經(jīng)不夠了,但是右兄弟也不寬裕了

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
我們的策略是讓當(dāng)前結(jié)點(diǎn)和它的右兄弟合并
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
合并時(shí),還需要把這兩個(gè)結(jié)點(diǎn)中間的關(guān)鍵字也一起合并,這樣才能滿足B樹特性

合并過程如下動(dòng)圖:
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
但是這里又出現(xiàn)問題了,由于我們從父節(jié)點(diǎn)拿走一個(gè)元素,父節(jié)點(diǎn)的關(guān)鍵字又不夠了
所以我們接下來繼續(xù)要合并
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
合并過程如下動(dòng)圖
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
而此時(shí),根結(jié)點(diǎn)已經(jīng)沒有任何關(guān)鍵字了,刪掉它
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

考試不會(huì)考5階以上的B樹插入刪除,上面介紹的已經(jīng)是比較難的例子,掌握好應(yīng)對考試足矣

4.2.3小結(jié)

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

4.3B+樹

對于B+樹,考研不會(huì)考很深,基本都是概念的東西
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
細(xì)心的同學(xué)可能會(huì)發(fā)現(xiàn),B+樹和我們的分塊查找比較類似
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
在分塊查中,我們會(huì)把這些元素分成一個(gè)個(gè)塊,在索引表中,我們會(huì)保存每個(gè)塊中最大的關(guān)鍵字。

再回頭看B+樹,其實(shí)也是保存了每個(gè)塊的最大關(guān)鍵字
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
然后每層索引往上,都是一樣的規(guī)律

4.3.1定義

下面是B+樹的定義
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
1.每個(gè)分支結(jié)點(diǎn)最多m棵子樹
比如這里的4階B+樹,它最多就是有4個(gè)子樹

2.非葉根節(jié)點(diǎn)至少有兩棵子樹,其他分支結(jié)點(diǎn)至少有?m/2?棵子樹
什么叫非葉根節(jié)點(diǎn)?
首先要明確的是,我們在B+樹中,把最下面一層稱為葉子結(jié)點(diǎn)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
下面三個(gè)例子中,第一個(gè)例子,只有一個(gè)根節(jié)點(diǎn),它同時(shí)也是葉子結(jié)點(diǎn)。
而第三個(gè)例子,根節(jié)點(diǎn)并不是最下面一層的結(jié)點(diǎn),也就是非葉子結(jié)點(diǎn),它至少有2棵子樹才能算是B+樹

而第二個(gè)例子,根節(jié)點(diǎn)只有左子樹沒有右子樹,不符合B+樹的定義,它不是B+樹
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
因?yàn)槲覀兿M鸅+樹高度盡可能低,所以我們會(huì)追求絕對平衡——所有子樹高度相同
所以,我們會(huì)要求非葉根節(jié)點(diǎn)至少兩棵子樹

另外,我們還要求其他每個(gè)分支結(jié)點(diǎn)至少有?m/2?棵子樹,這里m=4,也就是至少要有2棵子樹

至于為啥這樣,這個(gè)原因和B樹是一樣的,我們要保證每個(gè)結(jié)點(diǎn)關(guān)鍵字個(gè)數(shù)(結(jié)點(diǎn)個(gè)數(shù))不要過少,因?yàn)槿绻總€(gè)結(jié)點(diǎn)子樹數(shù)量太少,就會(huì)導(dǎo)致這棵B+樹長得很高,這樣查找效率就低了。

3.結(jié)點(diǎn)子樹個(gè)數(shù)和關(guān)鍵字個(gè)數(shù)相等(重要)
這一點(diǎn)比較容易在選擇題中進(jìn)行考察,因?yàn)檫@也是B+樹和B樹比較大的一點(diǎn)區(qū)別

比如下面這個(gè)結(jié)點(diǎn),對應(yīng)3個(gè)關(guān)鍵字,每個(gè)關(guān)鍵字也對應(yīng)1個(gè)分支
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
所以B+樹當(dāng)中一個(gè)結(jié)點(diǎn)里面,它含有多少個(gè)關(guān)鍵字,對應(yīng)的它就有多少個(gè)分支,有多少個(gè)子樹。

ps:B樹中,如果一個(gè)結(jié)點(diǎn)有2個(gè)關(guān)鍵字,那它是對應(yīng)3個(gè)分支

4.所有葉結(jié)點(diǎn)包含全部關(guān)鍵字及指向?qū)?yīng)記錄的指針,葉結(jié)點(diǎn)中將關(guān)鍵字按大小順序排列,并且相鄰葉結(jié)點(diǎn)按大小順序相互鏈接起來。

在B+樹中,我們可以通過指針p,從第一個(gè)葉子結(jié)點(diǎn)開始,一個(gè)個(gè)往后遍歷,把所有葉子結(jié)點(diǎn)都遍歷完。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
也就是說,B+樹是支持順序查找的

5.所有分支結(jié)點(diǎn)中僅包含它的各個(gè)子結(jié)點(diǎn)中關(guān)鍵字的最大值及指向其子節(jié)點(diǎn)的指針。
這個(gè)和分塊查找原理很類似,這里不再贅述

4.3.2B+樹的查找

先看一個(gè)查找成功的例子:
比如現(xiàn)在要查編號9的學(xué)生對應(yīng)信息

從根節(jié)點(diǎn)出發(fā),9<15,15表示的是它所指向的一整個(gè)分塊中最大的關(guān)鍵字是15

所以如果9存在,一定是在15的分塊中
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
3<9,指針后移
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
第二個(gè)正好是9,那么9是在當(dāng)前關(guān)鍵字所指結(jié)點(diǎn)中
ps:如果在B+樹查找中,我們在分支結(jié)點(diǎn)中找到我們所需關(guān)鍵字,其實(shí)整個(gè)查找并沒有結(jié)束。
你得找到最下面一層葉子結(jié)點(diǎn)才能知道相關(guān)記錄信息。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
指針繼續(xù)往下移,到葉子結(jié)點(diǎn)了

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
從左往右依次檢查,就可以在這個(gè)葉子結(jié)點(diǎn)中找到9這個(gè)關(guān)鍵字
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
那么通過9,就可以找到9號學(xué)生對應(yīng)的相關(guān)信息了

再看一個(gè)查找失敗的例子:
比如我們要查找的關(guān)鍵字是7,

那么從根結(jié)點(diǎn)出發(fā),7<15,應(yīng)該往15所指結(jié)點(diǎn)找
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
3<7,7肯定不在3所指結(jié)點(diǎn)內(nèi),指針右移
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
7<9,如果7存在,肯定在9所指結(jié)點(diǎn)內(nèi)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
檢查下一個(gè)結(jié)點(diǎn),6<7,指針右移
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
下一個(gè)元素是8,8>7
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
注意!這里已經(jīng)是最下面一層的葉子結(jié)點(diǎn)了,到這里沒找到7就說明7不存在了。

B+樹中無論是查找成功還是查找失敗,都要找到最下面一層的葉子結(jié)點(diǎn)。

除了根節(jié)點(diǎn)往下找,還可以通過葉子結(jié)點(diǎn)那里保存的指針p進(jìn)行順序查找
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

4.3.3B+樹和B樹的對比

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

4.3.4小結(jié)

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

五、散列表

5.1散列表的基本概念

5.1.1散列表、散列函數(shù)

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
我們根據(jù)散列函數(shù)可以計(jì)算出一個(gè)數(shù)據(jù)元素在散列表中的存儲地址。
比如這里散列函數(shù)是H(key)=key%13
那么如果你給我19,H(19)=19%13=6,我就可以立馬知道如果19存在,則存放在6號位置

再比如你給我16,H(16)=16%13=3,我就可以立馬知道如果16存在,則存放在3號位置
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
可以看到,在理想情況下,我們散列表查找一個(gè)元素時(shí)間復(fù)雜度只需要O(1)

5.1.2沖突、同義詞

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
我們并不希望沖突頻繁發(fā)生,沖突越少,散列表性能越高

5.1.3關(guān)于散列表,有待解決的問題

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.2散列函數(shù)的構(gòu)造

散列函數(shù)的作用是把一個(gè)關(guān)鍵字映射到與之對應(yīng)的存儲地址上。

當(dāng)我們設(shè)計(jì)一個(gè)散列函數(shù)時(shí),應(yīng)盡可能減少不同關(guān)鍵字之間發(fā)生沖突的情況。

該小節(jié)將介紹4種散列函數(shù)構(gòu)造方式:
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考試中最常考、現(xiàn)實(shí)中最常用的都是除留余數(shù)法
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.2.1除留余數(shù)法

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.2.2直接定址法

這種方法適用于關(guān)鍵字分布基本連續(xù)
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.2.3數(shù)字分析法

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.2.4平方取中法

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.2.5小結(jié)

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.3處理沖突的方法——拉鏈法

散列表通常不考代碼,著重掌握手算分析方法

散列表中沖突這種現(xiàn)象是無法避免的,可能會(huì)有多個(gè)元素映射到同一個(gè)散列地址。
這些元素在插入時(shí)就會(huì)發(fā)生沖突
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

我們第一種解決沖突的方式就是拉鏈法

我們把這些同義詞用一個(gè)鏈表連起來
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.3.1插入

現(xiàn)有如下的空散列表,長度13,散列函數(shù)H(key)=key%13,我們用拉鏈法解決沖突
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.3.2查找

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.3.3刪除

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.3.4小結(jié)

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.4處理沖突的方法——開放定址法

5.4.1基本原理

散列表中沖突是不可避免的,我們可以用拉鏈法,也可以用開放定址法

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
舉個(gè)例子,我們有散列函數(shù)H(key)=key%13,現(xiàn)在14已結(jié)占了1的位置,如果現(xiàn)在1再進(jìn)來,想占據(jù)1的位置怎么辦?

我們可以讓給1找另一個(gè)空閑位置。

所謂開放定址法,就是一個(gè)散列地址,既對同義詞開放,也對非同義詞開放。

那么問題也隨之而來,發(fā)生沖突時(shí),我們要給新元素找另一個(gè)位置,那這個(gè)位置怎么確定?

假設(shè)我們現(xiàn)在原始地址發(fā)生沖突了,我們從原始地址往右偏移1位
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果還發(fā)生沖突,就去探索一下原始位置左邊是否有沖突
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果還發(fā)生沖突,去原始地址右邊兩位看看有沒有空位
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
如果還沖突,就探索原位置左邊兩位有沒有空位。。。

也就是說,當(dāng)發(fā)生沖突時(shí),我們可以設(shè)計(jì)這樣一個(gè)序列:0,1,-1,2,-2,3,-3…
來規(guī)定發(fā)生沖突時(shí)我們探測每個(gè)位置的順序。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.4.2線性探測法

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
對于散列表的插入,就是你先根據(jù)散列函數(shù)算出一個(gè)原始地址,然后根據(jù)那個(gè)探測法去探測原始地址周圍的位置。

而對于散列表的查找,和插入基本一樣,也就先算原始地址,然后根據(jù)對應(yīng)探測法去探測。
但是對于查找有個(gè)需要注意的地方,就是如果你探測下來,探測到空位置還沒探測到,說明原先就沒有這個(gè)關(guān)鍵字。那么查找失敗。

5.4.3平方探測法

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.4.3雙散列法

對于雙散列法,需要設(shè)計(jì)第二個(gè)散列函數(shù),發(fā)生沖突時(shí),需要根據(jù)關(guān)鍵字的值去確定第二個(gè)散列函數(shù)的計(jì)算結(jié)果。

不同關(guān)鍵字的探測序列也是不同的
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.4.4偽隨機(jī)序列法

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

5.4.5刪除

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

需要注意,刪除元素時(shí)會(huì)有一個(gè)坑!以下是刪除的錯(cuò)誤示范?。。?/font>
比如我們現(xiàn)在用線性探測法,要?jiǎng)h15
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
首先應(yīng)該查找15
第一次定位15%13=2,但是2號位已經(jīng)被占了,往右1位,找到15
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
然后刪15
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

你這樣做好像沒啥問題,但是如果我們現(xiàn)在要你查找1怎么辦?
由于你之前把3號位置清空了,那么你查1的時(shí)候經(jīng)過3,系統(tǒng)會(huì)誤認(rèn)為沒有1,這樣就錯(cuò)了!
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

那該如何進(jìn)行正確的刪除呢?比如現(xiàn)在要?jiǎng)h15
我們先找到15
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
找到15之后要?jiǎng)h,但是這里的刪,不是物理上的刪,是邏輯上的刪!

我們可以給15所在位置做一個(gè)標(biāo)記,比如你設(shè)一個(gè)flag,把flag置為1,則表示已經(jīng)刪除

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
我們這里沒有物理刪除,而是邏輯刪除,這樣就可以保證線性探測法中間不會(huì)出現(xiàn)空檔,也就避免了有數(shù)但是沒找到的情況

考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

需要注意的是,如果用的是開放定址法,你刪了很多元素之后,會(huì)導(dǎo)致整個(gè)散列表看起來很滿,但是很空(邏輯刪除,物理上沒刪除)

這樣其實(shí)是會(huì)導(dǎo)致查找效率低下的
比如我們要找的元素初始地址是0,但是你一個(gè)個(gè)往右發(fā)現(xiàn)都沒有找到,最后才確定沒有這個(gè)元素,這就很浪費(fèi)時(shí)間了。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

所以,如果散列表中有很多這種邏輯刪除之后的元素,我們可以不定期的去整理散列表的數(shù)據(jù)

比如我們把元素1放到1號位置,其他位置邏輯刪除的位置全部物理刪除掉
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表

ps:你插新元素的時(shí)候,也是可以插到邏輯刪除的位置上的,就相當(dāng)于是一個(gè)數(shù)據(jù)覆蓋。
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
考研數(shù)據(jù)結(jié)構(gòu):第七章 查找,數(shù)據(jù)結(jié)構(gòu)專欄,順序查找,折半查找,分塊查找,二叉排序樹,平衡二叉樹,B樹,散列表
文章來源地址http://www.zghlxwxcb.cn/news/detail-648618.html

到了這里,關(guān)于考研數(shù)據(jù)結(jié)構(gòu):第七章 查找的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包