心中有“树”:数据结构之树详解
文章目錄
- 前言
 - (一)樹的基礎定義與表示
 - 1 樹的定義
 - 2 樹的圖示
 - 3 樹的邏輯結構表示法
 
- (二)二叉樹
 - 1 二叉樹定義
 - 2 二叉樹示意圖
 - 3 程序實現
 - (1)節點定義
 - (2)二叉樹的先序遍歷
 - (3)二叉樹非遞歸先序遍歷
 - (4)二叉樹層序遍歷
 - (5)計算二叉樹的深度
 
- 4 二叉樹算法設計題目
 - 1 查找元素為X的節點
 - 2 判斷二叉樹是否為完全二叉樹
 
- (三)樹
 - 1 樹的存儲結構
 - 2 存儲結構圖示
 - 3 樹的先序遍歷
 - 4 求樹的深度
 
- (四)總結
 - (五)參考文獻
 
前言
樹型結構是一類重要的非線性數據結構。其中以樹和二叉樹最為常用。樹狀結構在客觀世界中廣泛存在,如人類社會的族譜和社會組織機構等,都可以用樹來形象表示。同時在計算機領域中,樹可以用來表示源程序的語法結構,并用于加密信息。本文主要討論二叉樹極其各種操作與應用,并拓展樹的結構與簡單操作。
(一)樹的基礎定義與表示
1 樹的定義
樹(Tree)是n(n>=0)個節點的有限集。在任意一顆非空樹中:
 (1)有且僅有一個特定的根(Root)的節點;
 (2)n>1時其余節點可分為m(m>0)個互不相交的有限集合T1,T2,…Tm,其中每一個集合本身又是一顆樹,并且稱為根的子樹
 顯然,這是一個遞歸的定義。故對于樹的操作等也需要使用遞歸操作。
2 樹的圖示
 設有樹a,b,c,如上圖所示,其中a為空樹,b只有一個根節點為A,c的根節點為A,它又有三顆子樹,假設為T1,T2,T3,這三顆子樹又分別有自己的根B,C,D和相應的更小的子樹,依此類推。
3 樹的邏輯結構表示法
(1)層次表示法
 前面的樹的圖示所用的表示法即層次表示法,它可以直觀表示樹中個節點的層次關系。同時可以更好地理解關于樹的各種操作
 (2)嵌套表示法
 嵌套表示法采用集合形式表示邏輯結構,類似于集合中的韋恩圖,應用較少,此不過多介紹。
 (3)廣義表表示法
 廣義表利用根節點元素和括號的組合表示。括號外邊是根節點,括號里邊是每個子樹。如(2)中的圖示中 c 樹可以表示為 A(B(E(K,L),F),C(G),D(H(M),I,J)))。
(二)二叉樹
1 二叉樹定義
二叉樹是最常用的一種樹型結構,其特點是每個節點最多只有兩顆子樹(即二叉樹中不存在度大于2的節點),二叉樹的子樹有左右之分,次序不能任意顛倒。
2 二叉樹示意圖
二叉樹的順序表表示法較為繁瑣,且浪費空間,我們以二叉鏈表表示法為例:
 
 將左圖層次表示與節點的存儲結構關聯即右圖所示。每個節點有一個數據域,與兩個指針域,分別指向左孩子與右孩子。同時每個左孩子又是一顆子樹。在這里我們以 ^ 符號表示指針域為空(NULL)表示其沒有左(右)子樹。
3 程序實現
前面說了二叉樹的定義,接下來看一下關鍵的程序代碼。
(1)節點定義
二叉樹的二叉鏈表存儲結構中,節點擁有一個數據域存儲元素,此處元素類型我們以字符型為例,兩個指針域存儲左右孩子節點的地址。
二叉鏈表表示法
typedef struct BiTNode {char data; //數據元素BiTNode* lc, * rc;//分別指向左孩子 與 右}*BiTree;lc,rc分別為指向左右孩子節點的地址的指針。BiTNode為節點結構體類型,為了代碼編寫方便,同時我們將指向BiTNode的指針的類型定義為BiTree。BiTree相當于BiTNode*。同時二叉樹還有三叉鏈表表示法,其節點定義多了一個指向雙親的指針域。即BiTNode* parent,這樣可以更方便的尋找某個節點的雙親節點。
(2)二叉樹的先序遍歷
二叉樹的遍歷分為先序遍歷,中序遍歷,后序遍歷和層序遍歷。
 先序遍歷:
 (1)訪問根節點——D;
 (2)遍歷左子樹——L;
 (3)遍歷右子樹——R;
 先序遍歷簡稱為DLR遍歷,同理中序為LDR遍歷,后序為LRD遍歷。下面我們以先序遍歷程序為例,中序和后序僅需要調整程序順序即可。
其中visit為函數作為形參,為了使得代碼具有通用性。具體visit實現可以是最簡單的輸出,或者對于節點進行的一系列操作。函數作為形參參考下列代碼。
#include<iostream> int num[] = { 1,2,3,4 }; void Print(int m) { std::cout << m << std::endl; } void ArrayPrint(int num[], void visit(int)) {for (int i = 0; i < 4; i++)visit(num[i]); } int main() {ArrayPrint(num, Print); }(3)二叉樹非遞歸先序遍歷
由于遞歸實際是棧的壓棧出棧操作,所以我們可以將遞歸的先序遍歷改為非遞歸的棧操作。
 算法思路:從根節點開始,每訪問一個節點,按照前序遍歷規則走左子樹,如果系欸點的右子樹存在,那么將右指針入棧,以便正確遍歷相應子樹。非遞歸操作不常用,對二叉樹的操作基本采用遞歸形式容易理解。
(4)二叉樹層序遍歷
二叉樹層序遍歷需要用到隊列。即訪問根節點的同時,將其左右孩子一次入隊列,較為簡單。利用STL封裝的queue實現。代碼程序如下:
void QueuePreorder(BiTree T) {queue <BiTree> q;if(!T)q.push(T);while (!q.empty()) //隊列不為空判斷{visit(q.front()->data);if(!q.front()->lc)q.push(q.front()->lc);if(!q.front()->rc)q.push(q.front()->rc);q.pop(); //已遍歷節點出隊} }(5)計算二叉樹的深度
//求二叉樹的深度 int depth(BiTree T) {if (!T)return 0;int dl, dr;//分別表示左子樹深度與右子樹深度dl = depth(T->lc); dr = depth(T->rc);return dl > dr ? dl + 1 : dr + 1;//加一為 加上根節點所在深度 }4 二叉樹算法設計題目
1 查找元素為X的節點
題目:再二叉樹中查找是否有元素為X的節點,找到則返回節點地址,否則返回空指針
 分析:題目比較簡單。可以直接在二叉樹中遞歸查找,如果根是空,返回NULL。如果元素為X,返回地址。否則新建節點M接收左子樹返回的值,如果非空返回M。否則返回右子樹返回的值。同時遞歸時,我們只需要考慮當前一層的情況,具體內部遞歸回代實現可以不必詳細了解,否則很容易被繞進去。程序實現為
在這里我們需要用M來保存遞歸左子樹返回的值,否則若只有兩個TreeSearch的順序結構,則只有第二個TreeSearch起作用,左子樹是否右X不得而知。局部錯誤代碼為:
else {//錯誤TreeSearch(T->lc,e);TreeSearch(T->rc,e); }2 判斷二叉樹是否為完全二叉樹
完全二叉樹:深度為k的,有n個節點的二叉樹,當且僅當其每一個節點都與深度為k的滿二叉樹的1至n的節點一一對應時,稱為完全二叉樹。
 簡單來說,就是只可能最后一層節點是不滿的,同時節點應盡可能向左補充。
 算法思路:可以利用層序遍歷,首先將根節點入隊。當可以從隊列取出元素且不空,則將左右孩子入隊。當出現空指針結束循環。最后再用一個while循環如果遍歷到了非空元素則為非完全二叉樹,否則最終為完全二叉樹。(因為完全二叉樹只有最后一排可以空節點,也就是遇到空指針后,后面的層序遍歷只能都是空指針)。代碼實現為:
(三)樹
1 樹的存儲結構
此處推廣至更為普遍的樹結構,一個節點可以擁有多個孩子節點。常見表示法為雙親表示法、孩子表示法、孩子-兄弟表示法(二叉樹表示法),此處以更為常見的孩子兄弟表示法為例。
typedef struct CSNode {int data;CSNode* firstchild,*nextsibling;// }*Tree;2 存儲結構圖示
 孩子-兄弟表示法又稱為二叉樹表示法或二叉鏈表表示法。鏈表中節點的兩個指針域分別指向該節點的第一個孩子節點和下一個兄弟節點(即與第一個孩子節點在同一層)。
3 樹的先序遍歷
如果根節點為空,返回;否則訪問根節點。然后依次遍歷第一個孩子節點和它的兄弟節點(以其為根的子樹),此處可以用一個for循環。下面以fc代表第一個孩子節點指針域,ns代表兄弟節點指針域。程序實現為:
void TreePreorder(Tree T,void visit(TElemType)) {Tree p;if(!T)return;visit(T->data);for(p=T->fc;p;p=p->ns)TreePreorder(p,visit); }讀者可依據二叉樹的某些操作實現樹的一些操作,算法思路沒有太大變化。
4 求樹的深度
樹的深度為各個子樹的最大深度加一,可一次遍歷遞歸所有子樹,最后返回深度加一。程序實現:
int TreeDepth(Tree T) {int m1;Tree p;int m2;//記錄最大深度if(!T)return 0;for(m2=0,p=T->fc;p;p=p->ns)//循環求出子樹深度最大值,最后加上根節點層{m1=TreeDepth(p);//以p為根節點的子樹深度if(m1>m2)m2=m1;}return m2+1; }(四)總結
1 本篇文章總結了以二叉樹為主的樹的一些常用算法,包括對其的遍歷與一些算法設計題目。大部分通過遞歸實現,可在理解遞歸基礎上編寫相應算法。
 2 有關于樹的數據結構還有很多,例如Huffman樹,二叉排序樹等主要數據結構和算法,讀者可以自行學習,此處沒有詳細介紹。
 3 本章討論的樹是數據結構中非線性數據結構中很重要的一類,是一種層次模型的數據結構。它可以較好地描述數據間地層次關系,能對數據機構隨機再組織,故樹一般成為動態數據結構。
(五)參考文獻
【1】嚴蔚敏.吳偉民. 數據結構(C語言版). 北京:清華大學出版社,1997.
 【2】齊悅.夏克儉.姚琳. 數據結構、算法與應用. 北京:清華大學出版社.2015.
 【3】聶立新.桑兆陽.張華清. 數據結構與算法. 中國石油大學出版社.2017.
總結
以上是生活随笔為你收集整理的心中有“树”:数据结构之树详解的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: RocketMQ:消息存储机制详解与源码
 - 下一篇: 【洛谷】马的遍历--广度优先搜索(BFS