資料結構之二叉樹遞迴操作
二叉樹(binary tree)是n(n>=0)個結點的有限結合,該集合或者為空集(空二叉樹),或由一個根結點和兩棵互不相交的,分別稱為根結點的左子樹(left subtree)和右子樹(right subtree)的二叉樹組成。
二叉樹的特點:
1.每個結點最多有兩棵字樹,所以二叉樹中不存在度大於2的結點
2.二叉樹是有序的,其次序不能任意顛倒,即使樹中的某個結點只有一棵
字樹,也要區分它是左子樹還是右子樹。
下面介紹幾種特殊的二叉樹:
1.斜樹
所有結點都只有左子樹的二叉樹稱為左斜樹;所有結點都只有右子樹的二叉樹稱為右斜樹。是兩棵不同的二叉樹。如下示意圖。
2.滿二叉樹
在一棵二叉樹中,如果所有分支結點都存在左子樹和右字樹,並且所有葉子結點都在同一層上,這樣的二叉樹稱為滿二叉樹。如下示意圖。
3.完全二叉樹
對一棵具有n個結點的二叉樹按層序編號,如果編號為i(1<=i<=n)的結點與同樣深度的滿二叉樹中編號為i的結點在二叉樹中的位置完全相同,則這棵二叉樹稱為完全二叉樹。顯然一棵滿二叉樹必定是一棵完全二叉樹,完全二叉樹的特點是:
1.如果有度為1的結點,只可能有一個,且該結點只有左孩子。
2.葉子結點只能出現在最下面兩層,且最下層的葉子結點都集中在二叉樹左側連續的位置。
以上圖畫的不是太優雅,大家不要介意哈。
關於二叉樹的更多的性質,請讀者檢視維基百科()或者其他資料。
在中能用多種方法來構造二叉樹。常用的是順序儲存結構和二叉連結串列儲存結構。
順序儲存結構:
二叉樹可以用或線性表來儲存,而且如果這是,這種方法不會浪費空間。用這種緊湊排列,如果一個結點的索引為i,它的子結點能在索引2i+1和2i+2找到,並且它的父節點(如果有)能在索引floor((i-1)/2)找到(假設根節點的索引為0)。這種方法更有利於緊湊儲存和更好的,特別是在前序遍歷中。然而,它需要連續的,這樣在儲存高度為h的n個結點組成的一般普通樹時將會浪費很多空間。一種最極壞的情況下如果深度為h的二叉樹每個節點只有右孩子需要佔用2的h次冪減1,而實際卻只有h個結點,空間的浪費太大,這是順序儲存結構的一大缺點。
對於順序儲存結構,本文不作介紹,這種儲存結構的缺點導致物理空間的浪費,是我們難以接受的。下面重點介紹二叉連結串列儲存結構。
二叉連結串列儲存結構:
在使用或的程式語言中,二叉樹通常用樹結點結構來儲存。有時也包含指向唯一的父節點的指標。如果一個結點的子結點個數小於2,一些子結點指標可能為空值,或者為特殊的結點。 使用連結串列能避免順序儲存浪費空間的問題,演算法和結構相對簡單,但使用二叉連結串列,由於缺乏父鏈的指引,在找回父節點時需要重新掃描樹得知父節點的節點地址。
以上的儲存結構示意圖我想看的很清楚,這種結構減少了一定空間的浪費,但是這一定是最好的儲存結構麼,後續的文章會談及線索二叉連結串列。
對於二叉連結串列最重要的操作莫過於遍歷了,概括來說有遞迴和非遞迴兩種,本文介紹的是遞迴遍歷二叉樹。
一定要注意建立的二叉樹時的方法,上面也是透過遞迴的方法建立二叉樹,根據前中後序遍歷,很容易畫出這棵二叉樹:(如下字母即指結點又指結點資料域)
下面詳細的介紹是如何進行遞迴遍歷的,以上面構建的二叉樹為例:
前序遍歷:
呼叫PostOrderBiTree(BiTree &T),T根結點不為空,列印字元A
再呼叫PreOrderBiTree(T->leftChild),訪問根結點的左孩子,不為NULL,列印字元B
再次呼叫PreOrderBiTree(T->leftChild),訪問B結點的左孩子,不為空,列印C
再次呼叫PreOrderBiTree(T->leftChild),訪問結點C的左孩子,因為C沒有左孩子,T=NULL,返回函式。呼叫PreOrderBiTree(T->rightChild),C的右孩子也為空,此時返回到 上一級遞迴的函式,也就是列印B結點的函式;此時呼叫PreOrderBiTree(T->rightChild),B沒有右孩子,為空,此時返回到上一級遞迴的函式,就是列印A結點的函式;此時呼叫PreOrderBiTree(T->rightChild),列印了D;呼叫PreOrderBiTree(T->leftChild),因為D沒有左孩子,T=NULL,返回函式,呼叫了PreOrderBiTree(T->rightChild),D也沒有右孩子,T=NULL,返回函式,到此前序遍歷了整個二叉樹。
中序遍歷:
呼叫PostOrderBiTree(BiTree &T),T根結點不為空
呼叫PreOrderBiTree(T->leftChild),訪問根結點的左孩子B,不為NULL
呼叫PreOrderBiTree(T->leftChild),訪問B結點的左孩子C,不為NULL
呼叫PreOrderBiTree(T->leftChild),訪問C結點的左孩子,由於C結點沒有左孩子,所以T=NULL,返回函式;列印字元C,又呼叫PreOrderBiTree(T->rightChild),因為C沒有右PreOrderBiTree(T->rightChild)孩子,所以返回到上級遞迴函式,列印字元B;呼叫PreOrderBiTree(T->rightChild),由於B沒有右孩子,返回到上級遞迴函式;列印字元A,呼叫了PreOrderBiTree(T->rightChild),訪問了根結點的右孩子D,不為空;然後呼叫了PreOrderBiTree(T->leftChild),因為D沒有左孩子,返回函式;列印字元D,又呼叫PreOrderBiTree(T->rightChild),因為D沒有右孩子,返回函式,到此中序遍歷了整個二叉樹。
後序遍歷操作讀者感興趣可以自己完成,這裡不作贅述,從上面的分析,整個過程不是太複雜,關鍵要理解遞迴的思想。
程式碼中也簡單實現了列印二叉樹的葉子結點個數,二叉樹的高度,以及銷燬一棵二叉樹。如果明白了二叉樹的建立和遍歷,那麼樹的其他操作對於我們也不是難事。
二叉樹的特點:
1.每個結點最多有兩棵字樹,所以二叉樹中不存在度大於2的結點
2.二叉樹是有序的,其次序不能任意顛倒,即使樹中的某個結點只有一棵
字樹,也要區分它是左子樹還是右子樹。
下面介紹幾種特殊的二叉樹:
1.斜樹
所有結點都只有左子樹的二叉樹稱為左斜樹;所有結點都只有右子樹的二叉樹稱為右斜樹。是兩棵不同的二叉樹。如下示意圖。
在一棵二叉樹中,如果所有分支結點都存在左子樹和右字樹,並且所有葉子結點都在同一層上,這樣的二叉樹稱為滿二叉樹。如下示意圖。
對一棵具有n個結點的二叉樹按層序編號,如果編號為i(1<=i<=n)的結點與同樣深度的滿二叉樹中編號為i的結點在二叉樹中的位置完全相同,則這棵二叉樹稱為完全二叉樹。顯然一棵滿二叉樹必定是一棵完全二叉樹,完全二叉樹的特點是:
1.如果有度為1的結點,只可能有一個,且該結點只有左孩子。
2.葉子結點只能出現在最下面兩層,且最下層的葉子結點都集中在二叉樹左側連續的位置。
關於二叉樹的更多的性質,請讀者檢視維基百科()或者其他資料。
在中能用多種方法來構造二叉樹。常用的是順序儲存結構和二叉連結串列儲存結構。
順序儲存結構:
二叉樹可以用或線性表來儲存,而且如果這是,這種方法不會浪費空間。用這種緊湊排列,如果一個結點的索引為i,它的子結點能在索引2i+1和2i+2找到,並且它的父節點(如果有)能在索引floor((i-1)/2)找到(假設根節點的索引為0)。這種方法更有利於緊湊儲存和更好的,特別是在前序遍歷中。然而,它需要連續的,這樣在儲存高度為h的n個結點組成的一般普通樹時將會浪費很多空間。一種最極壞的情況下如果深度為h的二叉樹每個節點只有右孩子需要佔用2的h次冪減1,而實際卻只有h個結點,空間的浪費太大,這是順序儲存結構的一大缺點。
二叉連結串列儲存結構:
在使用或的程式語言中,二叉樹通常用樹結點結構來儲存。有時也包含指向唯一的父節點的指標。如果一個結點的子結點個數小於2,一些子結點指標可能為空值,或者為特殊的結點。 使用連結串列能避免順序儲存浪費空間的問題,演算法和結構相對簡單,但使用二叉連結串列,由於缺乏父鏈的指引,在找回父節點時需要重新掃描樹得知父節點的節點地址。
對於二叉連結串列最重要的操作莫過於遍歷了,概括來說有遞迴和非遞迴兩種,本文介紹的是遞迴遍歷二叉樹。
點選(此處)摺疊或開啟
-
#include<iostream>
-
using namespace std;
-
typedef struct node
-
{
-
struct node *leftChild;
-
struct node *rightChild;
-
char data;
-
}BiTreeNode, *BiTree;
-
-
void createBiTree(BiTree &);
-
void PreOrderBiTree(BiTree *);
-
void InOrderBiTree(BiTree *);
-
void PostOrderBiTree(BiTree *);
-
int BiTreeDeep(BiTree *);
-
int LeafTreecount(BiTree *);
-
-
void createBiTree(BiTree &T)
-
{
-
char c;
-
cin >> c;
-
if ('#' == c)
-
T = NULL;
-
else
-
{
-
T = new BiTreeNode;
-
T->data = c;
-
createBiTree(T->leftChild);
-
createBiTree(T->rightChild);
-
}
-
}
-
-
void PreOrderBiTree(BiTree &T)
-
{
-
if (T == NULL)
-
return;
-
cout << T->data << " ";
-
PreOrderBiTree(T->leftChild);
-
PreOrderBiTree(T->rightChild);
-
-
}
-
void InOrderBiTree(BiTree &T) {
-
-
if (T == NULL)
-
return;
-
InOrderBiTree(T->leftChild);
-
cout << T->data << " ";
-
InOrderBiTree(T->rightChild);
-
-
}
-
void PostOrderBiTree(BiTree &T) {
-
if (T == NULL)
-
return;
-
PostOrderBiTree(T->leftChild);
-
PostOrderBiTree(T->rightChild);
-
cout << T->data<<" ";
-
}
-
-
int BiTreeDeep(BiTree T)
-
{
-
int deep = 0;
-
if (T)
-
{
-
int leftdeep = BiTreeDeep(T->leftChild);
-
int rightdeep = BiTreeDeep(T->rightChild);
-
deep = leftdeep >= rightdeep ? leftdeep + 1 : rightdeep + 1;
-
}
-
return deep;
-
}
-
-
//求二叉樹葉子結點個數
-
-
int LeafTreecount(BiTree T,int &num)
-
{
-
if (T)
-
{
-
if (T->leftChild == NULL &&T->rightChild == NULL)
-
num++;
-
LeafTreecount(T->leftChild,num);
-
LeafTreecount(T->rightChild,num);
-
-
}
-
return num;
-
}
-
void DeleteBiTree(BiTree T) {
-
if (T == NULL)
-
return;
-
DeleteBiTree(T->leftChild);
-
DeleteBiTree(T->rightChild);
-
delete T;
-
-
}
-
int main()
-
{
-
BiTree T;
-
cout << "輸入的字元是:" << endl;
-
createBiTree(T);
-
cout << "前序遍歷:" << endl;
-
PreOrderBiTree(T); \
-
cout << endl;
-
cout <<"中序遍歷:"<< endl;
-
InOrderBiTree(T);
-
cout << endl;
-
cout << "後序遍歷:" << endl; \
-
PostOrderBiTree(T);
-
cout << endl;
-
cout << "二叉樹的高度為:" << BiTreeDeep(T) << endl;
-
int num = 0;
-
cout << "二叉樹的葉子結點個數為:" << LeafTreecount(T, num)<< endl;
-
DeleteBiTree(T);
-
cout << "樹已銷燬!!!" << endl;
-
return 0;
- }
執行結果如下:
一定要注意建立的二叉樹時的方法,上面也是透過遞迴的方法建立二叉樹,根據前中後序遍歷,很容易畫出這棵二叉樹:(如下字母即指結點又指結點資料域)
前序遍歷:
呼叫PostOrderBiTree(BiTree &T),T根結點不為空,列印字元A
再呼叫PreOrderBiTree(T->leftChild),訪問根結點的左孩子,不為NULL,列印字元B
再次呼叫PreOrderBiTree(T->leftChild),訪問B結點的左孩子,不為空,列印C
再次呼叫PreOrderBiTree(T->leftChild),訪問結點C的左孩子,因為C沒有左孩子,T=NULL,返回函式。呼叫PreOrderBiTree(T->rightChild),C的右孩子也為空,此時返回到 上一級遞迴的函式,也就是列印B結點的函式;此時呼叫PreOrderBiTree(T->rightChild),B沒有右孩子,為空,此時返回到上一級遞迴的函式,就是列印A結點的函式;此時呼叫PreOrderBiTree(T->rightChild),列印了D;呼叫PreOrderBiTree(T->leftChild),因為D沒有左孩子,T=NULL,返回函式,呼叫了PreOrderBiTree(T->rightChild),D也沒有右孩子,T=NULL,返回函式,到此前序遍歷了整個二叉樹。
中序遍歷:
呼叫PostOrderBiTree(BiTree &T),T根結點不為空
呼叫PreOrderBiTree(T->leftChild),訪問根結點的左孩子B,不為NULL
呼叫PreOrderBiTree(T->leftChild),訪問B結點的左孩子C,不為NULL
呼叫PreOrderBiTree(T->leftChild),訪問C結點的左孩子,由於C結點沒有左孩子,所以T=NULL,返回函式;列印字元C,又呼叫PreOrderBiTree(T->rightChild),因為C沒有右PreOrderBiTree(T->rightChild)孩子,所以返回到上級遞迴函式,列印字元B;呼叫PreOrderBiTree(T->rightChild),由於B沒有右孩子,返回到上級遞迴函式;列印字元A,呼叫了PreOrderBiTree(T->rightChild),訪問了根結點的右孩子D,不為空;然後呼叫了PreOrderBiTree(T->leftChild),因為D沒有左孩子,返回函式;列印字元D,又呼叫PreOrderBiTree(T->rightChild),因為D沒有右孩子,返回函式,到此中序遍歷了整個二叉樹。
後序遍歷操作讀者感興趣可以自己完成,這裡不作贅述,從上面的分析,整個過程不是太複雜,關鍵要理解遞迴的思想。
程式碼中也簡單實現了列印二叉樹的葉子結點個數,二叉樹的高度,以及銷燬一棵二叉樹。如果明白了二叉樹的建立和遍歷,那麼樹的其他操作對於我們也不是難事。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29876893/viewspace-1841619/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【資料結構】二叉樹遍歷(遞迴+非遞迴)資料結構二叉樹遞迴
- 【資料結構】——搜尋二叉樹的插入,查詢和刪除(遞迴&非遞迴)資料結構二叉樹遞迴
- 資料結構之「二叉樹」資料結構二叉樹
- js樹型結構資料簡易遞迴JS遞迴
- 基礎資料結構之遞迴資料結構遞迴
- 通用-遞迴樹結構遞迴
- 資料結構-遞迴資料結構遞迴
- 重學資料結構和演算法(二)之二叉樹、紅黑樹、遞迴樹、堆排序資料結構演算法二叉樹遞迴排序
- 資料結構(樹):二叉樹資料結構二叉樹
- 重學資料結構之樹和二叉樹資料結構二叉樹
- 資料結構之「二叉搜尋樹」資料結構
- 資料結構之遍歷二叉樹資料結構二叉樹
- [資料結構]二叉樹的前中後序遍歷(遞迴+迭代實現)資料結構二叉樹遞迴
- 遍歷二叉樹-------遞迴&非遞迴二叉樹遞迴
- 資料結構-二叉樹資料結構二叉樹
- 資料結構 - 二叉樹資料結構二叉樹
- 【資料結構】二叉樹!!!資料結構二叉樹
- 【資料結構導論之樹和二叉樹總結篇】資料結構二叉樹
- 二叉樹(資料結構)——利用“遞迴”思想實現相關演算法問題二叉樹資料結構遞迴演算法
- 常用資料結構之線索二叉樹資料結構二叉樹
- 資料結構之線索化二叉樹資料結構二叉樹
- 資料結構初階--二叉樹(前中後序遍歷遞迴+非遞迴實現+相關求算結點實現)資料結構二叉樹遞迴
- 資料結構5_遞迴資料結構遞迴
- 資料結構:二叉查詢樹的相關操作資料結構
- 資料結構實驗——二叉樹的常見操作資料結構二叉樹
- 實戰PHP資料結構基礎之遞迴PHP資料結構遞迴
- 實戰 PHP 資料結構基礎之遞迴PHP資料結構遞迴
- 資料結構-平衡二叉樹資料結構二叉樹
- 資料結構——平衡二叉樹資料結構二叉樹
- 二叉樹遞迴練習二叉樹遞迴
- 二叉樹的遞迴套路二叉樹遞迴
- 【C++】翻轉二叉樹(遞迴、非遞迴)C++二叉樹遞迴
- 關於樹型結構資料遞迴查詢,轉非遞迴查詢的實現遞迴
- 資料結構之樹結構概述(含滿二叉樹、完全二叉樹、平衡二叉樹、二叉搜尋樹、紅黑樹、B-樹、B+樹、B*樹)資料結構二叉樹
- 遞迴求解二叉樹任意一結點的深度遞迴二叉樹
- 資料結構之「樹」資料結構
- 資料結構中的樹(二叉樹、二叉搜尋樹、AVL樹)資料結構二叉樹
- 資料結構之你真的瞭解二叉樹嗎資料結構二叉樹