二叉树遍历的核心问题,是二维结构的线性化(栈、队列)。无论先序遍历、中序遍历、后续遍历,遍历过程中经过结点的路线是一样的,只是访问各结点的时机不同而已,每个结点都有三次访问机会。
一、二叉树结点,C语言定义
1 typedef struct TNode *Position; 2 typedef Position BinTree; /* 二叉树类型 */ 3 struct TNode{ /* 树结点定义 */ 4 ElementType Data; /* 结点数据 */ 5 BinTree Left; /* 指向左子树 */ 6 BinTree Right; /* 指向右子树 */ 7 };
二、递归遍历,C语言实现(其实递归也是使用堆栈实现的)
1 /* 先序遍历 */ 2 void PreorderTraversal( BinTree BT ) 3 { 4 if( BT ) { 5 printf("%d ", BT->Data ); 6 PreorderTraversal( BT->Left ); 7 PreorderTraversal( BT->Right ); 8 } 9 } 10 11 /* 中序遍历 */ 12 void InorderTraversal( BinTree BT ) 13 { 14 if( BT ) { 15 InorderTraversal( BT->Left ); 16 /* 此处假设对BT结点的访问就是打印数据 */ 17 printf("%d ", BT->Data); /* 假设数据为整型 */ 18 InorderTraversal( BT->Right ); 19 } 20 } 21 22 /* 后序遍历 */ 23 void PostorderTraversal( BinTree BT ) 24 { 25 if( BT ) { 26 PostorderTraversal( BT->Left ); 27 PostorderTraversal( BT->Right ); 28 printf("%d ", BT->Data); 29 } 30 }
三、非递归遍历
1 /* 先序遍历 */ 2 void PreorderTraversal( BinTree BT ) 3 { 4 BinTree T = BT; 5 Stack S = CreatStack( MaxSize ); /*创建并初始化堆栈S*/ 6 while( T || !IsEmpty(S) )/* 树不空或栈不空 */ 7 { 8 while(T)/*一直向左并将沿途结点压入堆栈*/ 9 { 10 Push(S,T); /* 结点压栈(第一次遇到结点) */ 11 printf("%5d", T->Data); /*(访问)打印结点*/ 12 T = T->Left;/* 一直向左 */ 13 } 14 if(!IsEmpty(S)) /* 栈不空 */ 15 { 16 T = Pop(S); /*结点弹出堆栈(第二次遇到结点)*/ 17 T = T->Right;/*转向右子树*/ 18 } 19 } 20 } 21 22 /* 中序遍历 23 非递归遍历算法: 24 1.遇到一个结点,就把它压栈,并去遍历它的左子树; 25 2.当左子树遍历结束后,从栈顶弹出这个结点并访问它; 26 3.然后按其右指针再去中序遍历该结点的右子树。 27 */ 28 void InOrderTraversal( BinTree BT ) 29 { 30 BinTree T = BT; 31 Stack S = CreatStack( MaxSize ); /*创建并初始化堆栈S*/ 32 while( T || !IsEmpty(S) )/* 树不空或栈不空 */ 33 { 34 while(T)/*一直向左并将沿途结点压入堆栈*/ 35 { 36 Push(S,T); /* 结点压栈(第一次遇到结点) */ 37 T = T->Left;/* 一直向左 */ 38 } 39 if(!IsEmpty(S)) /* 栈不空 */ 40 { 41 T = Pop(S); /*结点弹出堆栈(第二次遇到结点)*/ 42 printf("%5d", T->Data); /*(访问)打印结点*/ 43 T = T->Right; /*转向右子树*/ 44 } 45 } 46 } 47 48 /* 后序遍历 */ 49 void PostorderTraversal( BinTree BT ) 50 { 51 BinTree T = BT, P = NULL; /* P上一个已被访问的结点 */ 52 Stack S = CreatStack( MaxSize ); /*创建并初始化堆栈S*/ 53 54 while( T || !IsEmpty(S) )/* 树不空或栈不空 */ 55 { 56 while(T)/*一直向左并将沿途结点压入堆栈*/ 57 { 58 Push(S,T); /* 结点压栈(第一次遇到结点) */ 59 T = T->Left;/* 一直向左 */ 60 } 61 62 if( !IsEmpty(S)) 63 { 64 T = Pop(S); /* 先弹出结点 */ 65 if(T->Right == P || T->Right == NULL) 66 { /* 右孩子已访问或右孩子不存在, 弹出结点 */ 67 printf("%5d", T->Data); /* 访问结点 */ 68 P = T; /* P指向被访问结点 */ 69 T = NULL; /* 树置空(该树的左\右\根结点已经访问) */ 70 } 71 else 72 { 73 Push(S,T); /* 否则,不应该弹出结点, 结点再次入栈 */ 74 T = T->Right;/* 继续遍历右子树 */ 75 } 76 } 77 } 78 } 79 80 /* 层次遍历 */ 81 void LevelorderTraversal ( BinTree BT ) 82 { 83 Queue Q; 84 BinTree T; 85 86 if ( !BT ) return; /* 若是空树则直接返回 */ 87 88 Q = CreatQueue(); /* 创建空队列Q */ 89 AddQ( Q, BT ); 90 while ( !IsEmpty(Q) ) 91 { 92 T = DeleteQ( Q ); 93 printf("%d ", T->Data); /* 访问取出队列的结点 */ 94 if ( T->Left ) AddQ( Q, T->Left ); 95 if ( T->Right ) AddQ( Q, T->Right ); 96 } 97 }
四、遍历二叉树的应用
1、二叉树的构造,用先序序列或后序序列和中序序列可以构造唯一一棵二叉树,用扩充先序序列或扩充的后序序列可以构造唯一一棵二叉树(不同的二叉树可能具有相同的先序\中序\后序序列,因此,仅一个先、中、后序序列不能构造唯一一棵二叉树;用扩充中序序列不能构造唯一一棵二叉树)
(1)、后序序列+中序序列,构造一棵二叉树
/* a数组存储树的后序序列, b数组存储中序序列 int a[] = {2,3,1,5,7,6,4} ; //a数组 int b[] = {1,2,3,4,5,6,7} ; //b数组 */ BinTree BuildTree(int a[],int b[],int i,int j,int s,int e) { /* i,j树的后序序列的起止, s,e树的中序序列的起止 */ int k; BinTree p; if( i > j ) return NULL;//递归终止 p = NewNode(a[j]); /* 后序的根a[j]创建结点 */ //p = NewNode(a[i]); /* 先序的根a[i]创建结点 */ k = s; /* 寻找树根位置 */ while( ( k <= e ) && ( b[k] != a[j] ) ) k++; if( k > e ) exit(ERROR); p->Left = BuildTree(a, b, i, i+(k-s)-1, s, k-1); /* 左子树 */ p->Right = BuildTree(a, b, i+(k-s), j-1, k+1, e); /* 右子树 */ /* //a为先序序列, b为中序序列 p->Left = BuildTree(a, b, i+1, i+(k-s), s, k-1); p->Right = BuildTree(a, b, i+(k-s)+1, j, k+1, e); */ return p; }
(2)、扩充的先序序列构造二叉树,先序遍历二叉树时,如果当前要访问的结点不空,就记下这个结点值;如果为空,就记下“空”字所得 到的遍历序列,比如:A,B,D,0,H,0,0,E,0,I,0,0,C,F,0,0,G,J,0,K,0,0,0,
1 #include <stdio.h> 2 #include <malloc.h> 3 4 /* 二叉树结点结构体 */ 5 typedef char ElementType; 6 typedef struct TNode* BinTree; 7 struct TNode{ 8 ElementType Data; 9 BinTree Left; 10 BinTree Right; 11 }; 12 13 /* 创建结点 */ 14 BinTree NewNode(int x) 15 { 16 BinTree t = (BinTree)malloc(sizeof(struct TNode)); 17 t->Data = x; 18 t->Left = t->Right = NULL; 19 return t; 20 } 21 22 /* 扩充的先序序列构造二叉树 */ 23 BinTree PreCreate() 24 { 25 char ch; 26 scanf(" %c,",&ch); 27 /* 输入'0'符号即 NULL */ 28 if(ch=='0') 29 return NULL; 30 /* 根结点 */ 31 BinTree t = NewNode(ch); 32 /* 递归左右子树结点 */ 33 t->Left = PreCreate(); 34 t->Right = PreCreate(); 35 return t; 36 } 37 38 /* 先序遍历 */ 39 void PreorderTraversal( BinTree BT ){ 40 if(BT){ 41 printf("%5c",BT->Data); 42 PreorderTraversal(BT->Left); 43 PreorderTraversal(BT->Right); 44 } 45 } 46 /* A,B,D,0,H,0,0,E,0,I,0,0,C,F,0,0,G,J,0,K,0,0,0, */ 47 int main() 48 { 49 BinTree BT = PreCreate(); 50 PreorderTraversal(BT); 51 return 0; 52 }
2、输出二叉树中的叶子结点,在二叉树的遍历算法中增加检测结点的左右子树是否都为空
void PreOrderPrintLeaves( BinTree BT ) { if( BT ) { if ( !BT-Left && !BT->Right ) printf("%d", BT->Data ); PreOrderPrintLeaves ( BT->Left ); PreOrderPrintLeaves ( BT->Right ); } }
int leaf(BinTree root) { int LeafCount; if(root == NULL) LeafCount = 0; else if((root->Left == NULL) && (root->Right == NULL)) LeafCount = 1; else LeafCount = leaf(root->Left) + leaf(root->Right); return LeafCount; }
3、求二叉树的高度
int PostOrderGetHeight( BinTree BT ) { int HL, HR, MaxH; if( BT ) { HL = PostOrderGetHeight(BT->Left); /*求左子树的深度*/ HR = PostOrderGetHeight(BT->Right); /*求右子树的深度*/ MaxH = (HL > HR) ? HL : HR; /*取左右子树较大的深度*/ return ( MaxH + 1 ); /*返回树的深度*/ } else return 0; /* 空树深度为0 */ }
int depth = 0; void PreTreeDepth(BinTree root, int h) { if(root != NULL) { if(h>depth) depth = h; PreTreeDepth(root->Left, h+1); PreTreeDepth(root->Right, h+1); } }
4、根据后序和中序遍历输出先序遍历
1 #include <stdio.h> 2 #include <stdlib.h> 3 #define N 30 4 5 /* 6 7 7 2 3 1 5 7 6 4 8 1 2 3 4 5 6 7 9 */ 10 11 void Preorder(int a[],int b[],int i,int j,int s,int e) 12 { /* i,j树的后序序列的起止, s,e树的中序序列的起止 */ 13 int k; 14 if( i > j ) return; 15 printf(" %d", a[j]); 16 for(k = s; k <= e && b[k] != a[j] ; k++); 17 Preorder(a, b, i, i+(k-s)-1, s, k-1); /* 左子树 */ 18 Preorder(a, b, i+(k-s), j-1, k+1, e); /* 右子树 */ 19 } 20 21 int main() 22 { 23 int a[N*2]; 24 int n; 25 scanf("%d", &n); 26 for(int i=0;i<n*2;i++) 27 scanf("%d", &a[i]); 28 printf("Preorder:"); 29 Preorder(a,&a[n],0,n-1,0,n-1); 30 return 0; 31 }
1 #include <cstdio> 2 #define MAXN 50 3 4 int pre[MAXN], in[MAXN], post[MAXN]; 5 6 void InputPostAndIn(int N); 7 void solve(int preL, int inL, int postL, int n); 8 void OutputPost(int N); 9 10 int main() 11 { 12 int N; scanf("%d", &N); 13 InputPostAndIn(N); 14 solve(0, 0, 0, N); 15 OutputPost(N); 16 } 17 18 void InputPostAndIn(int N) 19 { 20 21 for (int i=0; i<N; i++) { 22 scanf("%d", &post[i]); 23 } 24 for (int i=0; i<N; i++) { 25 scanf("%d", &in[i]); 26 } 27 28 } 29 30 void solve(int preL, int inL, int postL, int Num) 31 { 32 if (Num == 0) { 33 return; 34 } 35 36 int root, i, LTreeNodeNum, RTreeNodeNum; 37 root = post[postL + Num - 1]; 38 pre[preL] = root; 39 40 for (i=inL; in[i]!=root; i++) {} 41 42 LTreeNodeNum = i - inL; RTreeNodeNum = Num - LTreeNodeNum - 1; 43 44 solve(preL+1, inL, postL, LTreeNodeNum); 45 solve(preL+1+LTreeNodeNum, inL+1+LTreeNodeNum, postL + LTreeNodeNum, RTreeNodeNum); 46 } 47 48 void OutputPost(int N) 49 { 50 printf("Preorder: "); 51 printf("%d", pre[0]); 52 for (int i=1; i<N; i++) { 53 printf(" %d", pre[i] ); 54 } 55 printf("\n"); 56 }
5、下列代码的功能是计算给定二叉树T的宽度。二叉树的宽度是指各层结点数的最大值。函数Queue_rear和Queue_front分别返回当前队列Q中队尾和队首元素的位置
1 int Width( BinTree T ) 2 { 3 BinTree p; 4 Queue Q; 5 int Last, temp_width, max_width; 6 7 temp_width = max_width = 0; 8 Q = CreateQueue(MaxElements); 9 Last = Queue_rear(Q); 10 11 if ( T == NULL) 12 return 0; 13 else 14 { 15 Enqueue(T, Q); 16 while (!IsEmpty(Q)) 17 { /* 队头出队 */ 18 p = Front_Dequeue(Q); 19 /* 层宽加1,其左右子树根结点入队 */ 20 temp_width++ 21 if ( p->Left != NULL ) 22 Enqueue(p->Left, Q); 23 if ( p->Right != NULL ) 24 Enqueue(p->Right, Q); 25 /* 如果到了队尾更新最大宽度 */ 26 if ( Queue_front(Q) > Last ) { 27 Last = Queue_rear(Q); 28 if ( temp_width > max_width ) 29 max_width = temp_width; 30 temp_width=0; /* 临时变量归零 */ 31 } 32 } 33 } 34 return max_width; /* 返回最大宽度 */ 35 }
6、层序遍历,输出层数及结点
1 /* 2 以二叉链表作为存储结构,编写算法打印输出每个结点及所在的层次数(设根结点为第1层) 3 算法: 4 一、以扩充的先序序列构造二叉树,其他形式也可以; 5 二、采取层序遍历的方式输出每层的结点及层次数; 6 1、层序遍历的队列采取循环队列,定义层数level = 1; 7 2、定义结点指针T、last、tail,初始指向树根,T = last = tail = BT; 8 3、树根出队,树根的左右子树根结点入队,tail随入队结点变化; 9 4、第一次循环,tail即指向第二层的最后的一个结点; 10 5、循环步骤3,出队的结点指针指向last即更新last(last = tail;); 11 注意: 12 1、last始终指向每层的最后一个结点,出队结点指向last,输出level++; 13 2、每次结点出队打印结点; 14 **** 15 tail是随入队的结点不断变化的; 16 每一层的结点出队完毕,tail即指向了下层的最后的结点; 17 用last记录下tail,tail再随结点入队变化... 18 19 判断每一层出队完毕条件是出队结点指针是否指向last; 20 last开始指向树根结点的,树根出队后(一层完毕),tail指向第二层最后结点, 21 更新last即last = tail,last指向了第二层的最后一个结点; 22 第二层出队,tail随出队结点的左右子树的根结点的入队,不断变化,更新; 23 第二层的结点出队完毕,last指向第三层的最后一个结点(last = tail); 24 以此类推... 25 **** 26 */ 27 28 #include <stdio.h> 29 #include <malloc.h> 30 #define MAXSIZE 100 31 32 /* 二叉树结点结构体 */ 33 typedef char ElementType; 34 typedef struct TNode* BinTree; 35 struct TNode{ 36 ElementType Data; 37 BinTree Left; 38 BinTree Right; 39 }; 40 41 /* 创建结点 */ 42 BinTree NewNode(int x) 43 { 44 BinTree t = (BinTree)malloc(sizeof(struct TNode)); 45 t->Data = x; 46 t->Left = t->Right = NULL; 47 return t; 48 } 49 50 /* 扩充的先序序列构造二叉树 */ 51 BinTree PreCreate() 52 { 53 char ch; 54 scanf(" %c,",&ch); 55 /* 输入'0'符号即 NULL */ 56 if(ch=='0') 57 return NULL; 58 /* 根结点 */ 59 BinTree t = NewNode(ch); 60 /* 递归左右子树结点 */ 61 t->Left = PreCreate(); 62 t->Right = PreCreate(); 63 return t; 64 } 65 66 /* 先序遍历 */ 67 void PreorderTraversal( BinTree BT ){ 68 if(BT){ 69 printf("%5c",BT->Data); 70 PreorderTraversal(BT->Left); 71 PreorderTraversal(BT->Right); 72 } 73 } 74 75 /* 层序遍历 */ 76 void LevelorderTraversal ( BinTree BT ) 77 { 78 if ( !BT ) return; /* 若是空树则直接返回 */ 79 80 BinTree Queue[MAXSIZE]; /* 循环队列 */ 81 int front = 0, rear = 0; 82 83 int level = 1; /* 层数 */ 84 BinTree T, last, tail; /* 记录层尾的last,tail */ 85 86 Queue[rear++] = BT; /* 进队 */ 87 rear %= MAXSIZE; 88 T = last = tail = BT; 89 90 /* last依次指向层的最右边的结点 */ 91 while ( front!=rear ) 92 { /* 结束 */ 93 if(T == last){ /* 为了输出层数,这段代码由最下上移 */ 94 printf("\nlevel %d:\n",level); 95 last = tail; /* 到了层尾部加一层 */ 96 level++; 97 } 98 /* 开始 */ 99 T = Queue[front++]; /* 出队 */ 100 front %= MAXSIZE; 101 102 printf("%5c", T->Data); /* 访问取出队列的结点 */ 103 104 if ( T->Left ){ 105 Queue[rear++] = T->Left; /* 进队 */ 106 rear %= MAXSIZE; 107 tail = T->Left; /* 更新尾部 */ 108 } 109 if ( T->Right ){ 110 Queue[rear++] = T->Right; /* 进队 */ 111 rear %= MAXSIZE; 112 tail = T->Right; /* 更新尾部 */ 113 } 114 } 115 printf("\n"); 116 } 117 118 /* A,B,D,0,H,0,0,E,0,I,0,0,C,F,0,0,G,J,0,K,0,0,0, */ 119 int main() 120 { 121 BinTree BT = PreCreate(); 122 //PreorderTraversal(BT); 123 LevelorderTraversal(BT); 124 return 0; 125 }
7、请写出二叉树层次遍历的算法,即从根结点开始按层次由上至下,从左到右访问二叉树中的每个结点
#include <stdio.h> #include <malloc.h> #define MAXSIZE 100 /* 二叉树结点结构体 */ typedef char elemtype; typedef struct btnode{ elemtype data; struct btnode *lchild ,*rchild; }bitnode, *bitree; /* 创建结点 */ bitree NewNode(int x) { bitree t = (bitree)malloc(sizeof(struct btnode)); t->data = x; t->lchild = t->rchild = NULL; return t; } /* 扩充的先序序列构造二叉树 */ bitree PreCreate() { char ch; scanf(" %c,",&ch); /* 输入'0'符号即 NULL */ if(ch=='0') return NULL; /* 根结点 */ bitree t = NewNode(ch); /* 递归左右子树结点 */ t->lchild = PreCreate(); t->rchild = PreCreate(); return t; } /* 先序遍历 */ void PreorderTraversal( bitree BT ){ if(BT){ printf("%5c",BT->data); PreorderTraversal(BT->lchild); PreorderTraversal(BT->rchild); } } /* 层序遍历 */ void LevelorderTraversal ( bitree BT ) { if ( !BT ) return; /* 若是空树则直接返回 */ bitree Queue[MAXSIZE]; /* 循环队列 */ int front = 0, rear = 0; int level = 1; /* 层数 */ bitree T, last, tail; /* 记录层尾的last,tail */ Queue[rear++] = BT; /* 进队 */ rear %= MAXSIZE; T = last = tail = BT; /* last依次指向层的最右边的结点 */ while ( front!=rear ) { /* 结束 */ if(T == last){ /* 为了输出层数,这段代码由最下上移 */ printf("\nlevel %d:\n",level); last = tail; /* 到了层尾部加一层 */ level++; } /* 开始 */ T = Queue[front++]; /* 出队 */ front %= MAXSIZE; printf("%5c", T->data); /* 访问取出队列的结点 */ if ( T->lchild ){ Queue[rear++] = T->lchild; /* 进队 */ rear %= MAXSIZE; tail = T->lchild; /* 更新尾部 */ } if ( T->rchild ){ Queue[rear++] = T->rchild; /* 进队 */ rear %= MAXSIZE; tail = T->rchild; /* 更新尾部 */ } } printf("\n"); } /* 扩充的先序序列构造二叉树,样例 A,B,D,0,H,0,0,E,0,I,0,0,C,F,0,0,G,J,0,K,0,0,0, */ int main() { bitree BT = PreCreate(); //PreorderTraversal(BT); printf("\n"); LevelorderTraversal(BT); return 0; }
8、利用栈构造表达式二叉树
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct TreeNode { char op_ch; struct TreeNode* left, * right; } TreeNode; typedef struct { struct TreeNode* root; } TreeType; typedef struct StackNode { TreeNode* item; struct StackNode* link; } StackNode; typedef struct { StackNode* top; } LinkedStackType; void init_t(TreeType* t) { t->root = NULL; } void init(LinkedStackType* s) { s->top = NULL; } int is_empty(LinkedStackType* s) { return (s->top == NULL); } int is_full(LinkedStackType* s) { return 0; } void push(LinkedStackType* s, TreeNode* item) { StackNode* temp = (StackNode*)malloc(sizeof(StackNode)); if (temp == NULL) { printf("结点申请失败\n"); } else { temp->item = item; temp->link = s->top; s->top = temp; } } TreeNode* pop(LinkedStackType* s) { if (is_empty(s)) { printf("栈空\n"); exit(1); } else { StackNode* temp = s->top; TreeNode* item = temp->item; s->top = s->top->link; free(temp); return item; } } void tree_display(TreeNode* r) { if (r != NULL) { tree_display(r->left); printf("%c ", r->op_ch); tree_display(r->right); } } void make_exp_tree(TreeType* t, char str[]) { LinkedStackType st; //利用栈构造表达式二叉树 init(&st); for (int i=0; i<strlen(str); i++) { if(str[i]==' ') //去除空格 continue; if( str[i]>='0'&&str[i]<='9' )//不是操作符 { t->root = (TreeNode*)malloc( sizeof(TreeNode) );// t->root->op_ch = str[i]; t->root->left = t->root->right=NULL; push(&st,t->root); } else //是操作符 { t->root = (TreeNode*)malloc( sizeof(TreeNode) );//操作符结点 t->root->op_ch = str[i]; t->root->left = t->root->right=NULL; // t->root->right = pop(&st); t->root->left = pop(&st); push(&st, t->root ); } } t->root = pop(&st); } int exp_evaluation(TreeNode* r) { } int descendant_node_count(TreeNode* node) { } int terminal_node_count(TreeNode* node) { } int main() { TreeType rt; char str[] = "2 3 * 5 +"; init_t(&rt); make_exp_tree(&rt, str); printf("\n1. Binary tree inorder traversal: "); tree_display(rt.root); printf("\n"); /* printf("\n2. Evaluation result: %d \n", exp_evaluation(rt.root)); printf("\n"); printf("3. Number of descendant nodes: %d \n\n", descendant_node_count(rt.root)); printf("4. Number of terminal nodes: %d \n\n", terminal_node_count(rt.root)); printf("\n"); */ return 0; }