演算法之二叉樹各種遍歷
樹形結構是一類重要的非線性資料結構,其中以樹和二叉樹最為常用。
二叉樹是每個結點最多有兩個子樹的有序樹。通常子樹的根被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。二叉樹常被用作二叉查詢樹和二叉堆或是二叉排序樹。二叉樹的每個結點至多隻有二棵子樹(不存在度大於2的結點),二叉樹的子樹有左右之分,次序不能顛倒。二叉樹的第i層至多有2的 i -1次方個結點;深度為k的二叉樹至多有2^(k) -1個結點;對任何一棵二叉樹T,如果其終端結點數(即葉子結點數)為n0,度為2的結點數為n2,則n0 = n2 + 1。
二叉樹的鏈式儲存結構是一類重要的資料結構,其形式定義如下:
//二叉樹結點
typedef struct BiTNode{
//資料
char data;
//左右孩子指標
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
二叉樹的建立:
通過讀入一個字串,建立二叉樹的演算法如下:
//按先序序列建立二叉樹 int CreateBiTree(BiTree &T){ char data; //按先序次序輸入二叉樹中結點的值(一個字元),‘#’表示空樹 scanf("%c",&data); if(data == '#'){ T = NULL; } else{ T = (BiTree)malloc(sizeof(BiTNode)); //生成根結點 T->data = data; //構造左子樹 CreateBiTree(T->lchild); //構造右子樹 CreateBiTree(T->rchild); } return 0; }
二叉樹的遍歷:
遍歷是對樹的一種最基本的運算,所謂遍歷二叉樹,就是按一定的規則和順序走遍二叉樹的所有結點,使每一個結點都被訪問一次,而且只被訪問一次。由於二叉樹是非線性結構,因此,樹的遍歷實質上是將二叉樹的各個結點轉換成為一個線性序列來表示。
遞迴演算法:
//輸出 void Visit(BiTree T){ if(T->data != '#'){ printf("%c ",T->data); } } //先序遍歷 void PreOrder(BiTree T){ if(T != NULL){ //訪問根節點 Visit(T); //訪問左子結點 PreOrder(T->lchild); //訪問右子結點 PreOrder(T->rchild); } } //中序遍歷 void InOrder(BiTree T){ if(T != NULL){ //訪問左子結點 InOrder(T->lchild); //訪問根節點 Visit(T); //訪問右子結點 InOrder(T->rchild); } } //後序遍歷 void PostOrder(BiTree T){ if(T != NULL){ //訪問左子結點 PostOrder(T->lchild); //訪問右子結點 PostOrder(T->rchild); //訪問根節點 Visit(T); } }
非遞迴演算法:
<1>先序遍歷:
【思路】:訪問T->data後,將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應為T,出棧,再先序遍歷T的右子樹。
/* 先序遍歷(非遞迴)
思路:訪問T->data後,將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應為T,出棧,再先序遍歷T的右子樹。
*/
void PreOrder2(BiTree T){
stack<BiTree> stack;
//p是遍歷指標
BiTree p = T;
//棧不空或者p不空時迴圈
while(p || !stack.empty()){
if(p != NULL){
//存入棧中
stack.push(p);
//訪問根節點
printf("%c ",p->data);
//遍歷左子樹
p = p->lchild;
}
else{
//退棧
p = stack.top();
stack.pop();
//訪問右子樹
p = p->rchild;
}
}//while
}
<2>中序遍歷
【思路】:T是要遍歷樹的根指標,中序遍歷要求在遍歷完左子樹後,訪問根,再遍歷右子樹。
先將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應為T,出棧,訪問T->data,再中序遍歷T的右子樹。
void InOrder2(BiTree T){
stack<BiTree> stack;
//p是遍歷指標
BiTree p = T;
//棧不空或者p不空時迴圈
while(p || !stack.empty()){
if(p != NULL){
//存入棧中
stack.push(p);
//遍歷左子樹
p = p->lchild;
}
else{
//退棧,訪問根節點
p = stack.top();
printf("%c ",p->data);
stack.pop();
//訪問右子樹
p = p->rchild;
}
}//while
}
<3>後序遍歷
【思路】:T是要遍歷樹的根指標,後序遍歷要求在遍歷完左右子樹後,再訪問根。需要判斷根結點的左右子樹是否均遍歷過。
//後序遍歷(非遞迴)
typedef struct BiTNodePost{
BiTree biTree;
char tag;
}BiTNodePost,*BiTreePost;
void PostOrder2(BiTree T){
stack<BiTreePost> stack;
//p是遍歷指標
BiTree p = T;
BiTreePost BT;
//棧不空或者p不空時迴圈
while(p != NULL || !stack.empty()){
//遍歷左子樹
while(p != NULL){
BT = (BiTreePost)malloc(sizeof(BiTNodePost));
BT->biTree = p;
//訪問過左子樹
BT->tag = 'L';
stack.push(BT);
p = p->lchild;
}
//左右子樹訪問完畢訪問根節點
while(!stack.empty() && (stack.top())->tag == 'R'){
BT = stack.top();
//退棧
stack.pop();
BT->biTree;
printf("%c ",BT->biTree->data);
}
//遍歷右子樹
if(!stack.empty()){
BT = stack.top();
//訪問過右子樹
BT->tag = 'R';
p = BT->biTree;
p = p->rchild;
}
}//while
}
<4>層次遍歷
【思路】:按從頂向下,從左至右的順序來逐層訪問每個節點,層次遍歷的過程中需要用佇列。
//層次遍歷
void LevelOrder(BiTree T){
BiTree p = T;
//佇列
queue<BiTree> queue;
//根節點入隊
queue.push(p);
//佇列不空迴圈
while(!queue.empty()){
//對頭元素出隊
p = queue.front();
//訪問p指向的結點
printf("%c ",p->data);
//退出佇列
queue.pop();
//左子樹不空,將左子樹入隊
if(p->lchild != NULL){
queue.push(p->lchild);
}
//右子樹不空,將右子樹入隊
if(p->rchild != NULL){
queue.push(p->rchild);
}
}
}
測試用例:
輸入:
ABC##DE#G##F###
輸出:
程式碼:
#include<iostream>
#include<stack>
#include<queue>
using namespace std;
//二叉樹結點
typedef struct BiTNode{
//資料
char data;
//左右孩子指標
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
//按先序序列建立二叉樹
int CreateBiTree(BiTree &T){
char data;
//按先序次序輸入二叉樹中結點的值(一個字元),‘#’表示空樹
scanf("%c",&data);
if(data == '#'){
T = NULL;
}
else{
T = (BiTree)malloc(sizeof(BiTNode));
//生成根結點
T->data = data;
//構造左子樹
CreateBiTree(T->lchild);
//構造右子樹
CreateBiTree(T->rchild);
}
return 0;
}
//輸出
void Visit(BiTree T){
if(T->data != '#'){
printf("%c ",T->data);
}
}
//先序遍歷
void PreOrder(BiTree T){
if(T != NULL){
//訪問根節點
Visit(T);
//訪問左子結點
PreOrder(T->lchild);
//訪問右子結點
PreOrder(T->rchild);
}
}
//中序遍歷
void InOrder(BiTree T){
if(T != NULL){
//訪問左子結點
InOrder(T->lchild);
//訪問根節點
Visit(T);
//訪問右子結點
InOrder(T->rchild);
}
}
//後序遍歷
void PostOrder(BiTree T){
if(T != NULL){
//訪問左子結點
PostOrder(T->lchild);
//訪問右子結點
PostOrder(T->rchild);
//訪問根節點
Visit(T);
}
}
/* 先序遍歷(非遞迴)
思路:訪問T->data後,將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應為T,出棧,再先序遍歷T的右子樹。
*/
void PreOrder2(BiTree T){
stack<BiTree> stack;
//p是遍歷指標
BiTree p = T;
//棧不空或者p不空時迴圈
while(p || !stack.empty()){
if(p != NULL){
//存入棧中
stack.push(p);
//訪問根節點
printf("%c ",p->data);
//遍歷左子樹
p = p->lchild;
}
else{
//退棧
p = stack.top();
stack.pop();
//訪問右子樹
p = p->rchild;
}
}//while
}
/* 中序遍歷(非遞迴)
思路:T是要遍歷樹的根指標,中序遍歷要求在遍歷完左子樹後,訪問根,再遍歷右子樹。
先將T入棧,遍歷左子樹;遍歷完左子樹返回時,棧頂元素應為T,出棧,訪問T->data,再中序遍歷T的右子樹。
*/
void InOrder2(BiTree T){
stack<BiTree> stack;
//p是遍歷指標
BiTree p = T;
//棧不空或者p不空時迴圈
while(p || !stack.empty()){
if(p != NULL){
//存入棧中
stack.push(p);
//遍歷左子樹
p = p->lchild;
}
else{
//退棧,訪問根節點
p = stack.top();
printf("%c ",p->data);
stack.pop();
//訪問右子樹
p = p->rchild;
}
}//while
}
//後序遍歷(非遞迴)
typedef struct BiTNodePost{
BiTree biTree;
char tag;
}BiTNodePost,*BiTreePost;
void PostOrder2(BiTree T){
stack<BiTreePost> stack;
//p是遍歷指標
BiTree p = T;
BiTreePost BT;
//棧不空或者p不空時迴圈
while(p != NULL || !stack.empty()){
//遍歷左子樹
while(p != NULL){
BT = (BiTreePost)malloc(sizeof(BiTNodePost));
BT->biTree = p;
//訪問過左子樹
BT->tag = 'L';
stack.push(BT);
p = p->lchild;
}
//左右子樹訪問完畢訪問根節點
while(!stack.empty() && (stack.top())->tag == 'R'){
BT = stack.top();
//退棧
stack.pop();
printf("%c ",BT->biTree->data);
}
//遍歷右子樹
if(!stack.empty()){
BT = stack.top();
//訪問過右子樹
BT->tag = 'R';
p = BT->biTree;
p = p->rchild;
}
}//while
}
//層次遍歷
void LevelOrder(BiTree T){
BiTree p = T;
//佇列
queue<BiTree> queue;
//根節點入隊
queue.push(p);
//佇列不空迴圈
while(!queue.empty()){
//對頭元素出隊
p = queue.front();
//訪問p指向的結點
printf("%c ",p->data);
//退出佇列
queue.pop();
//左子樹不空,將左子樹入隊
if(p->lchild != NULL){
queue.push(p->lchild);
}
//右子樹不空,將右子樹入隊
if(p->rchild != NULL){
queue.push(p->rchild);
}
}
}
int main()
{
BiTree T;
CreateBiTree(T);
printf("先序遍歷:\n");
PreOrder(T);
printf("\n");
printf("先序遍歷(非遞迴):\n");
PreOrder2(T);
printf("\n");
printf("中序遍歷:\n");
InOrder(T);
printf("\n");
printf("中序遍歷(非遞迴):\n");
InOrder2(T);
printf("\n");
printf("後序遍歷:\n");
PostOrder(T);
printf("\n");
printf("後序遍歷(非遞迴):\n");
PostOrder2(T);
printf("\n");
printf("層次遍歷:\n");
LevelOrder(T);
printf("\n");
return 0;
}