数据结构之二叉树的遍历
来源:互联网 发布:苹果电脑能编程吗 编辑:程序博客网 时间:2024/06/11 23:48
前言:在上一篇中介绍了二叉树的基础知识,这一篇介绍遍历二叉树的实现
一、二叉树的存储结构:
二叉树的存储结构可以采用顺序存储,也可以采用链式存储,其中链式存储更加灵活。
在链式存储结构中,与线性链表类似,二叉树的每个结点采用结构体表示,结构体包含三个域:数据域、左指针、右指针。
二、二叉树的遍历:
二叉树实现需要用到的数据结构代码如下:
struct BiTNode{ //定义二叉树char data; //每个结点的数据BiTNode *lchild, *rchild; //左右孩子指针};struct Stack{ //定义栈int top; //栈顶指针BiTNode *stacksize[100]; //栈的容量};void InitStack(Stack &S) //初始化一个空栈{S.top = -1;}int Push(Stack &S, BiTNode *pt) //元素进栈{S.stacksize[++S.top] = pt;return 1;}BiTNode * Pop(Stack &S) //元素出栈{BiTNode *pt;pt = S.stacksize[S.top--];return pt;}BiTNode * GetTop(Stack S) //获取栈顶元素{BiTNode *pt;if (S.top == -1)return 0;else{pt = S.stacksize[S.top];return pt;}}int IsEmptyStack(Stack S) //判断是否空栈,空栈返回1,否则返回0{return S.top == -1;}
二叉树遍历通常借用“栈”这种数据结构实现,有两种方式:递归方式及非递归方式。
(1)在递归方式中,栈是由操作系统维护的,用户不必关心栈的细节操作,用户只需关心“访问顺序”即可。因而,采用递归方式实现二叉树的遍历比较容易理解,算法简单,容易实现。
(2)借用“栈”采用非递归方式,也能实现遍历。但是,这时的栈操作(push、pop等)是由用户进行的,因而实现起来会复杂一些,而且也不容易理解,但有助于我们对树结构的遍历有一个深刻、清晰的理解。(a)非递归先序遍历:
在遍历某一个二叉(子)树时,以一当前指针记录当前要处理的二叉(左子)树,以一个栈保存当前树之后处理的右子树。首先访问当前树的根结点数据,接下来应该依次遍历其左子树和右子树,然而程序的控制流只能处理其一,所以考虑将右子树的根保存在栈里面,当前指针则指向需先处理的左子树,为下次循环做准备;若当前指针指向的树为空,说明当前树为空树,不需要做任何处理,直接弹出栈顶的子树,为下次循环做准备,代码如下
int PreOrderTraverse(BiTNode *T) //二叉树先序遍历的非递归算法{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;while (pt != NULL || IsEmptyStack(S)!=1){if (pt != NULL){cout << pt->data;Push(S, pt->rchild); //右子树进栈,让当前指针指向左子树,为下一个循环做准备pt = pt->lchild;}elsept=Pop(S); //左子树为空,不做任何处理,直接弹出右子树,为下一个循环准备}return 1; //遍历完成}
相对于非递归先序遍历,非递归的中序/后序遍历稍复杂一点。
(b)非递归中序遍历:
若当前树不为空树,则访问其根结点之前应先访问其左子树,因而先将当前根节点入栈,然后考虑其左子树,不断将非空的根节点入栈,直到左子树为一空树;当左子树为空时,不需要做任何处理,弹出并访问栈顶结点,然后指向其右子树,为下次循环做准备。代码如下:
int InOrderTraverse(BiTNode *T) //二叉树中序非递归遍历{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;while (pt != NULL || IsEmptyStack(S)!=1){if (pt != NULL){Push(S, pt); //访问根节点之前先访问左结点,所以现将根节点进栈pt = pt->lchild;}else //根指针退栈,访问根节点,遍历右子树{pt=Pop(S);cout << pt->data;pt = pt->rchild; //访问根节点之后,访问右子树}}return 1;}
(c)后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问,并且左孩子在右孩子之前访问才能访问根结f点,这就为流程控制带来了难题。下面介绍一种思路。
要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点p,先将其入栈。若p不存在左孩子和右孩子,则可以直接访问它,或者p存在左孩子或右孩子,但是其左孩子和右孩子都已经被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将p的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子之前别访问,左孩子和右孩子都在根结点前面被访问。代码如下:
int PostOrderTraverse(BiTNode *T) //二叉树的后序非递归算法{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;BiTNode *pre, *cur;pre = NULL;if (!pt) //空树{cout << "the tree is null" << endl;return 0;}Push(S, pt);while (IsEmptyStack(S)!=1){cur =GetTop(S); //获取栈顶元素if ((cur->lchild == NULL&&cur->rchild == NULL) || (pre != NULL && (pre == cur->lchild || pre == cur->rchild))){//如果当前结点没有孩子或者左右孩子都已经访问过,就直接访问该结点cout << cur->data;Pop(S);pre = cur; //标识它成为上一个被访问的元素}else{if (cur->rchild != NULL) //当前结点既有左右孩子没有被访问,那么就该结点就进栈Push(S, cur->rchild);if (cur->lchild != NULL)Push(S, cur->lchild);}}return 1;}
三、完整程序代码:
#include "stdafx.h"#include <iostream>using namespace std;struct BiTNode{ //定义二叉树char data; //每个结点的数据BiTNode *lchild, *rchild; //左右孩子指针};struct Stack{ //定义栈int top; //栈顶指针BiTNode *stacksize[100]; //栈的容量};void InitStack(Stack &S) //初始化一个空栈{S.top = -1;}int Push(Stack &S, BiTNode *pt) //元素进栈{S.stacksize[++S.top] = pt;return 1;}BiTNode * Pop(Stack &S) //元素出栈{BiTNode *pt;pt = S.stacksize[S.top--];return pt;}BiTNode * GetTop(Stack S) //获取栈顶元素{BiTNode *pt;if (S.top == -1)return 0;else{pt = S.stacksize[S.top];return pt;}}int IsEmptyStack(Stack S) //判断是否空栈,空栈返回1,否则返回0{return S.top == -1;}int PreOrderTraverse(BiTNode *T) //二叉树先序遍历的非递归算法{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;while (pt != NULL || IsEmptyStack(S)!=1){if (pt != NULL){cout << pt->data;Push(S, pt->rchild); //右子树进栈,让当前指针指向左子树,为下一个循环做准备pt = pt->lchild;}elsept=Pop(S); //左子树为空,不做任何处理,直接弹出右子树,为下一个循环准备}return 1; //遍历完成}int InOrderTraverse(BiTNode *T) //二叉树中序非递归遍历{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;while (pt != NULL || IsEmptyStack(S)!=1){if (pt != NULL){Push(S, pt); //访问根节点之前先访问左结点,所以现将根节点进栈pt = pt->lchild;}else //根指针退栈,访问根节点,遍历右子树{pt=Pop(S);cout << pt->data;pt = pt->rchild; //访问根节点之后,访问右子树}}return 1;}int PostOrderTraverse(BiTNode *T) //二叉树的非递归算法{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;BiTNode *pre, *cur;pre = NULL;if (!pt){cout << "the tree is null" << endl;return 0;}Push(S, pt);while (IsEmptyStack(S)!=1){cur =GetTop(S); //获取栈顶元素if ((cur->lchild == NULL&&cur->rchild == NULL) || (pre != NULL && (pre == cur->lchild || pre == cur->rchild))){//如果当前结点没有孩子或者左右孩子都已经访问过,就直接访问该结点cout << cur->data;Pop(S);pre = cur; //标识它成为上一个被访问的元素}else{if (cur->rchild != NULL) //当前结点既有左右孩子没有被访问,那么就该结点就进栈Push(S, cur->rchild);if (cur->lchild != NULL)Push(S, cur->lchild);}}return 1;}int CreateBiTree(BiTNode *&T) //采用先序遍历法创建二叉树{char c;c = getchar();if (c == '#') // 空树T = NULL;else{T = new BiTNode;T->data = c;CreateBiTree(T->lchild);CreateBiTree(T->rchild);}return 1;}int _tmain(int argc, _TCHAR* argv[]) {BiTNode *T = NULL;cout << "先序输入字符创建一个二叉树:" << endl;CreateBiTree(T);cout << "前序遍历二叉树序列为:" << endl;PreOrderTraverse(T);cout << endl;cout << "中序遍历二叉树序列为:" << endl;InOrderTraverse(T);cout << endl;cout << "后序遍历二叉树序列为:" << endl;PostOrderTraverse(T);cout << endl;return 0;}
参考:严蔚敏《数据结构》(c语言版)
- 数据结构之二叉树的遍历汇总
- java数据结构之二叉树的遍历
- 数据结构之二叉树的遍历
- 数据结构之二叉树的遍历
- 数据结构实验之二叉树的遍历
- 数据结构之二叉树遍历
- 数据结构之二叉树遍历
- 数据结构之树和二叉树---二叉树的遍历
- 二叉树的遍历:数据结构实验之二叉树二:遍历二叉树
- [C++ 二叉树的构建与遍历] 数据结构实验之二叉树二:遍历二叉树
- 数据结构 - 二叉树的遍历
- 数据结构-----二叉树的遍历
- 数据结构----二叉树的遍历
- 数据结构 二叉树的遍历
- 【数据结构】 二叉树的遍历
- 【数据结构】二叉树的遍历
- 数据结构 - 二叉树的遍历
- 数据结构二叉树的遍历
- 使用11g DNFS建立基于DNFS的tablespace
- JStorm中的并行( parallelismction )介绍
- 疯狂java——面向对象
- NOI国家集训队论文集
- TCP的三次握手(建立连接)和四次挥手(关闭连接)详解
- 数据结构之二叉树的遍历
- 一份小小的练习
- codeforces 566A Matching Names
- [Elasticsearch] 数据建模 - 处理关联关系(2)
- UVA - 11865 Stream My Contest(朱刘算法)
- UIButton增大响应区域
- Linux物理内存管理概述
- Android模拟器---Genymotion安装及使用
- 8.17开始刷LeetCode