1. 程式人生 > >資料結構與演算法 -- 二叉樹鏈式詳解((非)/遞迴遍歷,葉子個數,深度計算)

資料結構與演算法 -- 二叉樹鏈式詳解((非)/遞迴遍歷,葉子個數,深度計算)

前言

PS:樹型結構是一種重要的非線性資料結構,教科書上一般都是樹與二叉樹,由此可見,樹和二叉樹是有區別和聯絡的,網上有人說二叉樹是樹的一種特殊形式,但經過查資料,樹和二叉樹沒有一個肯定的說法,但唯一可以肯定都是樹型結構。但是按照定義來看二叉樹並不是樹的一種特殊形式(下面解釋)。樹型資料結構的作用可以表示資料元素之間一對多的關係,一個公司裡的各個部門都可以用樹形來表示。

二叉樹不是樹的一種特殊情形,主要差別:

  1. 樹中結點的最大度數沒有限制,而二叉樹結點的最大度數為2;
  2. 樹的結點無左、右之分,而二叉樹的結點有左、右之分。

二叉樹型別

(1)完全二叉樹 ——若設二叉樹的高度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第h層有

葉子結點,並且葉子結點都是從左到右依次排布,這就是完全二叉樹

(2)滿二叉樹 ——除了葉結點外每一個結點都有左右子葉且葉子結點都處在最底層的二叉樹。

(3)平衡二叉樹——平衡二叉樹又被稱為AVL樹(區別於AVL演算法),它是一棵二叉排序樹,且具有以下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹

二叉樹的鏈式

1:結構體

typedef struct twoCha {
    char data;
    struct twoCha *Lchild, *Rchild;//左右孩子結點
} twoCha, *twoChaL;

2:建立二叉樹

當我們在鍵盤上敲了一行的char型別資料,可以按照一定的結構儲存在計算機上,就要寫一個演算法,二叉樹分左右孩子,所以,如果沒有左(右)孩子就輸入一個空格或者#,代表空。如果有左(右)孩子就新建一個結點,來存放該結點的資料data。

複製程式碼

int createTwo(twoChaL &tL) {
    char ch;
    scanf("%c",&ch);
    if (ch == '#')
    {
        tL = NULL;
        return 0;
    }
    else
    {
        tL= (twoChaL)malloc(sizeof(twoCha));
        if (tL == NULL)
        {
            printf("錯誤tl=NULL\n");
            return 0;
        }
        else
        {
            tL->data = ch;
            createTwo(tL->Lchild);
            createTwo(tL->Rchild);
        }
    }
    return 0;
}

複製程式碼

3:遞迴遍歷

畫的不好,湊活著看吧。

我們以這個二叉樹為例子,來分析他的遍歷形式,遍歷分為三種

  • 先序遍歷 -- 根左右 -- ABDCE
  • 中序遍歷 -- 左根右 -- DBAEC
  • 後序遍歷 -- 左右根 -- DBECA

複製程式碼

void diGuiBianLi(twoChaL &tL,int xl)
{
    if (tL == NULL)
    {
        return;
    }
    else
    {
        if(xl == 1){
            //先序遍歷
            printf("%c ",tL->data);
            diGuiBianLi(tL->Lchild,xl);
            diGuiBianLi(tL->Rchild,xl);
        }else if(xl == 2){
            //中序遍歷
            diGuiBianLi(tL->Lchild,xl);
            printf("%c ",tL->data);
            diGuiBianLi(tL->Rchild,xl);
        }
        else if(xl == 3){
            //後序遍歷
            diGuiBianLi(tL->Lchild,xl);
            diGuiBianLi(tL->Rchild,xl);
            printf("%c ",tL->data);
        }
    }
}

複製程式碼

遞迴的程式碼非常少,但是一開始接觸 理解起來還是比較難的,當然,你一旦理解後,那就非常簡單了。遞迴的思想就是把一個大問題分成多個類似的小問題解決。

4:葉子個數

方法:查詢一個結點沒有左右孩子,就是葉子結點。可以定義一個static int count變數,如果該結點沒有左右結點那麼就讓count++;

複製程式碼

int leafCount(twoChaL &tL)
{
    static int count;
    if (tL != NULL)
    {
        if (tL->Lchild == NULL && tL->Rchild == NULL)
        {
            count++;
        }
        leafCount(tL->Lchild);
        leafCount(tL->Rchild);
    }

    return count;
}

複製程式碼

5:深度

方法:看結點的左孩子和右孩子哪一個更長,當深入到最低結點時,左右孩子比較,誰大誰加一(相等左孩子加一)。

複製程式碼

int treeDeep(twoChaL &tL)
{
    int deep = 0;
    if (tL != NULL)
    {
        int leftdeep = treeDeep(tL->Lchild);
        int rightdeep = treeDeep(tL->Rchild);
        deep = leftdeep >= rightdeep?leftdeep+1:rightdeep+1;
    }
    return deep;
}

複製程式碼

 6:非遞迴遍歷

這裡以中序為例,利用棧的知識點(順序棧),首先把根節點進棧,然後依次左孩子進棧,直到左孩子為NULL時結束,但是NULL也是入棧的,然後再把NULL出棧,下一步就是把棧頂元素取出並列印,再把該棧頂元素的右孩子進棧,不管右孩子是不是NULL都要入棧,入棧之前把棧頂元素彈棧。

定義棧的data域,要用結點的結構體

複製程式碼

int top = -1;
//棧
void push(twoChaL *a,twoChaL elem){
    a[++top]=elem;
}
//彈棧函式
void pop(){
    if (top==-1) {
        return ;
    }
    top--;
}
//拿到棧頂元素
twoChaL getTop(twoChaL *a){
    return a[top];
}

void zhongXu2(twoChaL Tree){
    //順序棧
    twoChaL a[100];
    twoChaL p;
    push(a, Tree);//根結點進棧
    while (top!=-1) {//top!=-1說明棧內不為空
        while ((p=getTop(a)) &&p){//取棧頂元素,且不能為NULL
            push(a, p->Lchild);//將該結點的左孩子進棧,如果沒有左孩子,NULL進棧
        }
        pop();//跳出迴圈,棧頂元素肯定為NULL,將NULL彈棧
        /*
        //測試資料,主要觀察棧中NULL出棧後,還有什麼在棧中(棧頂),
          if(a[top]){
            printf(" 跳出迴圈 %c\n",a[top]->data);
        }*/

        if (top!=-1) {
            p=getTop(a);//取棧頂元素
            pop();//棧頂元素彈棧
            printf("%c ",p->data);
            push(a, p->Rchild);//將p指向的結點的右孩子進棧
        }
    }
}

複製程式碼

結果圖:

 

鄭州男科醫院

鄭州治療不孕不育哪家好

鄭州男科醫院哪個較好

鄭州看婦科哪家好