国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

二叉樹(shù)那些事兒

Little_XM / 3178人閱讀

摘要:大家在聊到二叉樹(shù)的時(shí)候,總會(huì)離不開(kāi)鏈表。受限線(xiàn)性表主要包括棧和隊(duì)列,受限表示對(duì)結(jié)點(diǎn)的操作受限制。存儲(chǔ)結(jié)構(gòu)線(xiàn)性表主要由順序表示或鏈?zhǔn)奖硎?。鏈?zhǔn)奖硎局傅氖怯靡唤M任意的存儲(chǔ)單元存儲(chǔ)線(xiàn)性表中的數(shù)據(jù)元素,稱(chēng)為線(xiàn)性表的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)。

大家在聊到二叉樹(shù)的時(shí)候,總會(huì)離不開(kāi)鏈表。這里先帶大家一起了解一些基本概念。

線(xiàn)性表 概念

線(xiàn)性表是最基本、最簡(jiǎn)單、也是最常用的一種數(shù)據(jù)結(jié)構(gòu)。

線(xiàn)性表中數(shù)據(jù)元素之間的關(guān)系是一對(duì)一的關(guān)系,即除了第一個(gè)和最后一個(gè)數(shù)據(jù)元素之外,其它數(shù)據(jù)元素都是首尾相接的(注意,這句話(huà)只適用大部分線(xiàn)性表,而不是全部。比如,循環(huán)鏈表邏輯層次上也是一種線(xiàn)性表(存儲(chǔ)層次上屬于鏈?zhǔn)酱鎯?chǔ)),但是把最后一個(gè)數(shù)據(jù)元素的尾指針指向了首位結(jié)點(diǎn))。

我們說(shuō)“線(xiàn)性”和“非線(xiàn)性”,只在邏輯層次上討論,而不考慮存儲(chǔ)層次,所以雙向鏈表和循環(huán)鏈表依舊是線(xiàn)性表。在數(shù)據(jù)結(jié)構(gòu)邏輯層次上細(xì)分,線(xiàn)性表可分為一般線(xiàn)性表和受限線(xiàn)性表。一般線(xiàn)性表也就是我們通常所說(shuō)的“線(xiàn)性表”,可以自由的刪除或添加結(jié)點(diǎn)。受限線(xiàn)性表主要包括棧和隊(duì)列,受限表示對(duì)結(jié)點(diǎn)的操作受限制。

特征

1.集合中必存在唯一的一個(gè)“第一元素”。
2.集合中必存在唯一的一個(gè) “最后元素” 。
3.除最后一個(gè)元素之外,均有 唯一的后繼(后件)。
4.除第一個(gè)元素之外,均有 唯一的前驅(qū)(前件)。

存儲(chǔ)結(jié)構(gòu)

線(xiàn)性表主要由順序表示或鏈?zhǔn)奖硎?。在?shí)際應(yīng)用中,常以棧、隊(duì)列、字符串等特殊形式使用。

順序表示指的是用一組地址連續(xù)的存儲(chǔ)單元依次存儲(chǔ)線(xiàn)性表的數(shù)據(jù)元素,稱(chēng)為線(xiàn)性表的順序存儲(chǔ)結(jié)構(gòu)或順序映像。它以“物理位置相鄰”來(lái)表示線(xiàn)性表中數(shù)據(jù)元素間的邏輯關(guān)系,可隨機(jī)存取表中任一元素。

鏈?zhǔn)奖硎局傅氖怯靡唤M任意的存儲(chǔ)單元存儲(chǔ)線(xiàn)性表中的數(shù)據(jù)元素,稱(chēng)為線(xiàn)性表的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)。它的存儲(chǔ)單元可以是連續(xù)的,也可以是不連續(xù)的。在表示數(shù)據(jù)元素之間的邏輯關(guān)系時(shí),除了存儲(chǔ)其本身的信息之外,還需存儲(chǔ)一個(gè)指示其直接后繼的信息(即直接后繼的存儲(chǔ)位置),這兩部分信息組成數(shù)據(jù)元素的存儲(chǔ)映像,稱(chēng)為結(jié)點(diǎn)(node)。它包括兩個(gè)域;存儲(chǔ)數(shù)據(jù)元素信息的域稱(chēng)為數(shù)據(jù)域;存儲(chǔ)直接后繼存儲(chǔ)位置的域稱(chēng)為指針域。指針域中存儲(chǔ)的信息稱(chēng)為指針或鏈

鏈表
鏈表是一種物理存儲(chǔ)單元上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過(guò)鏈表中的指針鏈接次序?qū)崿F(xiàn)的。鏈表由一系列結(jié)點(diǎn)(鏈表中每一個(gè)元素稱(chēng)為結(jié)點(diǎn))組成,結(jié)點(diǎn)可以在運(yùn)行時(shí)動(dòng)態(tài)生成。

每個(gè)結(jié)點(diǎn)包括兩個(gè)部分:

存儲(chǔ)數(shù)據(jù)元素的數(shù)據(jù)域

存儲(chǔ)下一個(gè)結(jié)點(diǎn)地址的指針域。

鏈表不必須按順序存儲(chǔ),鏈表在插入的時(shí)候可以達(dá)到O(1)的時(shí)間復(fù)雜度,比另一種線(xiàn)性表順序表快得多,但是查找一個(gè)節(jié)點(diǎn)或者訪(fǎng)問(wèn)特定編號(hào)的節(jié)點(diǎn)則需要O(n)的時(shí)間,而線(xiàn)性表和順序表相應(yīng)的時(shí)間復(fù)雜度分別是O(logn)和O(1)。
鏈表結(jié)點(diǎn)聲明如下:

class ListNode
{
    let nodeValue; //數(shù)據(jù)
    let next; //指向下一個(gè)節(jié)點(diǎn)的指針

};

從內(nèi)存角度出發(fā): 鏈表可分為 靜態(tài)鏈表、動(dòng)態(tài)鏈表。
從鏈表存儲(chǔ)方式的角度出發(fā):鏈表可分為 單向鏈表、雙向鏈表、以及循環(huán)鏈表。

靜態(tài)鏈表

把線(xiàn)性表的元素存放在數(shù)組中,這些元素可能在物理上是連續(xù)存放的,也有可能不是連續(xù)的,它們之間通過(guò)邏輯關(guān)系來(lái)連接,數(shù)組單位存放鏈表結(jié)點(diǎn),結(jié)點(diǎn)的鏈域指向下一個(gè)元素的位置,即下一個(gè)元素所在的數(shù)組單元的下標(biāo)。顯然靜態(tài)鏈表需要數(shù)組來(lái)實(shí)現(xiàn)。
引出的問(wèn)題:數(shù)組的長(zhǎng)度定義的問(wèn)題,無(wú)法預(yù)支。

動(dòng)態(tài)鏈表(實(shí)際當(dāng)中用的最多)

改善了靜態(tài)鏈表的缺點(diǎn)。它動(dòng)態(tài)的為節(jié)點(diǎn)分配存儲(chǔ)單元。當(dāng)有節(jié)點(diǎn)插入時(shí),系統(tǒng)動(dòng)態(tài)的為結(jié)點(diǎn)分配空間。在結(jié)點(diǎn)刪除時(shí),應(yīng)該及時(shí)釋放相應(yīng)的存儲(chǔ)單元,以防止內(nèi)存泄露。

單鏈表

單鏈表是一種順序存儲(chǔ)的結(jié)構(gòu)。有一個(gè)頭結(jié)點(diǎn),沒(méi)有值域,只有連域,專(zhuān)門(mén)存放第一個(gè)結(jié)點(diǎn)的地址。有一個(gè)尾結(jié)點(diǎn),有值域,也有鏈域,鏈域值始終為NULL。所以,在單鏈表中為找第i個(gè)結(jié)點(diǎn)或數(shù)據(jù)元素,必須先找到第i - 1 結(jié)點(diǎn)或數(shù)據(jù)元素,而且必須知道頭結(jié)點(diǎn),否者整個(gè)鏈表無(wú)法訪(fǎng)問(wèn)。

循環(huán)鏈表

循環(huán)鏈表,類(lèi)似于單鏈表,也是一種鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu),循環(huán)鏈表由單鏈表演化過(guò)來(lái)。單鏈表的最后一個(gè)結(jié)點(diǎn)的鏈域指向NULL,而循環(huán)鏈表的建立,不要專(zhuān)門(mén)的頭結(jié)點(diǎn),讓最后一個(gè)結(jié)點(diǎn)的鏈域指向鏈表結(jié)點(diǎn)。

循環(huán)鏈表與單鏈表的區(qū)別

鏈表的建立。單鏈表需要?jiǎng)?chuàng)建一個(gè)頭結(jié)點(diǎn),專(zhuān)門(mén)存放第一個(gè)結(jié)點(diǎn)的地址。單鏈表的鏈域指向NULL。而循環(huán)鏈表的建立,不要專(zhuān)門(mén)的頭結(jié)點(diǎn),讓最后一個(gè)結(jié)點(diǎn)的鏈域指向鏈表的頭結(jié)點(diǎn)。

鏈表表尾的判斷。單鏈表判斷結(jié)點(diǎn)是否為表尾結(jié)點(diǎn),只需判斷結(jié)點(diǎn)的鏈域值是否是NULL。如果是,則為尾結(jié)點(diǎn);否則不是。而循環(huán)鏈表盤(pán)判斷是否為尾結(jié)點(diǎn),則是判斷該節(jié)點(diǎn)的鏈域是不是指向鏈表的頭結(jié)點(diǎn)。

雙鏈表

雙鏈表也是基于單鏈表的,單鏈表是單向的,有一個(gè)頭結(jié)點(diǎn),一個(gè)尾結(jié)點(diǎn),要訪(fǎng)問(wèn)任何結(jié)點(diǎn),都必須知道頭結(jié)點(diǎn),不能逆著進(jìn)行。而雙鏈表則是添加了一個(gè)鏈域。通過(guò)兩個(gè)鏈域,分別指向結(jié)點(diǎn)的前結(jié)點(diǎn)和后結(jié)點(diǎn)。這樣的話(huà),可以通過(guò)雙鏈表的任何結(jié)點(diǎn),訪(fǎng)問(wèn)到它的前結(jié)點(diǎn)和后結(jié)點(diǎn)。但是雙鏈表還是不夠靈活,在實(shí)際編程中比較常用的是循環(huán)雙鏈表。但循環(huán)雙鏈表使用較為麻煩。

鏈表相關(guān)題目

求單鏈表中結(jié)點(diǎn)的個(gè)數(shù)
這是最最基本的了,應(yīng)該能夠迅速寫(xiě)出正確的代碼,注意檢查鏈表是否為空。時(shí)間復(fù)雜度為O(n)。參考代碼如下:

function GetListLength(head)  
{  
    if(head === NULL) return 0;  
    let len = 0;  
    let current = head;  // ListNode
    while(current !== NULL)  
    {  
        len++;  
        current = current.next;  
    }  
    return len;  
} 

將單鏈表反轉(zhuǎn)
從頭到尾遍歷原鏈表,每遍歷一個(gè)結(jié)點(diǎn),將其摘下放在新鏈表的最前端。注意鏈表為空和只有一個(gè)結(jié)點(diǎn)的情況。時(shí)間復(fù)雜度為O(n)。參考代碼如下

// 反轉(zhuǎn)單鏈表  
function ReverseList(ListNode head)  
{  
    // 如果鏈表為空或只有一個(gè)結(jié)點(diǎn),無(wú)需反轉(zhuǎn),直接返回原鏈表頭指針  
    if(head === NULL || head.next === NULL) return head;  
    let prev = NULL; // ListNode 反轉(zhuǎn)后的新鏈表頭指針,初始為NULL  
    let current = head;  
    while(current !== NULL)  
    {  
        let temp = current;  //獲取當(dāng)前節(jié)點(diǎn)ListNode
        current = current.next;  //獲取下一個(gè)節(jié)點(diǎn)
        temp.next = prev; // 將當(dāng)前結(jié)點(diǎn)摘下,插入新鏈表的最前端  
        prev = temp;  //上一個(gè)節(jié)點(diǎn)前進(jìn)一位
    }  
    return prev;  
}

已知兩個(gè)單鏈表pHead1 和pHead2 各自有序,把它們合并成一個(gè)鏈表依然有序
這個(gè)類(lèi)似歸并排序。尤其注意兩個(gè)鏈表都為空,和其中一個(gè)為空時(shí)的情況。只需要O(1)的空間。時(shí)間復(fù)雜度為O(max(len1, len2))。參考代碼如下:

// 合并兩個(gè)有序鏈表  
function MergeSortedList(head1, head2)
{  
    if(head1 == NULL) return head2;  
    if(head2 == NULL) return head1;  
    let headMerged = NULL;  
    if(head1.nodeValue < head2.nodeValue)  
    {  
        headMerged = head1;  
        headMerged.next = MergeSortedList(head1.next, head2);  
    }  
    else  
    {  
        headMerged = head2;  
        headMerged.next = MergeSortedList(head1, head2.next);  
    }  
    return headMerged;  
} 

判斷一個(gè)單鏈表中是否有環(huán)
這里也是用到兩個(gè)指針。如果一個(gè)鏈表中有環(huán),也就是說(shuō)用一個(gè)指針去遍歷,是永遠(yuǎn)走不到頭的。因此,我們可以用兩個(gè)指針去遍歷,一個(gè)指針一次走兩步,一個(gè)指針一次走一步,如果有環(huán),兩個(gè)指針肯定會(huì)在環(huán)中相遇。時(shí)間復(fù)雜度為O(n)。參考代碼如下:

function HasCircle(head)  
{  
    let pFast = head; // 快指針每次前進(jìn)兩步  
    let pSlow = head; // 慢指針每次前進(jìn)一步  
    while(pFast != NULL && pFast.next != NULL)  
    {  
        pFast = pFast.next.next;  
        pSlow = pSlow.next;  
        if(pSlow == pFast) // 相遇,存在環(huán)  
            return true;  
    }  
    return false;  
}

判斷兩個(gè)單鏈表是否相交
如果兩個(gè)鏈表相交于某一節(jié)點(diǎn),那么在這個(gè)相交節(jié)點(diǎn)之后的所有節(jié)點(diǎn)都是兩個(gè)鏈表所共有的。也就是說(shuō),如果兩個(gè)鏈表相交,那么最后一個(gè)節(jié)點(diǎn)肯定是共有的。
先遍歷第一個(gè)鏈表,記住最后一個(gè)節(jié)點(diǎn),然后遍歷第二個(gè)鏈表,到最后一個(gè)節(jié)點(diǎn)時(shí)和第一個(gè)鏈表的最后一個(gè)節(jié)點(diǎn)做比較,如果相同,則相交,否則不相交。時(shí)間復(fù)雜度為O(len1+len2),因?yàn)橹恍枰粋€(gè)額外指針保存最后一個(gè)節(jié)點(diǎn)地址,空間復(fù)雜度為O(1)。參考代碼如下:

function IsIntersected(head1, head2)  
{  
    if(pHead1 == NULL || pHead2 == NULL) return false;  
    let pTail1 = head1;  // 用來(lái)存儲(chǔ)第一個(gè)鏈表的最后一個(gè)節(jié)點(diǎn)
    while(pTail1.next != NULL) { 
        pTail1 = pTail1.next; 
    } 
  
    let pTail2 = head2;  // 用來(lái)存儲(chǔ)第二個(gè)鏈表的最后一個(gè)節(jié)點(diǎn)
    while(pTail2.next != NULL) {  
        pTail2 = pTail2.next; 
    } 
    return pTail1 == pTail2;  
} 

查找單鏈表中的倒數(shù)第K個(gè)結(jié)點(diǎn)(k > 0)
主要思路就是使用兩個(gè)指針,先讓前面的指針走到正向第k個(gè)結(jié)點(diǎn),這樣前后兩個(gè)指針的距離差是k-1,之后前后兩個(gè)指針一起向前走,前面的指針走到最后一個(gè)結(jié)點(diǎn)時(shí),后面指針?biāo)附Y(jié)點(diǎn)就是倒數(shù)第k個(gè)結(jié)點(diǎn)。
參考代碼如下:

// 查找單鏈表中倒數(shù)第K個(gè)結(jié)點(diǎn)  
function GetRKthNode(head, k)
{  
    if(k == 0 || head == NULL) // 這里k的計(jì)數(shù)是從1開(kāi)始的,若k為0或鏈表為空返回NULL  
        return NULL;  
  
    let pAhead = head;  
    let pBehind = head;  
    while(k > 1 && pAhead != NULL) // 前面的指針先走到正向第k個(gè)結(jié)點(diǎn)  
    {  
        pAhead = pAhead.next;  
        k--;  
    }  
    if(k > 1 || pAhead == NULL)     // 結(jié)點(diǎn)個(gè)數(shù)小于k,返回NULL  
        return NULL;  
    while(pAhead.next != NULL)  // 前后兩個(gè)指針一起向前走,直到前面的指針指向最后一個(gè)結(jié)點(diǎn)  
    {  
        pBehind = pBehind.next;  
        pAhead = pAhead.next;
    }  
    return pBehind;  // 后面的指針?biāo)附Y(jié)點(diǎn)就是倒數(shù)第k個(gè)結(jié)點(diǎn)  
} 

查找單鏈表的中間結(jié)點(diǎn)
此題可應(yīng)用于上一題類(lèi)似的思想。也是設(shè)置兩個(gè)指針,只不過(guò)這里是,兩個(gè)指針同時(shí)向前走,前面的指針每次走兩步,后面的指針每次走一步,前面的指針走到最后一個(gè)結(jié)點(diǎn)時(shí),后面的指針?biāo)附Y(jié)點(diǎn)就是中間結(jié)點(diǎn),即第(n/2+1)個(gè)結(jié)點(diǎn)。注意鏈表為空,鏈表結(jié)點(diǎn)個(gè)數(shù)為1和2的情況。時(shí)間復(fù)雜度O(n)。參考代碼如下:

// 獲取單鏈表中間結(jié)點(diǎn),若鏈表長(zhǎng)度為n(n>0),則返回第n/2+1個(gè)結(jié)點(diǎn)  
function GetMiddleNode(head)  
{  
    if(head == NULL || head.next == NULL) // 鏈表為空或只有一個(gè)結(jié)點(diǎn),返回頭指針  
        return head;  
  
    let pAhead = head;  
    let pBehind = head;  
    while(pAhead.next != NULL) // 前面指針每次走兩步,直到指向最后一個(gè)結(jié)點(diǎn),后面指針每次走一步  
    {  
        pAhead = pAhead.next;  
        pBehind = pBehind.next;  
        if(pAhead.next != NULL)  
            pAhead = pAhead.next;  
    }  
    return pBehind; // 后面的指針?biāo)附Y(jié)點(diǎn)即為中間結(jié)點(diǎn)  
} 

反轉(zhuǎn)雙向鏈表

function reverse(head){
    let cur = null;
    while(head != null){
        cur = head;
        head = head.next;
        cur.next = cur.prev;
        cur.prev = head;
    }
    return cur;
}
二叉樹(shù) 概念
二叉樹(shù)(Binary Tree)是n(n>=0)個(gè)結(jié)點(diǎn)的有限集合,該集合或者為空集(空二叉樹(shù)),或者由一個(gè)根結(jié)點(diǎn)和兩棵互不相交的、分別稱(chēng)為根結(jié)點(diǎn)的左子樹(shù)和右子樹(shù)的二叉樹(shù)組成。
特點(diǎn)

每個(gè)結(jié)點(diǎn)最多有兩棵子樹(shù),所以二叉樹(shù)中不存在大于2的結(jié)點(diǎn)。二叉樹(shù)中每一個(gè)節(jié)點(diǎn)都是一個(gè)對(duì)象,每一個(gè)數(shù)據(jù)節(jié)點(diǎn)都有三個(gè)指針,分別是指向父母、左孩子和右孩子的指針。每一個(gè)節(jié)點(diǎn)都是通過(guò)指針相互連接的。相連指針的關(guān)系都是父子關(guān)系。

二叉樹(shù)節(jié)點(diǎn)的定義
struct BinaryTreeNode
{
    int m_nValue;
    BinaryTreeNode* m_pLeft;
    BinaryTreeNode* m_pRight;
};
二叉樹(shù)的五種基本形態(tài)

滿(mǎn)二叉樹(shù)

在一棵二叉樹(shù)中,如果所有分支結(jié)點(diǎn)都存在左子樹(shù)和右子樹(shù),并且所有葉子都在同一層上,這樣的二叉樹(shù)稱(chēng)為滿(mǎn)二叉樹(shù)。如下圖所示:

完全二叉樹(shù)

完全二叉樹(shù)是指最后一層左邊是滿(mǎn)的,右邊可能滿(mǎn)也可能不滿(mǎn),然后其余層都是滿(mǎn)的。一個(gè)深度為k,節(jié)點(diǎn)個(gè)數(shù)為 2^k - 1 的二叉樹(shù)為滿(mǎn)二叉樹(shù)(完全二叉樹(shù))。就是一棵樹(shù),深度為k,并且沒(méi)有空位。

完全二叉樹(shù)的特點(diǎn)有:

葉子結(jié)點(diǎn)只能出現(xiàn)在最下兩層。

最下層的葉子一定集中在左部連續(xù)位置。

倒數(shù)第二層,若有葉子結(jié)點(diǎn),一定都在右部連續(xù)位置。

如果結(jié)點(diǎn)度為1,則該結(jié)點(diǎn)只有左孩子。

同樣結(jié)點(diǎn)樹(shù)的二叉樹(shù),完全二叉樹(shù)的深度最小。

二叉樹(shù)的遍歷

是指從根結(jié)點(diǎn)出發(fā),按照某種次序依次訪(fǎng)問(wèn)二叉樹(shù)中所有結(jié)點(diǎn),使得每個(gè)結(jié)點(diǎn)被訪(fǎng)問(wèn)一次且僅被訪(fǎng)問(wèn)一次。

二叉樹(shù)的遍歷有三種方式,如下:
前序遍歷:
若二叉樹(shù)為空,則空操作返回,否則先訪(fǎng)問(wèn)根結(jié)點(diǎn),然后前序遍歷左子樹(shù),再前序遍歷右子樹(shù)。
中序遍歷:
若樹(shù)為空,則空操作返回,否則從根結(jié)點(diǎn)開(kāi)始(注意并不是先訪(fǎng)問(wèn)根結(jié)點(diǎn)),中序遍歷根結(jié)點(diǎn)的左子樹(shù),然后是訪(fǎng)問(wèn)根結(jié)點(diǎn),最后中序遍歷右子樹(shù)。
后序遍歷:
若樹(shù)為空,則空操作返回,否則從左到右先葉子后結(jié)點(diǎn)的方式遍歷訪(fǎng)問(wèn)左右子樹(shù),最后訪(fǎng)問(wèn)根結(jié)點(diǎn)。

二叉查找樹(shù)

二叉排序樹(shù)或者是一棵空樹(shù),或者是具有下列性質(zhì)的二叉樹(shù):
(1)若左子樹(shù)不空,則左子樹(shù)上所有結(jié)點(diǎn)的值均小于或等于它的根結(jié)點(diǎn)的值;
(2)若右子樹(shù)不空,則右子樹(shù)上所有結(jié)點(diǎn)的值均大于或等于它的根結(jié)點(diǎn)的值;
(3)左、右子樹(shù)也分別為二叉排序樹(shù);

二叉查找樹(shù)(BST)由節(jié)點(diǎn)組成,所以我們定義一個(gè)Node節(jié)點(diǎn)對(duì)象如下:

function Node(data,left,right){
    this.data = data;
    this.left = left;//保存left節(jié)點(diǎn)鏈接
    this.right = right;
    this.show = show;
}


function show(){
    return this.data;//顯示保存在節(jié)點(diǎn)中的數(shù)據(jù)
}

查找最大和最小值
查找BST上的最小值和最大值非常簡(jiǎn)單,因?yàn)檩^小的值總是在左子節(jié)點(diǎn)上,在BST上查找最小值,只需遍歷左子樹(shù),直到找到最后一個(gè)節(jié)點(diǎn)

function getMin(){
    var current = this.root;
    while(!(current.left == null)){
        current = current.left;
    }
    return current.data;
}

function getMax(){
    var current = this.root;
    while(!(current.right == null)){
        current = current.right;
    }
    return current.data;
}
二叉樹(shù)相關(guān)題目 求二叉樹(shù)中的節(jié)點(diǎn)個(gè)數(shù)

遞歸解法:
(1)如果二叉樹(shù)為空,節(jié)點(diǎn)個(gè)數(shù)為0
(2)如果二叉樹(shù)不為空,二叉樹(shù)節(jié)點(diǎn)個(gè)數(shù) = 左子樹(shù)節(jié)點(diǎn)個(gè)數(shù) + 右子樹(shù)節(jié)點(diǎn)個(gè)數(shù) + 1
參考代碼如下:

function GetNodeNum(pRoot)  
{  
    if(pRoot == NULL) return 0;  
    return GetNodeNum(pRoot->m_pLeft) + GetNodeNum(pRoot->m_pRight) + 1;  
} 
求二叉樹(shù)的深度

遞歸解法:
(1)如果二叉樹(shù)為空,二叉樹(shù)的深度為0
(2)如果二叉樹(shù)不為空,二叉樹(shù)的深度 = max(左子樹(shù)深度, 右子樹(shù)深度) + 1
參考代碼如下:

function GetDepth(pRoot)  
{  
    if(pRoot == NULL) return 0;  
    let depthLeft = GetDepth(pRoot->m_pLeft);  
    let depthRight = GetDepth(pRoot->m_pRight);  
    return depthLeft > depthRight ? (depthLeft + 1) : (depthRight + 1);   
} 
前序遍歷,中序遍歷,后序遍歷

前序遍歷遞歸解法:
(1)如果二叉樹(shù)為空,空操作
(2)如果二叉樹(shù)不為空,訪(fǎng)問(wèn)根節(jié)點(diǎn),前序遍歷左子樹(shù),前序遍歷右子樹(shù)
參考代碼如下:

function PreOrderTraverse(pRoot)  
{  
    if(pRoot == NULL) return;  
    Visit(pRoot); // 訪(fǎng)問(wèn)根節(jié)點(diǎn)  
    PreOrderTraverse(pRoot->m_pLeft); // 前序遍歷左子樹(shù)  
    PreOrderTraverse(pRoot->m_pRight); // 前序遍歷右子樹(shù)  
}

中序遍歷遞歸解法
(1)如果二叉樹(shù)為空,空操作。
(2)如果二叉樹(shù)不為空,中序遍歷左子樹(shù),訪(fǎng)問(wèn)根節(jié)點(diǎn),中序遍歷右子樹(shù)
參考代碼如下:

function InOrderTraverse(pRoot)  
{  
    if(pRoot == NULL) return;  
    InOrderTraverse(pRoot->m_pLeft); // 中序遍歷左子樹(shù)  
    Visit(pRoot); // 訪(fǎng)問(wèn)根節(jié)點(diǎn)  
    InOrderTraverse(pRoot->m_pRight); // 中序遍歷右子樹(shù)  
} 

后序遍歷遞歸解法
(1)如果二叉樹(shù)為空,空操作
(2)如果二叉樹(shù)不為空,后序遍歷左子樹(shù),后序遍歷右子樹(shù),訪(fǎng)問(wèn)根節(jié)點(diǎn)
參考代碼如下:

function PostOrderTraverse(pRoot)  
{  
    if(pRoot == NULL) return;  
    PostOrderTraverse(pRoot->m_pLeft); // 后序遍歷左子樹(shù)  
    PostOrderTraverse(pRoot->m_pRight); // 后序遍歷右子樹(shù)  
    Visit(pRoot); // 訪(fǎng)問(wèn)根節(jié)點(diǎn)  
}
分層遍歷二叉樹(shù)(按層次從上往下,從左往右)

相當(dāng)于廣度優(yōu)先搜索,使用隊(duì)列實(shí)現(xiàn)。隊(duì)列初始化,將根節(jié)點(diǎn)壓入隊(duì)列。當(dāng)隊(duì)列不為空,進(jìn)行如下操作:彈出一個(gè)節(jié)點(diǎn),訪(fǎng)問(wèn),若左子節(jié)點(diǎn)或右子節(jié)點(diǎn)不為空,將其壓入隊(duì)列。

function LevelTraverse(pRoot)  
{  
    if(pRoot == NULL) return;  
    queue q;  
    q.push(pRoot);  
    while(!q.empty())  
    {  
        BinaryTreeNode * pNode = q.front();  
        q.pop();  
        Visit(pNode); // 訪(fǎng)問(wèn)節(jié)點(diǎn)  
        if(pNode->m_pLeft != NULL)  
            q.push(pNode->m_pLeft);  
        if(pNode->m_pRight != NULL)  
            q.push(pNode->m_pRight);  
    }  
    return;  
} 
將二叉查找樹(shù)變?yōu)橛行虻碾p向鏈表

要求不能創(chuàng)建新節(jié)點(diǎn),只調(diào)整指針。
遞歸解法:
(1)如果二叉樹(shù)查找樹(shù)為空,不需要轉(zhuǎn)換,對(duì)應(yīng)雙向鏈表的第一個(gè)節(jié)點(diǎn)是NULL,最后一個(gè)節(jié)點(diǎn)是NULL
(2)如果二叉查找樹(shù)不為空:
如果左子樹(shù)為空,對(duì)應(yīng)雙向有序鏈表的第一個(gè)節(jié)點(diǎn)是根節(jié)點(diǎn),左邊不需要其他操作;
如果左子樹(shù)不為空,轉(zhuǎn)換左子樹(shù),二叉查找樹(shù)對(duì)應(yīng)雙向有序鏈表的第一個(gè)節(jié)點(diǎn)就是左子樹(shù)轉(zhuǎn)換后雙向有序鏈表的第一個(gè)節(jié)點(diǎn),同時(shí)將根節(jié)點(diǎn)和左子樹(shù)轉(zhuǎn)換后的雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)連接;
如果右子樹(shù)為空,對(duì)應(yīng)雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)是根節(jié)點(diǎn),右邊不需要其他操作;
如果右子樹(shù)不為空,對(duì)應(yīng)雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)就是右子樹(shù)轉(zhuǎn)換后雙向有序鏈表的最后一個(gè)節(jié)點(diǎn),同時(shí)將根節(jié)點(diǎn)和右子樹(shù)轉(zhuǎn)換后的雙向有序鏈表的第一個(gè)節(jié)點(diǎn)連 接。
參考代碼如下:

/*******************************************************************
參數(shù): 
pRoot: 二叉查找樹(shù)根節(jié)點(diǎn)指針 
pFirstNode: 轉(zhuǎn)換后雙向有序鏈表的第一個(gè)節(jié)點(diǎn)指針 
pLastNode: 轉(zhuǎn)換后雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)指針 
*******************************************************************/  
function Convert(BinaryTreeNode * pRoot,   
             BinaryTreeNode * & pFirstNode, BinaryTreeNode * & pLastNode)  
{  
    BinaryTreeNode *pFirstLeft, *pLastLeft, * pFirstRight, *pLastRight;  
    if(pRoot == NULL)   
    {  
        pFirstNode = NULL;  
        pLastNode = NULL;  
        return;  
    }  
  
    if(pRoot->m_pLeft == NULL)  
    {  
        // 如果左子樹(shù)為空,對(duì)應(yīng)雙向有序鏈表的第一個(gè)節(jié)點(diǎn)是根節(jié)點(diǎn)  
        pFirstNode = pRoot;  
    }  
    else  
    {  
        Convert(pRoot->m_pLeft, pFirstLeft, pLastLeft);  
        // 二叉查找樹(shù)對(duì)應(yīng)雙向有序鏈表的第一個(gè)節(jié)點(diǎn)就是左子樹(shù)轉(zhuǎn)換后雙向有序鏈表的第一個(gè)節(jié)點(diǎn)  
        pFirstNode = pFirstLeft;  
        // 將根節(jié)點(diǎn)和左子樹(shù)轉(zhuǎn)換后的雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)連接  
        pRoot->m_pLeft = pLastLeft;  
        pLastLeft->m_pRight = pRoot;  
    }  
  
    if(pRoot->m_pRight == NULL)  
    {  
        // 對(duì)應(yīng)雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)是根節(jié)點(diǎn)  
        pLastNode = pRoot;  
    }  
    else  
    {  
        Convert(pRoot->m_pRight, pFirstRight, pLastRight);  
        // 對(duì)應(yīng)雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)就是右子樹(shù)轉(zhuǎn)換后雙向有序鏈表的最后一個(gè)節(jié)點(diǎn)  
        pLastNode = pLastRight;  
        // 將根節(jié)點(diǎn)和右子樹(shù)轉(zhuǎn)換后的雙向有序鏈表的第一個(gè)節(jié)點(diǎn)連接  
        pRoot->m_pRight = pFirstRight;  
        pFirstRight->m_pLeft = pRoot;  
    }  
  
    return;  
}  
求二叉樹(shù)第K層的節(jié)點(diǎn)個(gè)數(shù)

遞歸解法:
(1)如果二叉樹(shù)為空或者k<1返回0
(2)如果二叉樹(shù)不為空并且k==1,返回1
(3)如果二叉樹(shù)不為空且k>1,返回左子樹(shù)中k-1層的節(jié)點(diǎn)個(gè)數(shù)與右子樹(shù)k-1層節(jié)點(diǎn)個(gè)數(shù)之和
參考代碼如下:

function GetNodeNumKthLevel(pRoot, k)  
{  
    if(pRoot == NULL || k < 1) return 0;  
    if(k == 1) return 1;  
    let numLeft = GetNodeNumKthLevel(pRoot->m_pLeft, k-1); // 左子樹(shù)中k-1層的節(jié)點(diǎn)個(gè)數(shù)  
    let numRight = GetNodeNumKthLevel(pRoot->m_pRight, k-1); // 右子樹(shù)中k-1層的節(jié)點(diǎn)個(gè)數(shù)  
    return (numLeft + numRight);  
} 
判斷兩棵二叉樹(shù)是否結(jié)構(gòu)相同

不考慮數(shù)據(jù)內(nèi)容。結(jié)構(gòu)相同意味著對(duì)應(yīng)的左子樹(shù)和對(duì)應(yīng)的右子樹(shù)都結(jié)構(gòu)相同。
遞歸解法:
(1)如果兩棵二叉樹(shù)都為空,返回真
(2)如果兩棵二叉樹(shù)一棵為空,另一棵不為空,返回假
(3)如果兩棵二叉樹(shù)都不為空,如果對(duì)應(yīng)的左子樹(shù)和右子樹(shù)都同構(gòu)返回真,其他返回假
參考代碼如下:

function StructureCmp(BinaryTreeNode * pRoot1, BinaryTreeNode * pRoot2)  
{  
    if(pRoot1 == NULL && pRoot2 == NULL) // 都為空,返回真  
        return true;  
    else if(pRoot1 == NULL || pRoot2 == NULL) // 有一個(gè)為空,一個(gè)不為空,返回假  
        return false;  
    let resultLeft = StructureCmp(pRoot1->m_pLeft, pRoot2->m_pLeft); // 比較對(duì)應(yīng)左子樹(shù)   
    let resultRight = StructureCmp(pRoot1->m_pRight, pRoot2->m_pRight); // 比較對(duì)應(yīng)右子樹(shù)  
    return (resultLeft && resultRight);  
} 
判斷二叉樹(shù)是不是平衡二叉樹(shù)

遞歸解法:
(1)如果二叉樹(shù)為空,返回真
(2)如果二叉樹(shù)不為空,如果左子樹(shù)和右子樹(shù)都是AVL樹(shù)并且左子樹(shù)和右子樹(shù)高度相差不大于1,返回真,其他返回假

求二叉樹(shù)的鏡像

遞歸解法:
(1)如果二叉樹(shù)為空,返回空
(2)如果二叉樹(shù)不為空,求左子樹(shù)和右子樹(shù)的鏡像,然后交換左子樹(shù)和右子樹(shù)
參考代碼如下:

function Mirror(BinaryTreeNode * pRoot)  
{  
    if(pRoot == NULL) // 返回NULL  
        return NULL;  
    BinaryTreeNode * pLeft = Mirror(pRoot->m_pLeft); // 求左子樹(shù)鏡像  
    BinaryTreeNode * pRight = Mirror(pRoot->m_pRight); // 求右子樹(shù)鏡像  
        // 交換左子樹(shù)和右子樹(shù)  
    pRoot->m_pLeft = pRight;  
    pRoot->m_pRight = pLeft;  
    return pRoot;  
}
判斷二叉樹(shù)是不是完全二叉樹(shù)

若設(shè)二叉樹(shù)的深度為h,除第 h 層外,其它各層 (1~h-1) 的結(jié)點(diǎn)數(shù)都達(dá)到最大個(gè)數(shù),第 h 層所有的結(jié)點(diǎn)都連續(xù)集中在最左邊,這就是完全二叉樹(shù)。
有如下算法,按層次(從上到下,從左到右)遍歷二叉樹(shù),當(dāng)遇到一個(gè)節(jié)點(diǎn)的左子樹(shù)為空時(shí),則該節(jié)點(diǎn)右子樹(shù)必須為空,且后面遍歷的節(jié)點(diǎn)左右子樹(shù)都必須為空,否則不是完全二叉樹(shù)。

bool IsCompleteBinaryTree(BinaryTreeNode * pRoot)  
{  
    if(pRoot == NULL)  
        return false;  
    queue q;  
    q.push(pRoot);  
    bool mustHaveNoChild = false;  
    bool result = true;  
    while(!q.empty())  
    {  
        BinaryTreeNode * pNode = q.front();  
        q.pop();  
        if(mustHaveNoChild) // 已經(jīng)出現(xiàn)了有空子樹(shù)的節(jié)點(diǎn)了,后面出現(xiàn)的必須為葉節(jié)點(diǎn)(左右子樹(shù)都為空)  
        {  
            if(pNode->m_pLeft != NULL || pNode->m_pRight != NULL)  
            {  
                result = false;  
                break;  
            }  
        }  
        else  
        {  
            if(pNode->m_pLeft != NULL && pNode->m_pRight != NULL)  
            {  
                q.push(pNode->m_pLeft);  
                q.push(pNode->m_pRight);  
            }  
            else if(pNode->m_pLeft != NULL && pNode->m_pRight == NULL)  
            {  
                mustHaveNoChild = true;  
                q.push(pNode->m_pLeft);  
            }  
            else if(pNode->m_pLeft == NULL && pNode->m_pRight != NULL)  
            {  
                result = false;  
                break;  
            }  
            else  
            {  
                mustHaveNoChild = true;  
            }  
        }  
    }  
    return result;  
} 

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/94381.html

相關(guān)文章

  • JDK源碼那些事兒之紅黑樹(shù)基礎(chǔ)上篇

    摘要:用這種范例表示紅黑樹(shù)是可能的,但是這會(huì)改變一些性質(zhì)并使算法復(fù)雜。插入會(huì)出現(xiàn)種情況為根節(jié)點(diǎn),即紅黑樹(shù)的根節(jié)點(diǎn)。 說(shuō)到HashMap,就一定要說(shuō)到紅黑樹(shù),紅黑樹(shù)作為一種自平衡二叉查找樹(shù),是一種用途較廣的數(shù)據(jù)結(jié)構(gòu),在jdk1.8中使用紅黑樹(shù)提升HashMap的性能,今天就來(lái)說(shuō)一說(shuō)紅黑樹(shù)。 前言 限于篇幅,本文只對(duì)紅黑樹(shù)的基礎(chǔ)進(jìn)行說(shuō)明,暫不涉及源碼部分,大部分摘抄自維基百科,這里也貼出對(duì)應(yīng)鏈接...

    qylost 評(píng)論0 收藏0
  • JDK源碼那些事兒之紅黑樹(shù)基礎(chǔ)下篇

    摘要:強(qiáng)調(diào)一下,紅黑樹(shù)中的葉子節(jié)點(diǎn)指的都是節(jié)點(diǎn)。故刪除之后紅黑樹(shù)平衡不用調(diào)整。將達(dá)到紅黑樹(shù)平衡。到此關(guān)于紅黑樹(shù)的基礎(chǔ)已經(jīng)介紹完畢,下一章我將就源碼中的進(jìn)行講解說(shuō)明,看一看紅黑樹(shù)是如何在源碼中實(shí)現(xiàn)的。 說(shuō)到HashMap,就一定要說(shuō)到紅黑樹(shù),紅黑樹(shù)作為一種自平衡二叉查找樹(shù),是一種用途較廣的數(shù)據(jù)結(jié)構(gòu),在jdk1.8中使用紅黑樹(shù)提升HashMap的性能,今天就來(lái)說(shuō)一說(shuō)紅黑樹(shù),上一講已經(jīng)給出插入平衡...

    羅志環(huán) 評(píng)論0 收藏0
  • 【數(shù)據(jù)結(jié)構(gòu)初階】第八篇——叉樹(shù)的鏈?zhǔn)浇Y(jié)構(gòu)(叉樹(shù)的前、中和后序遍歷+層序遍歷+鏈?zhǔn)浇Y(jié)構(gòu)的實(shí)現(xiàn)+相關(guān)

    摘要:代碼實(shí)現(xiàn)如下二叉樹(shù)的創(chuàng)建與銷(xiāo)毀二叉樹(shù)的創(chuàng)建問(wèn)題通過(guò)前序遍歷的數(shù)組給定一串字符串,代表的是空樹(shù),其他的都是節(jié)點(diǎn)。 ??本篇博客我要來(lái)和大家一起聊一聊數(shù)據(jù)結(jié)構(gòu)中的二...

    BigNerdCoding 評(píng)論0 收藏0
  • 數(shù)據(jù)結(jié)構(gòu)與算法:叉樹(shù)算法

    摘要:因此,根據(jù)題目給出的先序遍歷和中序遍歷,可以畫(huà)出二叉樹(shù)選參考數(shù)據(jù)結(jié)構(gòu)與算法描述實(shí)現(xiàn)二叉樹(shù)算法淺談數(shù)據(jù)結(jié)構(gòu)二叉樹(shù)慕課網(wǎng)實(shí)現(xiàn)二叉樹(shù)算法前端樹(shù)控件騰訊軟件開(kāi)發(fā)面試題 內(nèi)容銜接上一章 數(shù)據(jù)結(jié)構(gòu)與算法:常見(jiàn)排序算法 內(nèi)容提要 什么是樹(shù)   - 為什么使用樹(shù) 二叉樹(shù) 二叉查找樹(shù) 紅黑樹(shù) B、B+樹(shù) 堆 伸展樹(shù) 樹(shù) 可以點(diǎn)擊鏈接感受下筆者用d3.js畫(huà)的tree https://codepen...

    Little_XM 評(píng)論0 收藏0
  • 數(shù)據(jù)結(jié)構(gòu)-叉樹(shù)二叉查找樹(shù)

    摘要:樹(shù)是計(jì)算機(jī)科學(xué)中經(jīng)常用到的一種數(shù)據(jù)結(jié)構(gòu)數(shù)是一種非線(xiàn)性的數(shù)據(jù)結(jié)構(gòu)以分層的方式存儲(chǔ)數(shù)據(jù)數(shù)被用來(lái)存儲(chǔ)具有層級(jí)關(guān)系的數(shù)據(jù)比如文件系統(tǒng)中的文件樹(shù)還被用來(lái)存儲(chǔ)有序列表本章將研究一種特殊的樹(shù)二叉樹(shù)選擇樹(shù)而不是那些基本的數(shù)據(jù)結(jié)構(gòu)是因?yàn)樵诙鏄?shù)上進(jìn)行查找非常 樹(shù)是計(jì)算機(jī)科學(xué)中經(jīng)常用到的一種數(shù)據(jù)結(jié)構(gòu). 數(shù)是一種非線(xiàn)性的數(shù)據(jù)結(jié)構(gòu), 以分層的方式存儲(chǔ)數(shù)據(jù). 數(shù)被用來(lái)存儲(chǔ)具有層級(jí)關(guān)系的數(shù)據(jù), 比如文件系統(tǒng)中的文...

    lindroid 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<