【數(shù)據(jù)結(jié)構(gòu)】二叉樹及其各種遍歷

關(guān)于樹的定義和存儲結(jié)構(gòu)可以查看上一篇文章樹的定義和樹的三種存儲結(jié)構(gòu)

一蹄溉、二叉樹的定義

二叉樹的定義

二叉樹(Binary Tree)是n(n>=0)個結(jié)點的有限集合昼钻,該集合或者為空集(稱為空二叉樹),或者是由一個根結(jié)點和兩顆互不相交的、分別稱為根結(jié)點的左子樹和右子樹的二叉樹組成痘昌。(官方概念,不是很直觀,直接上圖)

二叉樹

二叉樹的特點

  • 每個結(jié)點最多有兩顆子樹辆苔,所以二叉樹中不存在度大于2的結(jié)點算灸。(注意不是只有兩顆子樹,是最多有)
  • 左子樹和右子樹是有順序的驻啤,次序不能任意顛倒菲驴。
  • 即使樹中某個結(jié)點只有一棵樹,也要區(qū)分它是左子樹還是右子樹街佑。

二叉樹的五中基本形態(tài)

  • 空二叉樹谢翎。
  • 只有一個根節(jié)點。
  • 根結(jié)點只有左子樹沐旨。
  • 根結(jié)點只有右子樹森逮。
  • 根結(jié)點既有左子樹又有右子樹。

特殊二叉樹

斜樹
  • 左斜樹:所有的結(jié)點都只有左子樹的二叉樹磁携。
  • 右斜樹:所有的結(jié)點都只有右子樹的二叉樹褒侧。
滿二叉樹

所有分支結(jié)點都存在左子樹和右子樹,并且所有葉子都在同一層上的二叉樹谊迄。

滿二叉樹的特點
  • 葉子結(jié)點只能出現(xiàn)在最下一層闷供。出現(xiàn)在其他層就不可能達到平衡。
  • 非葉子結(jié)點的度一定是2统诺。
  • 在同樣深度的二叉樹中歪脏,滿二叉樹的結(jié)點個數(shù)最多,葉子數(shù)最少粮呢。
完全二叉樹

對于一顆具有n個結(jié)點的二叉樹按層序編號婿失,如果編號為i(1<=i&&i<=n)的結(jié)點與同樣深度的滿二叉樹中編號為i的結(jié)點在二叉樹中位置完全相同的二叉樹。(概念難理解啄寡,直接上圖)

完全二叉樹

完全二叉樹的特點
  • 葉子結(jié)點只能出現(xiàn)在最下兩層豪硅。
  • 最下層的葉子一定集中在左部連續(xù)位置
  • 倒數(shù)第二層挺物,若有葉子結(jié)點懒浮,一定都在右部連續(xù)位置
  • 如果結(jié)點度為1识藤,則該節(jié)點只有左孩子砚著,即不存在只有右子樹的情況。
  • 同樣結(jié)點的二叉樹痴昧,完全二叉樹的深度最小赖草。
區(qū)分滿二叉樹和完全二叉樹
  • 滿二叉樹一定是完全二叉樹。
  • 完全二叉樹不一定是滿二叉樹剪个。

二、二叉樹的性質(zhì)

  • 性質(zhì)1:在二叉樹的第i層上至多2^(i-1)個結(jié)點(i>=1)。
  • 性質(zhì)2:深度為k的二叉樹至多有2^k - 1個結(jié)點(k>=1)扣囊。(注意是2^k后再減去1
  • 性質(zhì)3:對任何一顆二叉樹T乎折,如果其終端結(jié)點數(shù)為n0,度為2的結(jié)點樹為n2,則n0 = n2+1.
  • 性質(zhì)4:具有n個結(jié)點的完全二叉樹的深度為:向下取整log2n+1;(注意,對x的向下取整就是取不大于x的最大整數(shù))
  • 性質(zhì)5:如果對一顆有n個結(jié)點的完全二叉樹的結(jié)點按層序編號(從第1層到第(向下取整log2n+1)侵歇,每層從左到右)層骂澄,對任一結(jié)點i(1<=i&&i<=n)有:
    • 如果i=1,則結(jié)點i是二叉樹的根惕虑,無雙親坟冲;如果i>1,則其雙親是結(jié)點:向下取整i/2;
    • 如果2i>n溃蔫,則結(jié)點i無左孩子(結(jié)點i為葉子結(jié)點);否則其左孩子是結(jié)點2i;
    • 如果2i+1>n健提,則結(jié)點i無右孩子;否則其右孩子是結(jié)點2i+1伟叛;

三私痹、二叉樹的存儲結(jié)構(gòu)

樹的存儲結(jié)構(gòu)可以查看前一篇文章:
由于二叉樹是特殊的樹樹的定義和樹的三種存儲結(jié)構(gòu)

由于二叉樹是一種特殊的樹,所以一般采用二叉鏈表來存儲二叉樹统刮。將二叉樹的結(jié)點的最多有兩個孩子紊遵,所以為它設(shè)計一個數(shù)據(jù)域和兩個指針域。

lchild data rchild
指向左孩子的指針 數(shù)據(jù)域 指向右孩子的指針

代碼實現(xiàn):

/* 二叉樹的二叉鏈表結(jié)點結(jié)構(gòu)定義 */
typedef int  ElemeType;
typedef struct  BiTNode{ // 結(jié)點結(jié)構(gòu)
    ElemeType data; // 結(jié)點數(shù)據(jù)
    struct BiTNode * lchild; // 左孩子指針
    struct BiTNode * rchild; // 右孩子指針
}BiTNode, *BiTree;
二叉鏈表

四侥蒙、二叉樹的遍歷

二叉樹的遍歷定義

二叉樹的遍歷是指從根節(jié)點出發(fā)暗膜,按照某種次序訪問二叉樹中所有結(jié)點,使得每個結(jié)點被訪問一次且僅被訪問一次鞭衩。

二叉樹的遍歷方式有很多学搜,如果我們限制了從左到右的習(xí)慣方式,那么主要分為四種:

  • 前序遍歷
  • 中序遍歷
  • 后序遍歷
  • 層序遍歷

前序遍歷

  • 若二叉樹為空醋旦,則空操作返回恒水;
  • 若不為空,則先訪問根結(jié)點饲齐,然后前序遍歷左子樹钉凌,在前序遍歷右子樹。
  • 下圖的前序遍歷結(jié)果為:ABDGHCEIF


    前序遍歷

中序遍歷

  • 若二叉樹為空捂人,則空操作返回御雕;
  • 若不為空,則從根節(jié)點開始(注意并不是先訪問根結(jié)點)滥搭,中序遍歷根節(jié)點的左子樹酸纲,然后訪問根節(jié)點,最后中序遍歷右子樹瑟匆。
  • 下圖的中序遍歷結(jié)果是:GDHBAEICF


    中序遍歷

后序遍歷

  • 若二叉樹為空闽坡,則空操作返回;
  • 若不為空,則從左到右先葉子后結(jié)點的方式遍歷訪問左右子樹疾嗅,最后訪問根節(jié)點外厂。
  • 下圖的后序遍歷結(jié)果為:


層序遍歷

  • 若二叉樹為空,則空操作返回代承;
  • 若不為空汁蝶,則從樹的第一層(也就是根結(jié)點)開始訪問,從上到下逐層遍歷论悴,在同一層中掖棉,從左到右的順序?qū)Y(jié)點逐個訪問。
  • 下圖的層序遍歷結(jié)果為:ABCDEFGHI


    層序遍歷

代碼實現(xiàn)

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100 

typedef char ElemType;

typedef struct BiTNode{
    
    char data;  // 數(shù)據(jù)域
    struct BiTNode * lchild;  // 指向左孩子的指針
    struct BiTNode * rchild; // 指向右孩子的指針
}BiTNode, *BiTree;

/**
 * 按照前序輸入的方式構(gòu)造二叉樹
 */
void CreatBitree(BiTree *T){
    
    char c = '\0';
    
    scanf("%c", &c);
    
    if (c == '#') {  // 空結(jié)點
        *T = NULL;
        
    }else{
        
        *T = (BiTNode *)malloc(sizeof(BiTNode));
        if (!T)
            exit(0);
        (*T)->data = c;  // 生成根節(jié)點
        CreatBitree(&(*T)->lchild);  // 構(gòu)造左子樹
        CreatBitree(&(*T)->rchild);  // 構(gòu)造右子樹
    }
}


/**
 * 二叉樹的前序遞歸遍歷
 */
void PreOrderTraverse(BiTree T){
    
    if (T == NULL)  // 空樹
        return;
    printf("%c", T->data);  // 顯示結(jié)點數(shù)據(jù)
    
    PreOrderTraverse(T->lchild);  // 先序遍歷左子樹
    PreOrderTraverse(T->rchild);  // 再先序遍歷右子樹
}

/**
 * 二叉樹的中序遞歸遍歷
 */
void InOrderTraverse(BiTree T){
    
    if (T == NULL)  // 空樹
        return;
    InOrderTraverse(T->lchild);  // 中序遍歷左子樹
    
    printf("%c", T->data);  // 顯示結(jié)點數(shù)據(jù)
    
    InOrderTraverse(T->rchild); // 最后中序遍歷右子樹
}

/**
 * 二叉樹的后序遞歸遍歷
 */
void PostOrderTraverse(BiTree T){
    
    if (T == NULL)  // 空樹
        return;
    
    PostOrderTraverse(T->lchild);  // 先后序遍歷左子樹
    PostOrderTraverse(T->rchild);  // 再后序遍歷右子樹
    printf("%c", T->data);  // 顯示結(jié)點數(shù)據(jù)
}

測試代碼

int main(int argc, const char * argv[]) {
    
    printf("請按照先序輸入二叉樹的結(jié)點膀估,空結(jié)點用#表示, 回車結(jié)束\n");

    BiTree T = NULL;
    CreatBitree(&T);
    
    printf("該二叉樹的前序遍歷結(jié)果為:\n");
    PreOrderTraverse(T);
    printf("\n");
    
    printf("該二叉樹的中序遍歷結(jié)果為:\n");
    InOrderTraverse(T);
    printf("\n");
    
    printf("該二叉樹的后序遍歷結(jié)果為:\n");
    PostOrderTraverse(T);
    printf("\n");

    return 0;
}

擴展:遍歷二叉樹的同時輸出該節(jié)點所在的層數(shù)

在二叉樹的幾種遍歷方式的代碼中幔亥,為了輸出該節(jié)點的數(shù)據(jù),會有一個printf玖像,要輸出該節(jié)點的層數(shù) 紫谷,就可以改變這里的代碼來實現(xiàn),比如在前序遞歸遍歷的同時輸出節(jié)點所在層數(shù)捐寥,可以將這個打印代碼寫在一個封裝在一個方法里面然后調(diào)用

代碼實現(xiàn)
/**
 * 打印該節(jié)點所在層數(shù)
 */
void visit(char c, int level){
    printf("%c 位于第 %d 層\n", c, level);
}
/**
 * 二叉樹的前序遞歸遍歷
 */
void PreOrderTraverse(BiTree T, int level){
    
    if (T == NULL)  // 空樹
        return;
//    printf("%c", T->data);  // 顯示結(jié)點數(shù)據(jù)
    visit(T->data, level);  // 調(diào)用寫好的打印方法
    
    PreOrderTraverse(T->lchild, level + 1);  // 先序遍歷左子樹
    PreOrderTraverse(T->rchild, level + 1);  // 再先序遍歷右子樹
}
int main(int argc, const char * argv[]) {
    
    printf("請按照先序輸入二叉樹的結(jié)點笤昨,空結(jié)點用#表示, 回車結(jié)束\n");

    int level = 1;
    BiTree T = NULL;
    CreatBitree(&T);
    
    printf("該二叉樹的前序遍歷結(jié)果為:\n");
    PreOrderTraverse(T, level);
    printf("\n");
    return 0;
}
結(jié)果輸出
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市握恳,隨后出現(xiàn)的幾起案子瞒窒,更是在濱河造成了極大的恐慌,老刑警劉巖乡洼,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崇裁,死亡現(xiàn)場離奇詭異,居然都是意外死亡束昵,警方通過查閱死者的電腦和手機拔稳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锹雏,“玉大人巴比,你說我怎么就攤上這事〗缸瘢” “怎么了轻绞?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長佣耐。 經(jīng)常有香客問我政勃,道長,這世上最難降的妖魔是什么兼砖? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任奸远,我火速辦了婚禮既棺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘懒叛。我一直安慰自己援制,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布芍瑞。 她就那樣靜靜地躺著,像睡著了一般褐墅。 火紅的嫁衣襯著肌膚如雪拆檬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天妥凳,我揣著相機與錄音竟贯,去河邊找鬼。 笑死逝钥,一個胖子當(dāng)著我的面吹牛屑那,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播艘款,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼持际,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了哗咆?” 一聲冷哼從身側(cè)響起蜘欲,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晌柬,沒想到半個月后姥份,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡年碘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年澈歉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屿衅。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡埃难,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出傲诵,到底是詐尸還是另有隱情凯砍,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布拴竹,位于F島的核電站悟衩,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏栓拜。R本人自食惡果不足惜座泳,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一惠昔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挑势,春花似錦镇防、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至香拉,卻和暖如春啦扬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背凫碌。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工扑毡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盛险。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓瞄摊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親苦掘。 傳聞我的和親對象是個殘疾皇子换帜,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

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