樹(shù)基礎(chǔ)概念

樹(shù)(tree)

首先可以想象秋柄,現(xiàn)實(shí)中的樹(shù)是由樹(shù)根缀旁、莖干、樹(shù)枝、樹(shù)葉組成的粗俱,樹(shù)的營(yíng)養(yǎng)是由樹(shù)根出發(fā)收班、通過(guò)莖干與樹(shù)枝不斷傳遞涮帘,最終到達(dá)樹(shù)葉的帝簇。
在數(shù)據(jù)結(jié)構(gòu)中,樹(shù)則是用來(lái)概括這種傳遞關(guān)系的一種數(shù)據(jù)結(jié)構(gòu)来庭。

定義

結(jié)點(diǎn)(node):樹(shù)枝分叉處妒蔚、樹(shù)葉、樹(shù)根的抽象
根節(jié)點(diǎn)(root):樹(shù)根的抽象月弛,對(duì)一棵樹(shù)來(lái)說(shuō)最多存在一個(gè)根節(jié)點(diǎn)
葉子結(jié)點(diǎn)(leaf):樹(shù)葉的抽象肴盏,且葉子結(jié)點(diǎn)不再延申出新的結(jié)點(diǎn)
邊(edge):把莖干和樹(shù)枝的統(tǒng)一抽象,且一條只用來(lái)鏈接兩個(gè)結(jié)點(diǎn)(一個(gè)端點(diǎn)一個(gè))(特指二叉樹(shù))
樹(shù)中的結(jié)點(diǎn)不能被邊連接成環(huán)帽衙。
在數(shù)據(jù)結(jié)構(gòu)中菜皂,一般把根結(jié)點(diǎn)置于最上方,然后向下延伸出若干條邊到達(dá)子結(jié)點(diǎn)(child)(從而向下形成子樹(shù)(subtree))厉萝,而子結(jié)點(diǎn)又向下延伸出邊并連接一些結(jié)點(diǎn)......直至到達(dá)葉子結(jié)點(diǎn)恍飘,看起來(lái)就像是把現(xiàn)實(shí)中的樹(shù)顛倒過(guò)來(lái)的樣子。

常用性質(zhì)
  1. 樹(shù)可以沒(méi)有結(jié)點(diǎn)谴垫,這種情況下把樹(shù)稱(chēng)為空樹(shù)(empty tree)章母。
  2. 樹(shù)的層次(layer)從根結(jié)點(diǎn)開(kāi)始算起,即根結(jié)點(diǎn)為第一層翩剪,根結(jié)點(diǎn)子樹(shù)的根結(jié)點(diǎn)為第二層乳怎,依次類(lèi)推
  3. 把結(jié)點(diǎn)的子樹(shù)棵樹(shù)稱(chēng)為結(jié)點(diǎn)的度(degree),而樹(shù)中結(jié)點(diǎn)的最大的度稱(chēng)為樹(shù)的度(也成為樹(shù)的寬度)
  4. 由于一條邊連接兩個(gè)結(jié)點(diǎn)前弯,且樹(shù)中不存在環(huán)蚪缀,因此對(duì)有n個(gè)結(jié)點(diǎn)的樹(shù),邊數(shù)一定是n-1恕出。且滿(mǎn)足連通询枚、邊數(shù)等于頂點(diǎn)數(shù)減1的結(jié)構(gòu)一定是一棵樹(shù)
  5. 葉子結(jié)點(diǎn)被定義為度為0的結(jié)點(diǎn)剃根,因此當(dāng)樹(shù)中只有一個(gè)結(jié)點(diǎn)(即只有根結(jié)點(diǎn))時(shí)哩盲,根結(jié)點(diǎn)也算作葉子結(jié)點(diǎn)。
  6. 結(jié)點(diǎn)的深度(depth)是指從根結(jié)點(diǎn)(深度為1)開(kāi)始自頂向下逐層累加至該結(jié)點(diǎn)時(shí)的深度值狈醉;結(jié)點(diǎn)的高度(height)是指從最底層葉子結(jié)點(diǎn)(高度為1)開(kāi)始自頂向上逐層累加至該結(jié)點(diǎn)時(shí)的高度值廉油。樹(shù)的深度是指樹(shù)中結(jié)點(diǎn)的最大深度,樹(shù)的高度是指樹(shù)中結(jié)點(diǎn)的最大高度苗傅。對(duì)樹(shù)而言抒线,深度和高度是相等的,但是具體到某個(gè)結(jié)點(diǎn)來(lái)說(shuō)深度和高度就不一定相等了渣慕。
  7. 多棵樹(shù)組合在一起稱(chēng)為森林(forest)嘶炭,即森林是若干棵樹(shù)的集合

二叉樹(shù)

二叉樹(shù)的遞歸定義

  1. 要么二叉樹(shù)沒(méi)有根結(jié)點(diǎn),是一棵空樹(shù)逊桦。
  2. 要么二叉樹(shù)由根結(jié)點(diǎn)眨猎、左子樹(shù)、右子樹(shù)組成强经,且左子樹(shù)和右子樹(shù)都是二叉樹(shù)睡陪。

一是遞歸邊界,二是遞歸式匿情。
二叉樹(shù)中任何一個(gè)結(jié)點(diǎn)的左子樹(shù)既可以是一棵空樹(shù)兰迫,也可以是一棵有左子樹(shù)和右子樹(shù)的二叉樹(shù);結(jié)點(diǎn)的右子樹(shù)也既可以是一棵空樹(shù)炬称,又可以是一棵有左子樹(shù)和右子樹(shù)的二叉樹(shù)汁果,這樣直到遞歸邊界,遞歸定義結(jié)束玲躯。
注意:二叉樹(shù)度為2的樹(shù)的區(qū)別据德。對(duì)樹(shù)來(lái)說(shuō)不區(qū)分左右順序的,因此度為2的樹(shù)只能說(shuō)明樹(shù)中每個(gè)結(jié)點(diǎn)的子結(jié)點(diǎn)個(gè)數(shù)不超過(guò)2跷车。而二叉樹(shù)雖然也滿(mǎn)足每個(gè)結(jié)點(diǎn)的子結(jié)點(diǎn)個(gè)數(shù)不超過(guò)2晋控,但它的左右子樹(shù)是嚴(yán)格區(qū)分的,不能隨意交換左子樹(shù)和右子樹(shù)的位置姓赤,是最主要的區(qū)別赡译。

特殊的二叉樹(shù)

  1. 滿(mǎn)二叉樹(shù):每一層的結(jié)點(diǎn)個(gè)數(shù)都達(dá)到了當(dāng)層能達(dá)到的最大結(jié)點(diǎn)樹(shù)。
  2. 完全二叉樹(shù):除了最下面一層之外不铆,其余層的結(jié)點(diǎn)個(gè)數(shù)都達(dá)到了當(dāng)層能達(dá)到的最大結(jié)點(diǎn)樹(shù)蝌焚,且最下面一層只從左至右連續(xù)存在若干節(jié)點(diǎn),而這些連續(xù)結(jié)點(diǎn)右邊的結(jié)點(diǎn)全部不存在誓斥。

幾個(gè)概念:層次只洒、孩子結(jié)點(diǎn)、父親結(jié)點(diǎn)劳坑、兄弟結(jié)點(diǎn)毕谴、祖先結(jié)點(diǎn)、子孫結(jié)點(diǎn)

二叉樹(shù)的存儲(chǔ)結(jié)構(gòu)與基本操作

  1. 二叉樹(shù)的存儲(chǔ)結(jié)構(gòu)
    一般來(lái)說(shuō),二叉樹(shù)使用鏈表來(lái)定義涝开。如果某個(gè)子樹(shù)不存在循帐,則指向NULL。又把這種鏈表叫做二叉鏈表舀武。
struct node {
    typename data;  //數(shù)據(jù)域
    node* lchild;   //指向左子樹(shù)根結(jié)點(diǎn)的指針
    node* rchild;   //指向右子樹(shù)根結(jié)點(diǎn)的指針 
}; 

由于二叉樹(shù)建樹(shù)前根節(jié)點(diǎn)不存在拄养,因此其地址一般設(shè)為NULL:

node* root = NULL;

如果需要新建結(jié)點(diǎn)(例如往二叉樹(shù)中插入結(jié)點(diǎn)的時(shí)候),就可以使用下面的函數(shù):

node* newNode(int v) {
    node* Node = new node;  //申請(qǐng)一個(gè)node型變量的地址空間
    Node->data = v;     //結(jié)點(diǎn)權(quán)值為v
    Node->lchild = Node->rchild = NULL; //初始狀態(tài)下沒(méi)有左右孩子
    return Node;    //返回新建結(jié)點(diǎn)的地址 
} 
二叉樹(shù)常用操作

二叉樹(shù)結(jié)點(diǎn)的查找银舱、修改
查找操作是指在給定數(shù)據(jù)域的條件下瘪匿,在二叉樹(shù)中找到所有數(shù)據(jù)域?yàn)榻o定數(shù)據(jù)域的結(jié)點(diǎn),并將它們的數(shù)據(jù)域修改為給定的數(shù)據(jù)域寻馏。
遞歸式:指對(duì)當(dāng)前結(jié)點(diǎn)的左子樹(shù)和右子樹(shù)分別遞歸
遞歸邊界:當(dāng)前結(jié)點(diǎn)為空時(shí)到達(dá)死胡同

void search(node* root, int x, int newdata) {
    if(root == NULL) {
        return;         //空樹(shù)棋弥,死胡同(遞歸邊界) 
    }
    if(root->data == x) {   //找到數(shù)據(jù)域?yàn)閤的結(jié)點(diǎn),把它修改成newdata 
        root->data = newdata;
    } 
    search(root->lchild, x, newdata);       //往左子樹(shù)搜索x(遞歸式)
    search(root->rchild, x, newdata);       //往右子樹(shù)搜索x(遞歸式) 
} 

二叉樹(shù)結(jié)點(diǎn)的插入
二叉樹(shù)結(jié)點(diǎn)的插入位置就是數(shù)據(jù)域在二叉樹(shù)中查找失敗的位置诚欠。由于這個(gè)位置是確定的顽染,因此在遞歸查找的過(guò)程中一定是只根據(jù)二叉樹(shù)的性質(zhì)來(lái)選擇左子樹(shù)或右子樹(shù)中的一棵子樹(shù)進(jìn)行遞歸,且最后到達(dá)空樹(shù)(死胡同)的地方就是查找失敗的地方聂薪,也就是結(jié)點(diǎn)需要插入的地方家乘。

//insert函數(shù)將在二叉樹(shù)中插入一個(gè)數(shù)據(jù)域?yàn)閤的新結(jié)點(diǎn)
//注意根結(jié)點(diǎn)指針root要使用引用,否則插入不會(huì)成功
void insert(node* &root, int x) {
    if(root == NULL) {      //空樹(shù)藏澳,說(shuō)明查找失敗仁锯,也即插入位置(遞歸邊界) 
        root = newNode(x);
        return;
    }
    if(由二叉樹(shù)的性質(zhì),x應(yīng)該插在左子樹(shù)) {
        insert(root->lchild, x);    //往左子樹(shù)搜索(遞歸式)        
    } else {
        insert(root->rchild, y);    //往右子樹(shù)搜索(遞歸式)
    }
} 

根結(jié)點(diǎn)指針root使用了引用&翔悠,這么做是业崖,在insert函數(shù)中新建了結(jié)點(diǎn),并把新結(jié)點(diǎn)的地址賦給了當(dāng)層的root蓄愁。不使用引用就不能把新結(jié)點(diǎn)接導(dǎo)二叉樹(shù)上面双炕。(改變r(jià)oot本身,前面search函數(shù)知識(shí)修改指針root指向的內(nèi)容)
判斷是否加引用:如果函數(shù)中需要新建結(jié)點(diǎn)撮抓,即對(duì)二叉樹(shù)的結(jié)構(gòu)做出修改妇斤,就需要加引用;如果知識(shí)修改當(dāng)前已有結(jié)點(diǎn)的內(nèi)容丹拯,或僅僅是遍歷樹(shù)站超,就不用加引用。
注意:在新建結(jié)點(diǎn)之后乖酬,務(wù)必令新結(jié)點(diǎn)的左右指針域?yàn)镹ULL死相,表示這個(gè)新結(jié)點(diǎn)暫時(shí)沒(méi)有左右子樹(shù)。

二叉樹(shù)的創(chuàng)建
二叉樹(shù)的創(chuàng)建其實(shí)就是二叉樹(shù)結(jié)點(diǎn)的插入過(guò)程咬像。
把需要插入的數(shù)據(jù)存儲(chǔ)在數(shù)組中算撮,然后再將它們使用insert函數(shù)一個(gè)個(gè)插入二叉樹(shù)中生宛,并最終返回根結(jié)點(diǎn)的指針root。熟悉后可以直接在建立二叉樹(shù)的過(guò)程中邊輸入數(shù)據(jù)邊插入肮柜。

node* Create(int data[], int n) {
    node* root = NULL;  //新建空根結(jié)點(diǎn)root
    for(int i = 0; i < n; i++) {
        insert(root, data[i]);  //將data[0]~data[n-1]插入二叉樹(shù)中 
    } 
    return root;        //返回根結(jié)點(diǎn) 
}

二叉樹(shù)存儲(chǔ)結(jié)構(gòu)
注意:遞歸邊界為root == NULL而不是 * root == NULL
原因是如果子樹(shù)是空樹(shù)陷舅,那么root一定是NULL,表示這個(gè)結(jié)點(diǎn)不存在素挽。而*root的含義是獲取地址root指向的空間的內(nèi)容蔑赘,但這無(wú)法說(shuō)明地址root是否為空狸驳,也即無(wú)法確定是否存在這個(gè)結(jié)點(diǎn)预明,因此 * root == NULL的寫(xiě)法是錯(cuò)誤的。這個(gè)區(qū)別也是結(jié)點(diǎn)地址為NULL和結(jié)點(diǎn)內(nèi)容為NULL的區(qū)別(也相當(dāng)于結(jié)點(diǎn)不存在與結(jié)點(diǎn)存在但是沒(méi)有內(nèi)容的區(qū)別)

完全二叉樹(shù)的存儲(chǔ)結(jié)構(gòu)
對(duì)完全二叉樹(shù)當(dāng)中的任何一個(gè)結(jié)點(diǎn)(設(shè)編號(hào)為x)耙箍,其左孩子的編號(hào)一定是2x撰糠,而右孩子的編號(hào)一定是2x+1。
完全二叉樹(shù)可以通過(guò)建立一個(gè)大小為2的k次方的數(shù)組來(lái)存放所有結(jié)點(diǎn)的信息辩昆,其中k為完全二叉樹(shù)的最大高度阅酪,且1號(hào)位存放的必須是根結(jié)點(diǎn)。
數(shù)組中元素存放的順序恰好位該完全二叉樹(shù)的層序遍歷序列汁针,而判斷某個(gè)結(jié)點(diǎn)是否為葉結(jié)點(diǎn)的標(biāo)志為:該結(jié)點(diǎn)(記下標(biāo)為root)的左子結(jié)點(diǎn)的編號(hào)root * 2大于結(jié)點(diǎn)總個(gè)數(shù)n术辐;判斷某個(gè)結(jié)點(diǎn)是否為空結(jié)點(diǎn)的標(biāo)志:該結(jié)點(diǎn)下標(biāo)root大于結(jié)點(diǎn)總個(gè)數(shù)n。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末施无,一起剝皮案震驚了整個(gè)濱河市辉词,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猾骡,老刑警劉巖瑞躺,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異兴想,居然都是意外死亡幢哨,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)嫂便,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)捞镰,“玉大人,你說(shuō)我怎么就攤上這事毙替“妒郏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵蔚龙,是天一觀的道長(zhǎng)冰评。 經(jīng)常有香客問(wèn)我,道長(zhǎng)木羹,這世上最難降的妖魔是什么甲雅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任解孙,我火速辦了婚禮,結(jié)果婚禮上抛人,老公的妹妹穿的比我還像新娘弛姜。我一直安慰自己,他們只是感情好妖枚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布廷臼。 她就那樣靜靜地躺著,像睡著了一般绝页。 火紅的嫁衣襯著肌膚如雪荠商。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天续誉,我揣著相機(jī)與錄音莱没,去河邊找鬼。 笑死酷鸦,一個(gè)胖子當(dāng)著我的面吹牛饰躲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臼隔,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嘹裂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了摔握?” 一聲冷哼從身側(cè)響起寄狼,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盒发,沒(méi)想到半個(gè)月后例嘱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宁舰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年拼卵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛮艰。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腋腮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出壤蚜,到底是詐尸還是另有隱情即寡,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布袜刷,位于F島的核電站聪富,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏著蟹。R本人自食惡果不足惜墩蔓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一梢莽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奸披,春花似錦昏名、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至样刷,卻和暖如春仑扑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背颂斜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工夫壁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拾枣,地道東北人沃疮。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像梅肤,于是被迫代替她去往敵國(guó)和親司蔬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容