二叉树中序遍历,先序遍历,后序遍历(递归栈,非递归栈,Morris Traversal)
例题
- 中序遍历94. Binary Tree Inorder Traversal
- 先序遍历144. Binary Tree Preorder Traversal
- 后序遍历145. Binary Tree Postorder Traversal
递归栈
递归函数栈的方法很基础,写法也很简单,三种遍历方式之间只需要改变一行代码的位置即可
中序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void inorder(TreeNode* root, vector<int>& v){
if(root != nullptr) {
inorder(root->left, v);
v.push_back(root->val); // 改变位置的代码
inorder(root->right, v);
}
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> v;
inorder(root, v);
return v;
}
};
先序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void inorder(TreeNode* root, vector<int>& v){
if(root != nullptr) {
v.push_back(root->val);
inorder(root->left, v);
inorder(root->right, v);
}
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> v;
inorder(root, v);
return v;
}
};
后序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void inorder(TreeNode* root, vector<int>& v){
if(root != nullptr) {
inorder(root->left, v);
inorder(root->right, v);
v.push_back(root->val);
}
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> v;
inorder(root, v);
return v;
}
};
非递归栈
当树的深度过大时,函数栈可能会溢出,这时候需要我们使用数据结构中的栈,来模拟节点在栈中的压入和弹出
中序遍历
cur指针时刻指向需要处理的节点
如果当前节点不为空,则压入栈中,并改变cur指针指向其左节点
如果当前节点为空,也代表空节点的中序遍历自动完成,无需压栈,这时候需要弹出栈顶的节点,保存栈顶节点的值,并更改cur指向其右子树,以完成右子树的中序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> v;
stack<TreeNode*> s;
TreeNode* cur = root;
while(cur || !s.empty()){
if(cur){
s.push(cur);
cur = cur->left;
} else {
cur = s.top();
s.pop();
v.push_back(cur->val);
cur = cur->right;
}
}
return v;
}
};
先序遍历
先序遍历与中序遍历代码相比只改变了保存节点的值的代码的位置,当先访问根的时候就记录节点,而不是等左子树遍历完,弹出根节点的时候再记录
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> v;
stack<TreeNode*> s;
TreeNode* cur = root;
while(cur || !s.empty()){
if(cur){
v.push_back(cur->val);
s.push(cur);
cur = cur->left;
} else {
cur = s.top();
s.pop();
cur = cur->right;
}
}
return v;
}
};
后序遍历
因为后序遍历的压栈顺序是左-右-根,由于先遍历完左子树,然后遍历完右子树,然后才能处理当前节点,为了和之前的代码的结构保持一致,我们可以反向处理,也就是按根-右-左的顺序压栈,
结果反向输出即可
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> v;
stack<TreeNode*> s;
TreeNode* cur = root;
while(cur || !s.empty()){
if(cur){
v.push_back(cur->val);
s.push(cur);
cur = cur->right;
} else {
cur = s.top();
s.pop();
cur = cur->left;
}
}
reverse(v.begin(), v.end()); // 反向输出结果
return v;
}
};
Morris Traversal
线索二叉树,O(n)时间,常数空间
Morris Traversal方法遍历二叉树(非递归,不用栈,O(1)空间)
就是当前节点的中序遍历前驱节点,如果此前驱节点的右指针为空,则将此前驱节点的右指针指向当前节点
当寻找当前节点的中序遍历前驱节点时,发现能循环到自己,说明左子树已经遍历完,需要遍历右子树
中序遍历
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
TreeNode* cur, *prev;
vector<int> v;
cur = root;
prev = nullptr;
while(cur){
if(cur->left == nullptr){
v.push_back(cur->val);
cur = cur->right;
} else {
prev = cur->left;
while(prev->right != nullptr && prev->right != cur)
prev = prev->right;
if(prev->right == nullptr){
prev->right = cur;
cur = cur->left;
} else {
v.push_back(cur->val);
prev->right = nullptr;
cur = cur->right;
}
}
}
return v;
}
};
先序遍历
同样先序遍历与中序遍历相比也只需要改变一行代码的位置
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
TreeNode* cur, *prev;
vector<int> v;
cur = root;
prev = nullptr;
while(cur){
if(cur->left == nullptr){
v.push_back(cur->val);
cur = cur->right;
} else {
prev = cur->left;
while(prev->right != nullptr && prev->right != cur)
prev = prev->right;
if(prev->right == nullptr){
v.push_back(cur->val);
prev->right = cur;
cur = cur->left;
} else {
prev->right = nullptr;
cur = cur->right;
}
}
}
return v;
}
};
后序遍历
后序遍历需要反向输出cur->left到cur的中序前驱结点之间的路径
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void reverse_traverse_reverse(TreeNode* cur, vector<int>& v){
TreeNode* prev = nullptr;
while(cur){
auto right = cur->right;
cur->right = prev;
prev = cur;
cur = right;
}
cur = prev;
prev = nullptr;
while(cur){
auto right = cur->right;
cur->right = prev;
v.push_back(cur->val);
prev = cur;
cur = right;
}
}
vector<int> postorderTraversal(TreeNode* root) {
TreeNode* cur, *prev;
vector<int> v;
cur = new TreeNode(-1);
prev = nullptr;
cur->left = root;
while(cur){
if(cur->left == nullptr){
cur = cur->right;
} else {
prev = cur->left;
while(prev->right != nullptr && prev->right != cur)
prev = prev->right;
if(prev->right == nullptr){
prev->right = cur;
cur = cur->left;
} else {
prev->right = nullptr;
reverse_traverse_reverse(cur->left, v);
cur = cur->right;
}
}
}
return v;
}
};
总结
三种遍历方式中,后序遍历的处理比较麻烦,但是无论是使用递归栈,非递归栈还是Morris Traversal,代码的结构都是一样的,中序和前序甚至只有一行代码位置的差别
使用栈的版本中,最坏空间复杂度为O(n)链型,平均空间复杂度为O(lgn)
Morris Traversal空间复杂度为O(1)
二叉树中序遍历,先序遍历,后序遍历(递归栈,非递归栈,Morris Traversal)的更多相关文章
- 二叉树前中后/层次遍历的递归与非递归形式(c++)
/* 二叉树前中后/层次遍历的递归与非递归形式 */ //*************** void preOrder1(BinaryTreeNode* pRoot) { if(pRoot==NULL) ...
- 数据结构二叉树的递归与非递归遍历之java,javascript,php实现可编译(1)java
前一段时间,学习数据结构的各种算法,概念不难理解,只是被C++的指针给弄的犯糊涂,于是用java,web,javascript,分别去实现数据结构的各种算法. 二叉树的遍历,本分享只是以二叉树中的先序 ...
- C实现二叉树(模块化集成,遍历的递归与非递归实现)
C实现二叉树模块化集成 实验源码介绍(源代码的总体介绍):header.h : 头文件链栈,循环队列,二叉树的结构声明和相关函数的声明.LinkStack.c : 链栈的相关操作函数定义.Queue. ...
- 二叉树3种递归和非递归遍历(Java)
import java.util.Stack; //二叉树3种递归和非递归遍历(Java) public class Traverse { /******************一二进制树的定义*** ...
- JAVA递归、非递归遍历二叉树(转)
原文链接: JAVA递归.非递归遍历二叉树 import java.util.Stack; import java.util.HashMap; public class BinTree { priva ...
- 二叉树的创建、遍历(递归和非递归实现)、交换左右子数、求高度(c++实现)
要求:以左右孩子表示法实现链式方式存储的二叉树(lson—rson),以菜单方式设计并完成功能任务:建立并存储树.输出前序遍历结果.输出中序遍历结果.输出后序遍历结果.交换左右子树.统计高度,其中对于 ...
- JAVA递归、非递归遍历二叉树
前序遍历:1.访问根节点 2.前序遍历左子树 3.前序遍历右子树 中序遍历:1.中序遍历左子树 2.访问根节点 3.中序遍历右子树 后序遍历:1.后序遍历左子树 2.后序遍历右子树 3.访问根节点-- ...
- 基于Java的二叉树的三种遍历方式的递归与非递归实现
二叉树的遍历方式包括前序遍历.中序遍历和后序遍历,其实现方式包括递归实现和非递归实现. 前序遍历:根节点 | 左子树 | 右子树 中序遍历:左子树 | 根节点 | 右子树 后序遍历:左子树 | 右子树 ...
- 数据结构-树以及深度、广度优先遍历(递归和非递归,python实现)
前面我们介绍了队列.堆栈.链表,你亲自动手实践了吗?今天我们来到了树的部分,树在数据结构中是非常重要的一部分,树的应用有很多很多,树的种类也有很多很多,今天我们就先来创建一个普通的树.其他各种各样的树 ...
- 二叉树之AVL树的平衡实现(递归与非递归)
这篇文章用来复习AVL的平衡操作,分别会介绍其旋转操作的递归与非递归实现,但是最终带有插入示例的版本会以递归呈现. 下面这张图绘制了需要旋转操作的8种情况.(我要给做这张图的兄弟一个赞)后面会给出这八 ...
随机推荐
- 浏览器端-W3School-JavaScript:Location 对象
ylbtech-浏览器端-W3School-JavaScript:Location 对象 1.返回顶部 1. Location 对象 Location 对象 Location 对象包含有关当前 URL ...
- linux(centOS7)的基本操作(六) 进程管理
进程的概念 1. 在linux系统中,每一段执行的程序都称为一个进程,被分配一个进程编号(id). 2. 每个进程都对应一个父进程,一个父进程可以复制多个子进程. 3. 一个进程可能以两种方式存在:前 ...
- Java学习之==>数组【array】
一.定义数组 /** * 一维数组定义 * * 为数组插入元素 */ public void case1() { // 声明 int[] arr1; // 声明+初始化 int[] arr2 = ne ...
- Centos7 修改系统时间和硬件时间不一致的问题
查看系统时间 [root@localhost ~]# dateSat Feb 24 14:41:22 CST 2018 查看硬件时间 [root@localhost ~]# hwclock --sho ...
- UOJ#48最大矩形面积
题面 这是一道标准的单调栈的题目,但是由于题目的个例性,该题对于前后两数等于的情况并无额外处理,so也确实是让这题简单了一点 也没什么好说的直接上代码吧 #include<iostream> ...
- 在Linux上实现Python调用C语言函数
一般思路 Python中内置ctypes库,需调用c编译成的.so文件来实现函数调用. 假设我们所需调用的c文件名为test.c,文件里有我们需要的函数func(x,y). 将.c文件编译成 .so文 ...
- Linux系统基础知识整理(一)
本文来自于: https://www.cnblogs.com/hafiz/p/6686187.html#4196989 一.说明 本篇文章,我将结合自己的实践以及简介,来对linux系统做一个直观清晰 ...
- spring boot-6.profile 多环境支持
在正式项目中一般都会区分多个环境,一般至少分为开发环境,测试生产环境,生产环境,实际可能会有更加精细的区分,针对不同的环境,项目的配置可能需要切换,spring boot 提供了很方便的环境切换方式. ...
- [转帖]Linux杂谈: 树形显示多级目录--tree
Linux杂谈: 树形显示多级目录--tree https://www.cnblogs.com/tp1226/p/8456539.html tree -L 最近写博客的时候偶尔会需要将文件目录结构直观 ...
- 小记---------maxwell 一个可以实时读取mysql二进制日志binlog,并生成JSON格式的消息,作为生产者发送给kafka,Redis,文件或其他平台的应用程序
maxwell主要提供了下列功能 支持 SELECT * FROM table 的方式进行全量数据初始化 支持在主库发生failover后,自动回复binlog位置(GTID) ...