非递归实现二叉树的前序,中序,后序遍历打印
来源:互联网 发布:young网络注册的域名 编辑:程序博客网 时间:2024/06/06 10:42
前言:前面这篇文章主要是用递归的思想写了一颗二叉树,其中包括前序建树;前序,中序,后序遍历打印,层序遍历(非递归);求高度;求节点数;求叶子节点数;求第k层结点树等;详情请看:
递归实现二叉树的前,中,后序打印
在我们了解了最简单的递归遍历以后,现在来说说用栈辅助,非递归实现二叉树的前序,中序,后序遍历打印
一:非递归实现前序遍历打印
1. 前序遍历,访问的顺序为根->左->右;
2. 前序遍历的思想:
(1)首先定义一个Node* 的指针cur; 用来遍历二叉树;然后定义一个栈,里面存储二叉树的结点;
(2)利用while循环,一开始cur指向树的根结点,先把cur的值打印出来,然后入栈,然后将cur的值更新为他的左结点,继续循环;直到cur为NULL停止;这时cur指向最左结点的左结点,为NULL,此时表示最左结点和其左子树已经被打印;
(3)接下来,需要打印最左结点的右子树;取出栈顶元素,将cur更新为栈顶元素的右结点;删除栈顶结点;
void _PreOrder(Node* root)//前序遍历打印(根,左,右) { Node* cur=root; stack<Node*> s; while (cur||!s.empty()) { //每次将当前结点访问了。然后入栈 //当while循环结束的时候,cur指向最左结点的左结点为NULL while (cur) { //访问了以后还保存,是为了下面从上往上打印左右结点的时候可以找到对应结点的左右节点 cout<<cur->_data<<"->";//打印 s.push(cur);//入栈 cur=cur->_left;//更新 } //while循环出来表示整个数的根结点和其左子树的根结点均已被打印; //现在要做的是从下往上一次打印左右节点; Node* top=s.top();//取出最左结点; s.pop();//将最左结点出栈 cur=top->_right; } }
二:非递归实现中序遍历打印
1. 前序遍历,访问的顺序为左->根->右;
2. 前序遍历的思想:
(1)首先定义一个Node* 的指针cur; 用来遍历二叉树;然后定义一个栈,里面存储二叉树的结点;
(2)利用while循环,一开始cur指向树的根结点,将cur入栈,然后将cur的值更新为他的左结点,继续循环;直到cur为NULL停止;这时cur指向最左结点的左结点,为NULL,
(3)接下来,需要打印最左结点的左子树;因为左字树为空,所以直接打印他的根,取出栈顶元素,这就是最后一颗树的根,打印,删除栈顶结点,将cur更新为栈顶元素的右结点;
void _InOrder(Node* root)//中序遍历打印(左,根,右) { Node* cur=root; stack<Node*> s; while (cur||!s.empty()) { //一直压栈到最左结点,while循环结束 //表示当前结点及左子树结点均已经被访问过, //出循环的时候cur指得是最左结点的左子树,为NULL while (cur) { s.push(cur); cur=cur->_left; } //开始打印左结点 Node* top=s.top();//取到最左结点 cout<<top->_data<<"->";//打印最左结点 s.pop();//从栈中删除最左结点 //最左结点的左孩子和根结点都已经被访问,接下来用子问题的方式访问右孩子 cur=top->_right; } }
三:非递归实现后序遍历打印
1. 前序遍历,访问的顺序为左->右->根;
2. 前序遍历的思想:
(1)首先定义一个栈和一个记录上一个被访问节点的变量,每遇到一个节点,就对其数据进行访问;然后将其入栈,将当前节点的左孩子赋给它,循环此过程,直到最左节点被访问并被压入栈中。
(2)出循环后用临时变量保存栈顶元素,只有当前节点的右子树为空或者其右子树已经访问过时,才能对当前节点进行访问,同时将栈顶元素出栈
(3)将临时变量的右孩子赋给当前节点,用子问题的方式去访问其右子树。
void _EndOrder(Node* root)//后续遍历打印(左,右,根) { Node* cur=root;//当前结点 Node* prev=NULL;//上一次打印的结点 stack<Node*> s; while (cur || !s.empty())//只要当前结点或者栈中还有元素,则该二叉树一定还没有打印完 { while (cur)//一直找到最左结点 { s.push(cur); cur=cur->_left; }//while循环出来cur指向最左结点的做结点为NULL Node* top=s.top();//取出最左结点 //如果结点的右孩子为空,或者右孩子已经被打印,则可以打印本结点 if (top->_right==NULL || top->_right==prev) { cout<<top->_data<<"->"; s.pop(); prev=top;//将prev更新为已经打印过的结点 } //如果结点的右孩子不为空,且还没有被访问,则将cur更新为右孩子,继续while循环, else { cur=top->_right; } } }
四:完整代码:
#include<iostream>using namespace std;#include<assert.h>#include<stack>template<class T>struct TreeNode{ TreeNode( const T& data=T()) :_data(data) ,_left(NULL) ,_right(NULL) {} int _data; TreeNode<T>* _left; TreeNode<T>* _right;};template<class T>class BinaryTree { typedef TreeNode<T> Node;public: BinaryTree()//无参构造函数 :_root(NULL) {} BinaryTree(const T* arr,int sz,const T invalid)//有参构造函数 { assert(arr); int index=0;//数组中的位置 _root=BuildTree(arr,sz,invalid,index); } void PreOrder()//前序遍历打印(递归) { cout<<"前序打印:"; _PreOrder(_root); } void InOrder()//中序遍历打印(递归) { cout<<"中序打印:"; _InOrder(_root); } void EndOrder()//后序遍历打印(递归) { cout<<"后序打印:"; _EndOrder(_root); }protected: Node* BuildTree(const T* arr,int sz,const T& invalid, int& index)//前序遍历建树 { assert(arr); if (index<sz && arr[index]!=invalid) { Node* root=new Node(arr[index]); root->_left=BuildTree(arr,sz,invalid,++index); root->_right=BuildTree(arr,sz,invalid,++index); return root; } return NULL; } void _PreOrder(Node* root)//前序遍历打印(根,左,右) { Node* cur=root; stack<Node*> s; while (cur||!s.empty()) { //每次将当前结点访问了。然后入栈 //当while循环结束的时候,cur指向最左结点的左结点为NULL while (cur) { //访问了以后还保存,是为了下面从上往上打印左右结点的时候可以找到对应结点的左右节点 cout<<cur->_data<<"->";//打印 s.push(cur);//入栈 cur=cur->_left;//更新 } //while循环出来表示整个数的根结点和其左子树的根结点均已被打印; //现在要做的是从下往上一次打印左右节点; Node* top=s.top();//取出最左结点; s.pop();//将最左结点出栈 cur=top->_right; } } void _InOrder(Node* root)//中序遍历打印(左,根,右) { Node* cur=root; stack<Node*> s; while (cur||!s.empty()) { //一直压栈到最左结点,while循环结束 //表示当前结点及左子树结点均已经被访问过, //出循环的时候cur指得是最左结点的左子树,为NULL while (cur) { s.push(cur); cur=cur->_left; } //开始打印左结点 Node* top=s.top();//取到最左结点 cout<<top->_data<<"->";//打印最左结点 s.pop();//从栈中删除最左结点 //最左结点的左孩子和根结点都已经被访问,接下来用子问题的方式访问右孩子 cur=top->_right; } } void _EndOrder(Node* root)//后续遍历打印(左,右,根) { Node* cur=root;//当前结点 Node* prev=NULL;//上一次打印的结点 stack<Node*> s; while (cur || !s.empty())//只要当前结点或者栈中还有元素,则该二叉树一定还没有打印完 { while (cur)//一直找到最左结点 { s.push(cur); cur=cur->_left; }//while循环出来cur指向最左结点的做结点为NULL Node* top=s.top();//取出最左结点 //如果结点的右孩子为空,或者右孩子已经被打印,则可以打印本结点 if (top->_right==NULL || top->_right==prev) { cout<<top->_data<<"->"; s.pop(); prev=top;//将prev更新为已经打印过的结点 } //如果结点的右孩子不为空,且还没有被访问,则将cur更新为右孩子,继续while循环, else { cur=top->_right; } } }protected: Node* _root;};void Test(){ int arr[] = {1, 2, 3, '#', '#', 4, '#' , '#', 5, 6,'#','#','#'}; int sz=sizeof(arr)/sizeof(arr[0]); BinaryTree<int> bt(arr,sz,'#');//调用带有参数的构造函数 bt.PreOrder(); cout<<endl; bt.InOrder(); cout<<endl; bt.EndOrder(); cout<<endl;}int main(){ Test(); return 0;}
运行结果:
接下来,更新线索化二叉树的内容。。。。O(∩_∩)O哈哈~
- 非递归实现二叉树的前序,中序,后序遍历打印
- 二叉树非递归前、中、后序遍历实现
- 非递归实现二叉树的后序遍历、前序遍历、中序遍历
- 二叉树的非递归【前/中/后 序遍历】
- 二叉树 前序遍历的非递归实现 中序遍历的非递归实现 后序遍历的非递归实现 创建二叉树
- 【二叉树】实现二叉树的前序、中序、后序的非递归遍历
- 二叉树的前序、中序、后序遍历 递归非递归实现
- 二叉树的前序、中序、后序(递归、非递归)遍历java实现
- 二叉树的前序,中序,后序遍历(递归非递归实现)
- 实现二叉树的前序/中序/后序递归、非递归遍历
- 二叉树的前序,中序,后序遍历。用递归和非递归实现
- 【二叉树遍历算法】——前/中/后序递归与非递归的实现
- JAVA实现二叉树的前、中、后序遍历(递归与非递归)
- 二叉树的遍历:前序、中序、后序、层序的非递归实现
- 二叉树的非递归前序,中序,后序遍历的Java实现
- 用java实现二叉树非递归的前序,中序,后序遍历算法
- 非递归实现二叉树的前序、中序、后序遍历
- 非递归实现二叉树的遍历(前序、中序、后序)
- linux中的shell是什么意思
- Eclipse部署项目到tomcat加载两次的问题
- 方法重载(overload)
- 作用域、执行环境、闭包
- java 运行 linux shell 命令
- 非递归实现二叉树的前序,中序,后序遍历打印
- 取消MySQL timestamp列默认ON UPDATE CURRENT_TIMESTAMP
- Java常见的线程安全的类
- 浅拷贝与深拷贝
- Linux基础 Shell基础学习
- 梯度上升算法迭代过程和数学原理
- batch_size 对分类器性能影响实验记录
- Oracle 软件及静默安装数据库
- 简单MVC框架搭建