1. 程式人生 > 實用技巧 >SDUST 2020/資料結構/期末集合.part3

SDUST 2020/資料結構/期末集合.part3

07 樹

1、(4)樹的型別定義

①基本概念:

結點:資料元素+若干指向子樹的分支

結點的度:分支的個數(子樹的個數)

樹的度:樹中所有結點的度的最大值

葉子結點:度為0的結點

分支結點:度大於0的結點(包含根和中間結點)

深度:對任意結點ni,ni的深度等於根到該點的唯一路徑的長

高度:對任意結點ni,其高度等於該點到一片樹葉的最長的路徑

寬度:

結點的層次:假設根節點在第一層,那麼其孩子結點在第二層,以此類推

②樹的基本分類

有向樹:有確定的根、樹根與子樹之間是有向關係

有序樹:子樹之間存在確定的次序關係

無序樹:子樹之間不存在確定的次序關係

樹和森林:

③基本操作

2、二叉樹

①定義

二叉樹:或為空樹,或是由一個根結點加上兩棵分別稱為左子樹和右子樹的、互不交的二叉樹組成(樹的度最大為2)。

二叉樹的五種基本形態:空樹、只含根節點、左子樹為空樹、右子樹為空樹、左右都不為空樹

②重要特性

對於結點為n的二叉樹,它的分支樹b=n-1;

任意的二叉樹中葉子節點都比度為2的節點多1

在二叉樹的第二層至多有 2i-1 個結點;

深度為k的二叉樹上至多含有 2k-1 個結點;

對任何一個二叉樹,若它有n0個葉子結點,n2個度為2的結點,那麼必存在關係式n0=n2+1;

證明:設二叉樹結點總數是n=n0+n1+n2

則分支總數為b=n1+2xn2

又因為b=n-1。聯立得出結論。

具有n個結點的完全二叉樹的深度為 ⌊log2n⌋+1

③二叉樹的分類

滿二叉樹:深度為k且有2k-1個結點,即滿樹葉的二叉樹

完全二叉樹:樹中所含的n個結點和滿二叉樹中編號為1到n的結點一一對應

特點:葉子結點出現在最後兩層;對於任意結點,若其右分支下的子孫最大層次為L,則左分支下的子孫的最大層次為L或L+1

④二叉樹的儲存結構

♦順序儲存方式

♦鏈式儲存結構

二叉連結串列:這是解題時沒有特殊情況最常用的結構

typedef struct BiTNode
{
    TElemType data;
    struct
BiTNode *lchild,*rchild; }BiTNode,*BiTree;

三叉連結串列:

typedef struct TriTNode
{
    TElemType data;
    struct TriTNode *lchild,*rchild;
    struct TriTNode *parent;
    
}TriTNode,*TriTree;

雙親連結串列:

3、二叉樹的遍歷

單獨把遍歷拿出來說一下

遍歷一共有三種方式:先序、中序、後序,先後都是相對於根節點講的

如下一棵樹,先序遍歷是-+a*b-cd/ef,後序遍歷是a+b*c-d-e/f,中序遍歷是abcd-*+ef/-

總結:無論先序、中序、後序遍歷二叉樹,遍歷時的搜尋路線是相同的:從根結點出發,逆時針延二叉樹外緣移動對每個結點均途徑三次。
先序遍歷:第一次經過結點時訪問。
中序遍歷:第二次經過結點時訪問。
後序遍歷:第三次經過結點時訪問。

②演算法遞迴的描述

如下是先中後序的遞迴演算法

void preorderT(BiTree T)
{
    if(T)
    {
        printf("%d",T->data);
        preorderT(T->lchild);
        preorderT(T->rchild);
    }
}

void inorderT(BiTree T)
{
    if(T)
    {
        inorderT(T->lchild);
        printf("%d",T->data);
        inorderT(T->rchild);
    }
}

void postorderT(BiTree T)
{
    if(T)
    {
        postorderT(T->lchild);
        postorderT(T->rchild);
        printf("%d",T->data);
    }
}

特殊地,出現層次遍歷:

void LevelorderTraversal( BinTree BT )//逐層遍歷
{
    if(!BT)
        return;
    BinTree a[10000],b;
    a[0]=BT;
    int len=1;//len記錄當前層的節點數量
    while(1)
    {
        if(len==0)
            return;
        int pos=0;
        BinTree b[10000];

        for(int i=0; i<len; i++)
        {
            if(a[i]!=NULL)
                printf(" %c",a[i]->Data);
            if(a[i]->Left!=NULL)
                b[pos++]=a[i]->Left;
            if(a[i]->Right!=NULL)
                b[pos++]=a[i]->Right;
        }

        len=pos;//更新下一層寬度,為下一次迴圈做準備
        for(int i=0; i<len; i++) //將下層的b賦給a,為下一次迴圈做準備
            a[i]=b[i];
    }
}

④應用舉例

在樹的整章學習中,遞迴的思想極大地簡化了解題步驟,而涉及遍歷的題目,大都與遞迴有關

♦先序輸出葉結點/求葉子結點的個數:

void PreorderPrintLeaves( BinTree BT )
{
    if(BT)
    {
        if(BT->Left==NULL&&BT->Right==NULL)
            printf(" %c",BT->Data);
        else
        {
            if(BT->Left)
                PreorderPrintLeaves(BT->Left);
            if(BT->Right)
                PreorderPrintLeaves(BT->Right);
        }
    }
}
int LeafCount(Bitree T)
{
    if(!T)
        return 0;
    else if(!T->lchild&&!T->rchild)
        return 1;
    else
        return LeafCount(T->lchild)+LeafCount(T->rchild);
}

♦求二叉樹的深度

經典題目。關鍵是要搞清楚深度的定義,即左右子樹深度的最大值+1。演算法仍是遞迴。

int BitreeDepth(Bitree T)
{
    if(!T)
        depth=0;
    else
    {
        depthleft=BitreeDepth(T->lchild);
        depthright=BitreeDepth(T->rchild);
        depth=1+(depthleft>depthright?depthleft:depthright);
    }
    return depth;
}

♦建立二叉樹的儲存結構

以字串的形式建樹

Status Creatbitree (Bitree *T)
{
    scanf("%s",ch);
    if(ch=='')
        T=NULL;
    else
    {
        if(!(T=(BiTNode*)malloc(sizeof(BiTNode))))d
            exit(OVERFLOW);
        T->data=ch;
        Creatbitree(T->lchild);
        Creatbitree(T->rchild);
    }
    return OK;
}

按給定的表示式建樹

scanf(&ch);//由字首表示式建樹
if(In(ch,字符集))
    建立葉子結點;
else
{
    建立根節點;
    遞迴左子樹;
    遞迴右子樹;
}

根據後續和中序遍歷輸出先序遍歷

eg

4、線索二叉樹

對線索連結串列中結點的約定:
在二叉連結串列的結點中增加兩個標誌域,並作如下規定:
♦若該結點的左子樹不空,則lchild域的指標指向其左子樹,且左標誌域的值為“Link(指標)”;否則,lchild域的指標指向其“前驅”,且左標誌的值為“Thread(線索)”
♦若該結點的右子樹不空,則rchild域的指標指向其右子樹,且右標誌域的值為“Link(指標)”;否則,rchild域的指標指向其“後繼”,且右標誌的值為“Thread(線索)”。
如此定義的二叉樹的儲存結構稱作“線索連結串列”。