樹和森林


一.樹的定義

  • 有且僅有一個特點的稱為根的節點
  • 當n>1時,其余節點可分為m(m>0)個互不相干的有限交集,每個交集稱為根的子樹。

二.森林的定義

  • m個互不相交的森林樹的集合,子樹的集合稱為子樹的森林。

三.樹的存儲結構

1.雙親表示法

  • 在樹中,除了根節點沒有雙親外,其他節點的雙親的唯一確定的。

//雙親存儲的結構類型定義
typedef struct PTNode{
  TElemType data; //數據域
  int parent;     //雙親的位置,根節點的雙親為-1
}PTNode;        //雙親的節點類型
typedef struct {
  PTNode *nodes;  //初始化分配的結點數組
  int r,nodeNum; //根的位置和結點數
}PTree;         //樹的雙親存儲結構類型

優點: 可用parent直接找到雙親,並且很容易找到祖先。

缺點:需要查找節點的孩子及其子孫時要遍歷整個結構。

2.雙親孩子表示法

  • 是對雙親表示法的擴展,為各節點構造孩子單鏈表,以便於訪問結點的孩子及其子孫,在結點的數組元素中增加firstchild域作為結點的孩子鏈表頭的頭指針。

//雙親孩子存儲結構的類型定義
typedef struct ChildNode{
  int childIndex;       //孩子在結點數組的位置
  struct ChildNode *nextChild;  //下一個孩子
}ChildNode;         //孩子鏈表中的節點類型
​
typdef  struct{
  TElemType data;       //元素值
  int parent;           //雙親的位置
  struct ChildNode *firstChild; //孩子鏈表頭指針
}PCTreeNode;            //雙親節點的節點類型
​
typedef struct{
  PCTreeNode *nodes;    //節點數組
  int nodeNum,r;    //結點元素的個數,根位置
}PCTree;            //樹的雙親孩子存儲結構類型

 

3.孩子兄弟表示法

  • 在樹中,結點的最左孩子(第一個孩子)和右兄弟如果存在則都是唯一的。采取二叉鏈式存儲結構,每個結點包含三個域,元素值data,最左邊孩子指針firstchild和右兄弟指針nextsibling。

//孩子兄弟鏈表的類型定義
typedef struct CSTNode{
    TElemType data;         //數據域
    struct CSTNode *firstChild,*nextSibling; //最左孩子指針,右兄弟指針
}CSTnode,*CSTree,*CSForest;     //孩子兄弟鏈表

 

4.孩子兄弟表示法的樹的接口

Status InitTree(CSTree &T); //構造空樹
CSTree MakeTree(TElemType e,int n....)  //創建根結點為e和n顆子樹的樹
Status DestroyTree(CSTree &T)  //銷毀樹
int TreeDepth(CSTree T)  //返回樹的深度
CSNode *Search(CSTree T,TElemType e); //查找樹T中的節點e並返回其指針
Status InesertChild(CSTree &T,int i,CSTree c) //插入c為T的第i顆子樹,c非空並且與T不相交
Status DeleteChild(CSTree &T,int i) //刪除第i棵子樹

1.創建樹

#include<stdarg.h>// 標准頭文件,提供宏va_start、va_arg和va_end,
​
CSTree MakeTree(TElemType e,int n....){
  int i;
  CSTree t,p,pi;
  va_list argptr; //存放變長參數表信息的數組
  t=(CSTree)malloc(sizeof(CSTNode));
  if(t=NULL) return NULL;
  t->data=e;    //根結點的值為e;
  t->firstChild=t->nextSibling=NULL;
  if(n<=0) return t;  //若無子樹,則返回根結點
  va_start(argptr,n) //令argptr 指向n后的第一個實參
  p=va_arg(argptr,CSTree); //取第一棵子樹的實參轉化為CSTree類型
  t->firstChild=p;
  pi=p;
  for(i=1;i<n;i++){
    p=va_arg(argptr,CSTree); //取下一顆子樹的實參並轉換為CSTree類型
    pi->nextSibling=p;
    pi=p;
  }
  va_end(argptr);
  return t;
}

//可變參數的使用
使用可變參數應該有以下步驟(要加入<stdarg.h>): 

1)首先在函數里定義一個va_list型的變量,這里是argptr,這個變 量是指向參數的指針. 

2)然后用va_start宏初始化變量argptr,這個宏的第二個參數是第一個可變參數的前一個參數,是一個固定的參數. 

3)然后用va_arg返回可變的參數,並賦值給變量p(CSTree類型). va_arg的第二個 參數是你要返回的參數的類型,這里是CSTree類型.然后你就可以在函數里使用第二個參數了.如果函數有多個可變參數的,依次調用va_arg獲取各個參數. 

4)最后用va_end宏結束可變參數的獲取.

2.插入第i個子樹

 
        
Status InesertChild(CSTree &T,int i,CSTree c){
  int j;
  CSTree p;
  if(NULL==T||i<1) return ERROR;
  if(i==1){ //c為第一棵插入子樹
    c->nextSibling=T->firstChild;
    T->firstChild=c; //T成為T的第一棵子樹
  }else{
    p=T->firstChild;
    for(j=2;p!=NULL&&j<i;j++){
      p=p->nextSibling; //尋找插入位置
    }
    if(j==i){
      c->nextSibling = p->nextSibling;
      p->nextSibling = c;
    }else return ERROR;
  }
  return OK;
}

3.求樹的深度

 
        
int TreeDepth(CSTree T) {  // 求樹T的深度  
    int dep1, dep2, dep;
    if(NULL==T) dep = 0; // 樹為空,深度則為0
    else {   
        dep1 = TreeDepth(T->firstChild);    // 求T的子樹森林的深度
        dep2 = TreeDepth(T->nextSibling);   // 求除T所在樹以外的其余樹的深度
        dep = dep1+1>dep2 ? dep1+1 : dep2;  // 樹的深度 
     }
     return dep;
} 

4.查找樹

CSTreeNode* Search(CSTree T, TElemType e) {  
    // 查找樹T中的結點e並返回其指針
    CSTreeNode* result = NULL;       
    if(NULL==T) return NULL;  // 樹為空,返回NULL 
    if(T->data==e) return T;  // 找到結點,返回其指針
    if((result = Search(T->firstChild, e))!=NULL) // 在T的子樹森林查找
        return result; 
    return Search(T->nextSibling, e); 
        // 在除T所在樹以外的其余樹構成的森林查找
}
​

 四.樹的遍歷

1.三種遍歷

1.先序(次序)遍歷

  • 若樹不空,則先訪問根結點,然后依次先序遍歷各棵子樹。

2.后序(次序)遍歷

  • 若樹不空,則先依次后序遍歷各棵子樹,然后訪問根結點。

3.層序遍歷

  • 若樹不空,則自上而下自左至右訪問樹中每個結點。

2.樹的先序遍歷

若樹不為空,則依次進行以下操作:

(1)訪問根結點;

(2)訪問根結點T所在的子樹森林;

(3)除根結點所在樹以外的其余樹構成的森林。

Status PreOrderTraverseTree(CSTree T, Status(*visit)(TElemType)) {
      if(NULL==T) return OK;//空樹
      if(ERROR==visit(T->data)) return ERROR; //訪問根節點
      if(ERROR==PreOrderTraverseTree(T->firstChild,visit)) //訪問左樹
          return ERROR;
      return PreOrderTraverseTree(T->nextSibling,visit);    // 遞歸訪問除T所在樹以外的其余樹構成的森林
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM