非递归实现二叉树的三种遍历操作,C++描述
body, table{font-family: 微软雅黑; font-size: 13.5pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}
|
图一:
前序遍历:abc
中序遍历:bac
后序遍历:bca
|
图二:
前序遍历:ABCDEF
中序遍历:CBAEDF
后序遍历:CBEFDA
|
|
#include<iostream>
#include<stack>
using namespace std;
typedef struct BinaryTree
{
char data;
struct BinaryTree* lchild;
struct BinaryTree* rchild;
}binaryTreeNode,*pBinaryTree;
int createBinaryTree(pBinaryTree& root);
//非递归实现前序遍历,利用栈来模拟递归过程
void preorderTraversalNonRecursion(pBinaryTree root);
//非递归实现中序遍历,利用栈来模拟递归过程
void inorderTraversalNonRecursion(pBinaryTree root);
//非递归实现后序遍历,利用栈来模拟递归过程
void postorderTraversalNonRecursion(pBinaryTree root);
int createBinaryTree(pBinaryTree& root)
{
char data;
if(cin>>data)
{
if('#'==data) //输入#表示该结点为空
{
root = NULL;
return -1; //只要当输入是#才会返回-1,表示空树
}
//建立根结点
binaryTreeNode* node = new binaryTreeNode();
node->data = data;
root = node;
//递归去建立左子树
createBinaryTree(root->lchild);
//递归去建立右子树
createBinaryTree(root->rchild);
}
return 0; //最终递归返回的是0;
}
void preorderTraversalNonRecursion(pBinaryTree root)
{//访问节点顺序是:根结点,左子树,右子树;针对左右子树,又是按照根左右的顺序访问,所有这个过程中要记录根结点
if(nullptr==root)
return;
stack<binaryTreeNode*> rootNode; //遍历过程中记录根结点
while(!rootNode.empty()||
nullptr!=root)
{
while(nullptr!=root)
{
cout<<root->data<<" "; //输出根结点
//遍历左子树
rootNode.push(root); //记录左子树的根结点,前序遍历回溯访问右子树的时候要用到
root = root->lchild;
}//一直遍历到最左边的最后一个结点,肯定左子树为空了,出while循环,这时候就要回溯访问右子树了
if(!rootNode.empty())
{
root = rootNode.top(); //栈中取出最近入栈的根结点
root = root->rchild; //根结点指向右子树
rootNode.pop(); //最近压栈的根结点弹栈
//右子树也是一棵二叉树,又回到第二个while循环,前序遍历,根左右的方法访问右子树
}
else
root = NULL;
}
}
void inorderTraversalNonRecursion(pBinaryTree root)
{//左子树,根结点,右子树
if(nullptr==root)
return ;
stack<binaryTreeNode*> rootNode;
while(nullptr!=root||
!rootNode.empty())
{
while(nullptr!=root)
{
rootNode.push(root); //栈中保存根结点,先去访问左子树
root = root->lchild;
}//左子树遍历完毕
cout<<rootNode.top()->data<<" "; //访问根结点
root = rootNode.top()->rchild; //访问右子树
rootNode.pop(); //出栈最近保存的结点
}
}
|
typedef struct Traversal
{
binaryTreeNode* rootAddr; //二叉树结点地址
bool accessToken; //访问标记
}traversalInfo,*pTraversal;
void postorderTraversalNonRecursion(pBinaryTree root)
{//后序遍历,左子树,右子树,根结点
//遍历完左子树后要回溯到根结点去遍历右子树,所以需要一个标记来记录根结点,如果是第二次遍历到就直接输出值
if(nullptr==root)
return ;
stack<traversalInfo> rootNodeIndo;
while(nullptr!=root) //二叉树根结点最后才会访问
{
//遍历左子树
traversalInfo tmp;
tmp.rootAddr = root;
tmp.accessToken = false; //第一次访问,第二次访问修改为true,输出信息
rootNodeIndo.push(tmp); //遍历右子树沿途的根结点入栈
root = root->lchild;
}//开始回溯遍历右子树
while(!rootNodeIndo.empty())
{
root = rootNodeIndo.top().rootAddr; //取出最近访问的根结点
//遍历右子树
while(nullptr!=root->rchild&&
!rootNodeIndo.top().accessToken) //右子树不空,并且根结点只存在第一次访问
{
rootNodeIndo.top().accessToken = true; //根结点第二次访问,修改为true,下次回溯访问到就要输出
//右子树根结点进栈
root = root->rchild;
traversalInfo tmp;
tmp.rootAddr = root;
tmp.accessToken = false;
rootNodeIndo.push(tmp);
//右子树的左子树不空,接着遍历左子树
while(nullptr!=root->lchild)
{
root = root->lchild;
traversalInfo tmp;
tmp.rootAddr = root;
tmp.accessToken = false;
rootNodeIndo.push(tmp);
}//右子树的左子树遍历完毕,回溯遍历右子树的右子树。
}
//输出结点信息
//执行下面语句只有两种情况,1、对应二叉树最左边结点没有右子树,包括右子树的最左边结点
//2、回溯再次访问到根结点,即第3个while循环的第二个条件!rootNodeIndo.top().accessToken不满足
cout<<rootNodeIndo.top().rootAddr->data<<" ";
rootNodeIndo.pop(); //出栈
}
}
int main()
{
pBinaryTree root = nullptr;
int ret = createBinaryTree(root);
if(0==ret)
{
cout<<"preorder traversal non-recursion:"<<endl;
preorderTraversalNonRecursion(root);
cout<<endl<<endl;;
cout<<"inorder traversal non-recursion:"<<endl;
inorderTraversalNonRecursion(root);
cout<<endl<<endl;;
cout<<"postorder traversal non-recursion:"<<endl;
postorderTraversalNonRecursion(root);
cout<<endl;
}
cout<<endl;
system("pause");
}
|
|
//后序遍历算法优化
typedef struct Traversal
{
binaryTreeNode* rootAddr;
bool accessToken;
}traversalInfo,*pTraversal;
void stackPush(stack<traversalInfo>& rootNodeInfo,binaryTreeNode* &rootAddr,bool&& flag)
{
traversalInfo tmp;
tmp.rootAddr = rootAddr;
tmp.accessToken = flag;
rootNodeInfo.push(tmp);
}
|
void postorderTraversalNonRecursion(pBinaryTree root)
{
if(nullptr==root)
return ;
stack<traversalInfo> rootNodeInfo;
while(nullptr!=root)
{
stackPush(rootNodeInfo,root,false);
root = root->lchild;
}
while(!rootNodeInfo.empty())
{
root = rootNodeInfo.top().rootAddr;
while(nullptr!=root->rchild&&
!rootNodeInfo.top().accessToken)
{
rootNodeInfo.top().accessToken = true;
root = root->rchild;
stackPush(rootNodeInfo,root,false);
while(nullptr!=root->lchild)
{
root = root->lchild;
stackPush(rootNodeInfo,root,false);
}
}
cout<<rootNodeInfo.top().rootAddr->data<<" ";
rootNodeInfo.pop();
}
}
|
非递归实现二叉树的三种遍历操作,C++描述的更多相关文章
- PTA 二叉树的三种遍历(先序、中序和后序)
6-5 二叉树的三种遍历(先序.中序和后序) (6 分) 本题要求实现给定的二叉树的三种遍历. 函数接口定义: void Preorder(BiTree T); void Inorder(BiTr ...
- 基于Java的二叉树的三种遍历方式的递归与非递归实现
二叉树的遍历方式包括前序遍历.中序遍历和后序遍历,其实现方式包括递归实现和非递归实现. 前序遍历:根节点 | 左子树 | 右子树 中序遍历:左子树 | 根节点 | 右子树 后序遍历:左子树 | 右子树 ...
- C++编程练习(8)----“二叉树的建立以及二叉树的三种遍历方式“(前序遍历、中序遍历、后续遍历)
树 利用顺序存储和链式存储的特点,可以实现树的存储结构的表示,具体表示法有很多种. 1)双亲表示法:在每个结点中,附设一个指示器指示其双亲结点在数组中的位置. 2)孩子表示法:把每个结点的孩子排列起来 ...
- 大数据学习day13------第三阶段----scala01-----函数式编程。scala以及IDEA的安装,变量的定义,条件表达式,for循环(守卫模式,推导式,可变参数以及三种遍历方式),方法定义,数组以及集合(可变和非可变),数组中常用的方法
具体见第三阶段scala-day01中的文档(scala编程基础---基础语法) 1. 函数式编程(https://www.cnblogs.com/wchukai/p/5651185.html): ...
- java:数据结构(四)二叉查找树以及树的三种遍历
@TOC 二叉树模型 二叉树是树的一种应用,一个节点可以有两个孩子:左孩子,右孩子,并且除了根节点以外每个节点都有一个父节点.当然这种简单的二叉树不能解决让树保持平衡状态,例如你一直往树的左边添加元素 ...
- map的三种遍历方法!
map的三种遍历方法! 集合的一个很重要的操作---遍历,学习了三种遍历方法,三种方法各有优缺点~~ /* * To change this template, choose Tools | Te ...
- 二叉树及其三种遍历方式的实现(基于Java)
二叉树概念: 二叉树是每个节点的度均不超过2的有序树,因此二叉树中每个节点的孩子只能是0,1或者2个,并且每个孩子都有左右之分. 位于左边的孩子称为左孩子,位于右边的孩子成为右孩子:以左孩子为根节点的 ...
- javase-常用三种遍历方法
javase-常用三种遍历方法 import java.util.ArrayList; import java.util.Iterator; import java.util.List; public ...
- Java中Map的三种遍历方法
Map的三种遍历方法: 1. 使用keySet遍历,while循环: 2. 使用entrySet遍历,while循环: 3. 使用for循环遍历. 告诉您们一个小秘密: (下↓面是测试代码,最爱看 ...
随机推荐
- Codeforces 939E - Maximize!
939E - Maximize! 思路: 贪心:最后的集合是最大值+前k小个 因为平均值时关于k的凹形函数,所以可以用三分求最小值 又因为后面的k肯定比前面的k大,所以又可以双指针 三分: #incl ...
- 小程序歌词展示,格式lrc歌词
代码: wxml: <view class="page"> <view class="lrc" style="margin-top: ...
- 浏览器缓存之Expires Etag Last-Modified max-age详解
前段时间去面试移动端的H5开发工程师,在最后面试的时候被问到了max-age Expires Etag有什么不同,在什么情况下应用,当时乱编了一通,自我感觉良好,结果…… 大家懂得,现在讲他们几个的区 ...
- Getting started with Processing 第六章总结
平移,旋转和缩放 前言 在这一章节中,平移.旋转和缩放都是以原点为基准的.并且是通过控制坐标系原点的位置来达到图元平移,旋转.缩放的视觉效果.作者在文中的原话是:另一种在屏幕上改变位置和移动物体的技术 ...
- Linux(centos7)上安装最新版R3.4.1
说来惭愧,居然没有在Linux安装R的经验,因为一直很少用R,用也是在win平台. 下载路径:https://cran.rstudio.com/src/base/R-3/ 强烈建议不要安装最新的R,除 ...
- (转)解决windows10下无法安装.net framework 3.5,错误代码0x800F081F
1.下载 NET Framework 3.5的安装包netfx3.cab 将下载的文件复制到复制到 C 盘的 Windows 文件夹 后请在“命令提示符(管理员)”中执行下面的命令: dism /on ...
- p2725 Stamps
背包. #include <iostream> #include <cstdio> #include <cmath> #include <algorithm& ...
- 4.2 面向对象分析(二) CRC方法标识概念类
CRC 又称为CRC索引卡片:CRC card 每张卡片代表一个类 Each card represents one class 每张卡片上写出这个类承担的职责.与其合作交互的其他类名 ...
- python记录_day33 线程
##进程就像加工厂,线程是里边的流水线##进程是资源单位,线程是运行单位,每个进程至少有一个线程 即进程是资源分配的最小单位,线程是CPU调度的最小单位 一.线程的创建两种方式,和进程类似1.t = ...
- Codeforces Beta Round #64D - Professor's task
题意:两种操作1.加点2.查询点是否在之前给定点的凸包内 题解:set维护动态凸包,分别维护上下凸壳,对y取反就行,判断点是否在凸壳内,把点加进去看要不要删除就好了 //#pragma GCC opt ...