樹的雙親表示法,孩子表示法以及孩子兄弟表示法


  如下圖所示,這是一棵普通的樹,該如何存儲呢?通常,存儲具有普通樹結構數據的方法有 3 種:
  雙親表示法;
  孩子表示法;
  孩子兄弟表示法;
在這裏插入圖片描述
                    圖1

樹的雙親表示法

  雙親表示法採用順序表(也就是數組)存儲普通樹,其實現的核心思想是:順序存儲各個節點的同時,給各節點附加一個記錄其父節點位置的變量
  注意,根節點沒有父節點(父節點又稱爲雙親節點),因此根節點記錄父節點位置的變量通常置爲 -1。
在這裏插入圖片描述
              圖2
  雙親表示法存儲普通樹代碼

/*
 * @Description: 樹的雙親表示法
 * @Version: V1.0
 * @Autor: Carlos
 * @Date: 2020-05-21 14:41:32
 * @LastEditors: Carlos
 * @LastEditTime: 2020-06-01 22:12:34
 */ 
#include<stdio.h>
#include<stdlib.h>
//宏定義樹中結點的最大數量
#define MAX_SIZE 20
//宏定義樹結構中數據類型
typedef char ElemType;
//結點結構
typedef struct Snode  
{
    //樹中結點的數據類型
    ElemType data;
    //結點的父結點在數組中的位置下標
    int parent;
}PNode;
//樹結構
typedef struct  
{
    //存放樹中所有結點
    PNode tnode[MAX_SIZE];
    //結點個數
    int n;                
}PTree;
/**
 * @Description: 節點初始化
 * @Param: PTree tree 結構體變量
 * @Return: PTree 結構體地址
 * @Author: Carlos
 */
PTree InitPNode(PTree tree)
{
    int i,j;
    char ch;
    printf("請輸入節點個數:\n");
    scanf("%d",&(tree.n));

    printf("請輸入結點的值其雙親位於數組中的位置下標:\n");
    for(i=0; i<tree.n; i++)
    {
        fflush(stdin);
        scanf("%c %d",&ch,&j);
        tree.tnode[i].data = ch;
        tree.tnode[i].parent = j;
    }
    return tree;
}
/**
 * @Description: 查找樹中指定節點
 * @Param: PTree tree 結構體變量
 * @Return: 無
 * @Author: Carlos
 */
void FindParent(PTree tree)
{
    char a;
    int isfind = 0;
    printf("請輸入要查詢的結點值:\n");
    fflush(stdin);
    scanf("%c",&a);
    for(int i =0;i<tree.n;i++){
        if(tree.tnode[i].data == a){
            isfind=1;
            //找到父節點的下標數值
            int ad=tree.tnode[i].parent;
            printf("%c的父節點爲 %c,存儲位置下標爲 %d",a,tree.tnode[ad].data,ad);
            break;
        }
    }
    if(isfind == 0){
        printf("樹中無此節點");
    }
}

int main()
{
    PTree tree;
    tree = InitPNode(tree);
    FindParent(tree);
    return 0;
}

樹的孩子表示法

  孩子表示法存儲普通樹採用的是 “順序表+鏈表” 的組合結構,其存儲過程是:從樹的根節點開始,使用順序表依次存儲樹中各個節點,需要注意的是,與雙親表示法不同,孩子表示法會給各個節點配備一個鏈表,用於存儲各節點的孩子節點位於順序表中的位置。
  如果節點沒有孩子節點(葉子節點),則該節點的鏈表爲空鏈表。
  例如,使用孩子表示法存儲左圖中的普通樹,則最終存儲狀態如右圖所示:
在這裏插入圖片描述
                    圖3

/*
 * @Description: 樹的孩子表示法。三部分構成,鏈表,節點,樹
 * @Version: 
 * @Autor: Carlos
 * @Date: 2020-05-21 14:59:47
 * @LastEditors: Carlos
 * @LastEditTime: 2020-06-01 22:47:38
 */ 

#include<stdio.h>
#include<stdlib.h>
#define MAX_SIZE 20
#define TElemType char
typedef struct CTNode{
    //鏈表中每個結點存儲的不是數據本身,而是數據在數組中存儲的位置下標!!
    int child;
    struct CTNode * next;
}ChildPtr;
typedef struct {
    //結點的數據類型
    TElemType data;
    //孩子鏈表的頭指針
    ChildPtr* firstchild;
}CTBox;
typedef struct{
    //存儲結點的數組
    CTBox nodes[MAX_SIZE];
    //結點數量和樹根的位置
    int n,r;
}CTree;
/**
 * @Description: 孩子表示法存儲普通樹
 * @Param: CTree tree 樹的結構體變量
 * @Return: CTree tree 結構體變量
 * @Author: Carlos
 */
CTree InitTree(CTree tree){
    printf("輸入節點數量:\n");
    scanf("%d",&(tree.n));
    for(int i=0;i<tree.n;i++){
        printf("輸入第 %d 個節點的值:\n",i+1);
        fflush(stdin);
        scanf("%c",&(tree.nodes[i].data));
        tree.nodes[i].firstchild=(ChildPtr*)malloc(sizeof(ChildPtr));
        tree.nodes[i].firstchild->next=NULL;

        printf("輸入節點 %c 的孩子節點數量:\n",tree.nodes[i].data);
        int Num;
        scanf("%d",&Num);
        if(Num!=0){
            //帶頭結點的鏈表
            ChildPtr * p = tree.nodes[i].firstchild;
            for(int j = 0 ;j<Num;j++){
                ChildPtr * newEle=(ChildPtr*)malloc(sizeof(ChildPtr));
                newEle->next=NULL;
                printf("輸入第 %d 個孩子節點在順序表中的位置",j+1);
                scanf("%d",&(newEle->child));
                p->next= newEle;
                p=p->next;
            }
        }
    }
    return tree;
}
/**
 * @Description:查找節點
 * @Param: CTree tree 樹的結構體,char a 要查找的節點
 * @Return: 無
 * @Author: Carlos
 */
void FindKids(CTree tree,char a){
    int hasKids=0;
    for(int i=0;i<tree.n;i++){
        if(tree.nodes[i].data==a){
            ChildPtr * p=tree.nodes[i].firstchild->next;
            while(p){
                hasKids = 1;
                //打印所有孩子節點 p->child 孩子節點在數組中的位置
                printf("%c ",tree.nodes[p->child].data);
                p=p->next;
            }
            break;
        }
    }
    if(hasKids==0){
        printf("此節點爲葉子節點");
    }
}

int main()
{
    CTree tree;
    tree = InitTree(tree);
    //默認數根節點位於數組notes[0]處
    tree.r=0;
    printf("找出節點 F 的所有孩子節點:");
    FindKids(tree,'F');
    return 0;
}

樹的孩子兄弟表示法

  樹結構中,位於同一層的節點之間互爲兄弟節點。例如,圖1中的普通樹中,節點 A、B 和 C 互爲兄弟節點,而節點 D、E 和 F 也互爲兄弟節點。
  孩子兄弟表示法,採用的是鏈式存儲結構,其存儲樹的實現思想是:從樹的根節點開始,依次用鏈表存儲各個節點的孩子節點和兄弟節點
  因此,該鏈表中的節點應包含以下 3 部分內容:
  節點的值;
  指向孩子節點的指針;
  指向兄弟節點的指針;
在這裏插入圖片描述
                    圖4
  用 C 語言代碼表示節點結構爲:

#define ElemType char
typedef struct CSNode{
    ElemType data;
    struct CSNode * firstchild,*nextsibling;
}CSNode,*CSTree;

  以圖1爲例,使用孩子兄弟表示法進行存儲的結果如下圖所示:
在這裏插入圖片描述
                    圖5
  由圖5可以看到,節點 R 無兄弟節點,其孩子節點是 A;節點 A 的兄弟節點分別是 B 和 C,其孩子節點爲 D,依次類推。
  實現上圖中的 C 語言實現代碼也很簡單,根據圖中鏈表的結構即可輕鬆完成鏈表的創建和使用,因此不再給出具體代碼。
  接下來觀察圖 1 和圖 5。圖 1 爲原普通樹,圖5 是由圖 1 經過孩子兄弟表示法轉化而來的一棵樹,確切地說,圖5是一棵二叉樹。因此可以得出這樣一個結論,即通過孩子兄弟表示法,任意一棵普通樹都可以相應轉化爲一棵二叉樹,換句話說,任意一棵普通樹都有唯一的一棵二叉樹於其對應。
  因此,孩子兄弟表示法可以作爲將普通樹轉化爲二叉樹的最有效方法,通常又被稱爲"二叉樹表示法"或"二叉鏈表表示法"。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章