剑指offer 树的基本操作:四种遍历方式
前序遍历 递归版
编程思想
即借助系统栈,效率较低。二叉树的前序遍历规则:1. 访问根结点; 2. 遍历左子树; 3. 遍历右子树
编程实现
//树的定义
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
private:
void rec(TreeNode* root,vector<int> &ret){
if(root != nullptr){
ret.push_back(root->val);
rec(root->left,ret);
rec(root->right,ret);
}
}
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ret;
rec(root,ret);
return ret;
}
};
编程总结
常规方法,注意向量要用引用。
前序遍历 迭代版
编程思想
使用了一个辅助结点p,这种写法其实可以看作是一个模版,对应的还有中序和后序的模版写法,形式很统一,方便于记忆。
辅助结点p初始化为根结点,while循环的条件是栈不为空或者辅助结点p不为空,在循环中首先判断如果辅助结点p存在,那么先将p加入栈中,然后将p的结点值加入结果res中,此时p指向其左子结点。否则如果p不存在的话,表明没有左子结点,我们取出栈顶结点,将p指向栈顶结点的右子结点。
编程实现
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> s;
TreeNode *p = root;
while (!s.empty() || p) {
if (p) {
s.push(p);
res.push_back(p->val);
p = p->left;
}
else {
TreeNode *t = s.top();
s.pop();
p = t->right;
}
}
return res;
}
};
题目总结
在掌握规律的前提下,使用模板记忆。
中序遍历 递归版
编程思想
即借助系统栈,效率较低。二叉树的前序遍历规则:1. 遍历左子树; 2. 访问根结点; 3. 遍历右子树
编程实现
class Solution {
private:
void rec(TreeNode* root,vector<int> &ret){
if(root != NULL){
rec(root->left,ret);
ret.push_back(root->val);
rec(root->right,ret);
}
}
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
rec(root,ret);
return ret;
}
};
题目总结
常规。
中序遍历 迭代版
编程思想
使用了一个辅助结点p,这种写法其实可以看作是一个模版,对应的还有前序和后序的模版写法,形式很统一,方便于记忆。
因为中序遍历的顺序是左-根-右,故与前序不同的是把结点值加入结果res的步骤从if中移动到了else中。
辅助结点p初始化为根结点,while循环的条件是栈不为空或者辅助结点p不为空,在循环中首先判断如果辅助结点p存在;那么先将p加入栈中,将p指向栈顶结点的左子结点。否则如果p不存在的话,表明没有左子结点,我们取出栈顶结点,然后将p的结点值加入结果res中,此时p指向其右子结点。
编程实现
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> s;
TreeNode *p = root;
while (!s.empty() || p) {
if (p) {
s.push(p);
p = p->left;
}
else {
TreeNode *t = s.top();
s.pop();
res.push_back(t->val);
p = t->right;
}
}
return res;
}
};
题目总结
注意与前序遍历的区别和联系,因为中序遍历的顺序是左-根-右,故与前序不同的是把结点值加入结果res的步骤从if中移动到了else中。
后序遍历 递归版
编程思想
即借助系统栈,效率较低。二叉树的前序遍历规则: 1. 遍历左子树;2. 遍历右子树;3. 访问根结点;
编程实现
class Solution {
private:
void rec(TreeNode* root,vector<int> &ret){
if(root != NULL){
rec(root->left,ret);
rec(root->right,ret);
ret.push_back(root->val);
}
}
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ret;
rec(root,ret);
return ret;
}
};
编程总结
常规。
后序遍历 迭代版
编程思想
使用了一个辅助结点p,这种写法其实可以看作是一个模版,对应的还有前序和中序的模版写法,形式很统一,方便于记忆。
由于后序遍历的顺序是左-右-根,而前序遍历的顺序是根-左-右,二者其实还是很相近的,我们可以先在先序遍历的方法上做些小改动,使其遍历顺序变为根-右-左,然后翻转一下,就是左-右-根了。翻转的方法,是反向加入结果res,每次都在结果res的开头加入结点值,而改变先序遍历的顺序就只要改变一下入栈顺序,先左后右,这样出栈处理的时候就是先右后左了。一定要对比前序遍历记忆!!!
拓展:当访问一个结点*p时,栈中结点恰好为*p结点的所有祖先。从栈底到栈底结点再加上*p结点,刚好构成从根节点到*p结点的一条路径。这一特性非常重要,如求根结点到某结点的路径;求两个结点的最近公共祖先;均可用这个思想。
编程实现
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> s;
TreeNode *p = root;
while (!s.empty() || p) {
if (p) {
s.push(p);
res.insert(res.begin(), p->val); //反向添加,而前序是正向添加
p = p->right; //与前序对比
}
else {
TreeNode *t = s.top();
s.pop();
p = t->left;
}
}
return res;
}
};
编程总结
拓展思想很重要!!
层次遍历 版本1
编程思想
进行常规层次遍历,需要借助一个队列。
先将根节点入队,然后出队,访问该根结点,如果它有左子树,则将左子树根节点入队,形成下一层;如果它有右子树,则将右子树根结点入队,形成下一层。然后出队,访问该结点,如此反复,直至队列为空。看代码比较容易懂。
补充:
queue 的基本操作有:
入队,如例:q.push(x); 将x 接到队列的末端。
出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
访问队首元素,如例:q.front(),即最早被压入队列的元素。
访问队尾元素,如例:q.back(),即最后被压入队列的元素。
判断队列空,如例:q.empty(),当队列空时,返回true。
访问队列中的元素个数,如例:q.size()
编程实现
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>>v;
queue<TreeNode*>q;
q.push(root); //根节点入队
if(root == NULL)
return v ;
while(!q.empty()){ //队列不空
vector<int>vv;
queue<TreeNode*> next ; // 建立第二个队列 用来存放下一层的结点
while(!q.empty()){ //遍历当前层的结点 这层循环是核心 其他都是为了满足OJ输出
TreeNode* tre = q.front() ;
vv.push_back(tre->val); //访问该结点,为了满足输出要求,所以有点复杂,
q.pop(); //对头元素出队
if(tre->left!=NULL){ //它有左子树
next.push(tre->left);
}
if(tre->right!=NULL){ //它有右子树
next.push(tre->right);
}
}
v.push_back(vv);
q=next; // // 遍历完后进入下一层
}
return v;
}
};
编程总结
层次遍历往往要用到队列,要对队列的基本操作熟悉,注意二维向量的生成。
层次遍历 版本2
编程思想
给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)。
同版本一类似,只不过需要多一个栈,把每层返回的结点加入栈中,最后输出。
编程实现
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>>v;
stack<vector<int>>s;
queue<TreeNode*>q;
q.push(root); //根节点入队
if(root == NULL)
return v ;
while(!q.empty()){ //队列不空
vector<int>vv;
queue<TreeNode*> next ; // 建立第二个队列 用来存放下一层的结点
while(!q.empty()){ //遍历每层的结点 这层循环是核心 其他都是为了满足OJ输出
TreeNode* tre = q.front() ;
vv.push_back(tre->val); //访问该结点,为了满足输出要求,所以有点复杂,
q.pop(); //对头元素出队
if(tre->left!=NULL){ //它有左子树
next.push(tre->left);
}
if(tre->right!=NULL){ //它有右子树
next.push(tre->right);
}
}
s.push(vv); //将每层结点入栈
//v.push_back(vv);
q=next; // // 遍历完后进入下一层
}
while(!s.empty()){ //将每层结点倒序输出
v.push_back(s.top());
s.pop();
}
return v;
}
};
编程总结
注意其他数据结构的配合使用。
剑指offer 树的基本操作:四种遍历方式的更多相关文章
- 剑指Offer(二十四):二叉树中和为某一值的路径
剑指Offer(二十四):二叉树中和为某一值的路径 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.ne ...
- 剑指Offer(三十四):第一个只出现一次的字符
剑指Offer(三十四):第一个只出现一次的字符 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.net ...
- lua中for循环的四种遍历方式
lua中for的四种遍历方式区别 table.maxn 取最大的整数key #table 从1开始的顺序整数最大值,如1,2,3,6 #table == 3 key,value pairs 取每一 ...
- list的四种遍历方式
1.手先增强for循环和iterator遍历的效果是一样的,也就说 增强for循环的内部也就是调用iteratoer实现的,但是增强for循环 有些缺点,例如不能在增强循环里动态的删除集合内容.不能获 ...
- Map 的四种遍历方式
Map 的四种遍历方式 import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class ...
- java集合四种遍历方式
package conection; import java.util.Iterator;import java.util.LinkedList;import java.util.List; publ ...
- map的四种遍历方式
map是Java中非常常用的一种数据结构,但map不同于set和list都继承自Collection接口. 所以map没有实现Collection的Iterator 方法,自身没有迭代器来遍历元素. ...
- Map集合的四种遍历方式
很久以前写的代码,和上一个做比较吧!便于以后查看 import java.util.HashMap; import java.util.Iterator; import java.util.Map; ...
- java list 的 四种遍历方式
在java中遍历一个list对象的方法主要有以下四种: 1. For Loop —— 普通for循环 2. Advanced For Loop —— 高级for循环 3. Iterator Loop ...
随机推荐
- Vue高仿网易云网页端源码
音乐播放器虽然烂大街了,但是作为前端没自己撸一个一直是个遗憾, 而且偶然间发现 pc 端 web 版的网易云音乐做的实在是太简陋了, 社区仿 pc 客户端的网易云也不多见,为了弥补这个遗憾, 就用 ...
- 牛客挑战赛46 B
题目链接: 最小的指数 乍一看还以为是Pollard_rho算法,其实大可不必. 发现\(1<= n <= 1e18\),我们可以将n分为两部分(分块思想降低时间复杂度). 剔除小于等于\ ...
- Feign String 参数 传递null 以及 空字符串问题
笔记链接:https://app.yinxiang.com/fx/c82f6d74-3432-4703-83c8-5175f5986f97 备注 因为笔记在印象笔记上进行编辑,而且为Markdown格 ...
- kali linux没有ip解决办法
故障情况 今天打开kali202001复测环境,发现自启动ssh竟然连不上. 上到kali主机使用命令:ifconfig 查看发现没有ethh0网卡显示,看来是kal获取不到ip地址导致的 继续查看发 ...
- Visual Studio 2013中安装Resharper之后一些快捷键无法使用,比如F6和F12
快捷键是一个很好用的东西,尤其对于计算机从业者来说,好的快捷键能够高程度提高工作效率.像我们程序员经常需要团队开发,我们会遇到一个问题,那就是快捷键不一致问题,我一般会安装resharper,但是有的 ...
- 手摸手带你学CSS
好好学习,天天向上 本文已收录至我的Github仓库DayDayUP:github.com/RobodLee/DayDayUP,欢迎Star HTML常用标签总结 前言 在大一的时候,上过网页设计的课 ...
- SQL Server中datetimeset转换datetime类型问题浅析
在SQL Server中,数据类型datetimeoffset转换为datetime类型或datetime2类型时需要特别注意,有可能一不小心你可能会碰到下面这种情况.下面我们构造一个简单案例,模拟一 ...
- Gitignore 配置语法
Gitignore 配置语法 原文地址:http://kuanghy.github.io/2016/05/17/gitignore Git 的 .gitignore 配置文件用于配置不需要加入到版本管 ...
- 老板居然让我在Java项目中“造假”
1. 前言 老板说,明天甲方要来看产品,你得造点数据,而且数据必须是"真"的,演示效果要好看一些,这样他才会买我们的产品,我好明年给你换个嫂子.一般开发接到这种过分要求都不会很乐意 ...
- 记录第一次使用Vivado——以全加器为例子
从altera转战xilinx,经典的FPGA到ZYNQ系列,第一站就是先熟悉编译软件Vivado.我就直接跳过软件安装部分了,如有疑问,可以在评论区提出来,我看到了就帮你解答. 首先是是打开界面 然 ...