1. 程式人生 > >資料結構入門-樹的遍歷以及二叉樹的建立

資料結構入門-樹的遍歷以及二叉樹的建立

樹定義:

  1. 有且只有一個稱為根的節點
  2. 有若干個互不相交的子樹,這些子樹本身也是一個樹

通俗的講:

  1. 樹是有結點和邊組成,
  2. 每個結點只有一個父結點,但可以有多個子節點
  3. 但有一個節點例外,該節點沒有父結點,稱為根節點

一、專業術語

結點、父結點、子結點、根結點

深度:從根節點到最底層結點的層數稱為深度,根節點第一層

葉子結點:沒有子結點的結點

非終端節點:實際上是非葉子結點

度:子結點的個數成為度

二、樹的分類

一般樹:任意一個結點的子結點的個數都不受限制

二叉樹:任意一個結點的子結點個數最多是兩個,且子結點的位置不可更改

二叉數分類:

  1. 一般二叉數
  2. 滿二叉樹:在不增加樹層數的前提下,無法再多新增一個結點的二叉樹
  3. 完全二叉樹:如果只是刪除了滿二叉樹最底層最右邊的連續若干個結點,這樣形成的二叉樹就是完全二叉樹

森林:n個互不相交的樹的集合

三、樹的儲存

二叉樹儲存

連續儲存(完全二叉樹)

優點:查詢某個結點的父結點和子結點(也包括判斷有沒有子結點)速度很快

缺點:耗用記憶體空間過大

鏈式儲存

一般樹儲存

  1. 雙親表示法:求父結點方便

  2. 孩子表示法:求子結點方便

  3. 雙親孩子表示法:求父結點和子結點都很方便

  4. 二叉樹表示法:把一個一般樹轉化成一個二叉樹來儲存,

    • 具體轉換方法:
    • 設法保證任意一個結點的左指標域指向它的第一個孩子,右指標域指向它的兄弟,只要能滿足此條件,就可以把一個一般樹轉化為二叉樹

    一個普通樹轉換成的二叉樹一定沒有右子樹

森林的儲存

先把森林轉化為二叉樹,再儲存二叉樹

四、樹的遍歷

先序遍歷:根左右

先訪問根結點,再先序訪問左子樹,再先序訪問右子樹

中序遍歷:左根右

中序遍歷左子樹,再訪問根結點,再中序遍歷右子樹

後續遍歷:左右根

後續遍歷左子樹,後續遍歷右子樹,再訪問根節點

五、已知兩種遍歷求原始二叉樹

給定了二叉樹的任何一種遍歷序列,都無法唯一確定相應的二叉樹,但是如果知道了二叉樹的中序遍歷序列和任意的另一種遍歷序列,就可以唯一地確定二叉樹

已知先序和中序求後序

先序:ABCDEFGH

中序:BDCEAFHG

求後序: 這個自己畫個圖體會一下就可以了,非常簡單,這裡簡單記錄一下

  1. 首先根據先序確定根,上面的A就是根
  2. 中序確定左右,A左邊就是左樹(BDCE),A右邊就是右樹(FHG)
  3. 再根據先序,A左下面就是B,然後根據中序,B左邊沒有,右邊是DCE
  4. 再根據先序,B右下是C,根據中序,c左下邊是D,右下邊是E,所以整個左樹就確定了
  5. 右樹,根據先序,A右下是F,然後根據中序,F的左下沒有,右下是HG,
  6. 根據先序,F右下為G,然後根據中序,H在G的左邊,所以G的左下邊是H

再來一個例子,和上面的思路是一樣的,這裡就不詳細的寫了

先序:ABDGHCEFI

中序:GDHBAECIF

已知中序和後序求先序

中序:BDCEAFHG

後序:DECBHGFA

這個和上面的思路是一樣的,只不過是反過來找,後序找根,中序找左右

樹簡單應用

樹是資料庫中資料組織一種重要形式

作業系統子父程序的關係本身就是一棵樹

面嚮物件語言中類的繼承關係

哈夫曼樹

六、二叉樹的建立

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

typedef struct Node
{
    char data;
    struct Node * lchild;
    struct Node * rchild;
}BTNode;

/*
二叉樹建立
*/
void BuildBT(BTNode ** tree)
{
    char ch;
    scanf("%c" , &ch); // 輸入資料
    if(ch == '#')  // 如果這個節點的資料是#說明這個結點為空
        *tree = NULL;
    else
    {
        *tree = (BTNode*)malloc(sizeof(BTNode));//申請一個結點的記憶體
        (*tree)->data = ch; // 將資料寫入到結點裡面
        BuildBT(&(*tree)->lchild); // 遞迴建立左子樹
        BuildBT(&(*tree)->rchild); // 遞迴建立右子樹
    }
}

/*
二叉樹銷燬
*/
void DestroyBT(BTNode *tree) // 傳入根結點
{
    if(tree != NULL)
    {
        DestroyBT(tree->lchild);
        DestroyBT(tree->rchild);
        free(tree);  // 釋放記憶體空間
    }
}

/*
二叉樹的先序遍歷
*/
void Preorder(BTNode * node)
{
    if(node == NULL)
        return;
    else
    {
        printf("%c ",node->data );
        Preorder(node->lchild);
        Preorder(node->rchild);
    }
}

/*
二叉樹的中序遍歷
*/
void Inorder(BTNode * node)
{
    if(node == NULL)
        return;
    else
    {
        
        Inorder(node->lchild);
        printf("%c ",node->data );
        Inorder(node->rchild);
    }
}


/*
二叉樹的後序遍歷
*/
void Postorder(BTNode * node)
{
    if(node == NULL)
        return;
    else
    {
        
        Postorder(node->lchild);
        Postorder(node->rchild);
        printf("%c ",node->data );
    }
}

/*
二叉樹的高度
樹的高度 = max(左子樹高度,右子樹高度) +1
*/
int getHeight(BTNode *node)
{
    int Height = 0;
    if (node == NULL)
        return 0;
    else
    {
        int L_height = getHeight(node->lchild);
        int R_height = getHeight(node->rchild);
        Height = L_height >= R_height ? L_height +1 : R_height +1;
    }

    return Height;
}


int main(int argc, char const *argv[])
{
    BTNode * BTree; // 定義一個二叉樹
    printf("請輸入一顆二叉樹先序序列以#表示空結點:");
    BuildBT(&BTree);

    printf("先序序列:");
    Preorder(BTree);
    printf("\n中序序列:");
    Inorder(BTree);
    printf("\n後序序列:");
    Postorder(BTree);

    printf("\n樹的高度為:%d" , getHeight(BTree));
    return 0;
}
// ABC##DE##F##G##