前序遍历 递归版

编程思想

即借助系统栈,效率较低。二叉树的前序遍历规则:1. 访问根结点; 2. 遍历左子树; 3. 遍历右子树

编程实现

  1. //树的定义
  2. struct TreeNode {
  3. int val;
  4. TreeNode *left;
  5. TreeNode *right;
  6. TreeNode(int x) : val(x), left(NULL), right(NULL) {}
  7. };
  1. class Solution {
  2. private:
  3. void rec(TreeNode* root,vector<int> &ret){
  4. if(root != nullptr){
  5. ret.push_back(root->val);
  6. rec(root->left,ret);
  7. rec(root->right,ret);
  8. }
  9. }
  10. public:
  11. vector<int> preorderTraversal(TreeNode* root) {
  12. vector<int> ret;
  13. rec(root,ret);
  14. return ret;
  15. }
  16. };

编程总结

常规方法,注意向量要用引用。


前序遍历 迭代版

编程思想

使用了一个辅助结点p,这种写法其实可以看作是一个模版,对应的还有中序和后序的模版写法,形式很统一,方便于记忆。
辅助结点p初始化为根结点,while循环的条件是栈不为空或者辅助结点p不为空,在循环中首先判断如果辅助结点p存在,那么先将p加入栈中,然后将p的结点值加入结果res中,此时p指向其左子结点。否则如果p不存在的话,表明没有左子结点,我们取出栈顶结点,将p指向栈顶结点的右子结点。

编程实现

  1. class Solution {
  2. public:
  3. vector<int> preorderTraversal(TreeNode* root) {
  4. vector<int> res;
  5. stack<TreeNode*> s;
  6. TreeNode *p = root;
  7. while (!s.empty() || p) {
  8. if (p) {
  9. s.push(p);
  10. res.push_back(p->val);
  11. p = p->left;
  12. }
  13. else {
  14. TreeNode *t = s.top();
  15. s.pop();
  16. p = t->right;
  17. }
  18. }
  19. return res;
  20. }
  21. };

题目总结

在掌握规律的前提下,使用模板记忆。


中序遍历 递归版

编程思想

即借助系统栈,效率较低。二叉树的前序遍历规则:1. 遍历左子树; 2. 访问根结点; 3. 遍历右子树

编程实现

  1. class Solution {
  2. private:
  3. void rec(TreeNode* root,vector<int> &ret){
  4. if(root != NULL){
  5. rec(root->left,ret);
  6. ret.push_back(root->val);
  7. rec(root->right,ret);
  8. }
  9. }
  10. public:
  11. vector<int> inorderTraversal(TreeNode* root) {
  12. vector<int> ret;
  13. rec(root,ret);
  14. return ret;
  15. }
  16. };

题目总结

常规。


中序遍历 迭代版

编程思想

使用了一个辅助结点p,这种写法其实可以看作是一个模版,对应的还有前序和后序的模版写法,形式很统一,方便于记忆。
因为中序遍历的顺序是左-根-右,故与前序不同的是把结点值加入结果res的步骤从if中移动到了else中。
辅助结点p初始化为根结点,while循环的条件是栈不为空或者辅助结点p不为空,在循环中首先判断如果辅助结点p存在;那么先将p加入栈中,将p指向栈顶结点的左子结点。否则如果p不存在的话,表明没有左子结点,我们取出栈顶结点,然后将p的结点值加入结果res中,此时p指向其右子结点。

编程实现

  1. class Solution {
  2. public:
  3. vector<int> inorderTraversal(TreeNode* root) {
  4. vector<int> res;
  5. stack<TreeNode*> s;
  6. TreeNode *p = root;
  7. while (!s.empty() || p) {
  8. if (p) {
  9. s.push(p);
  10. p = p->left;
  11. }
  12. else {
  13. TreeNode *t = s.top();
  14. s.pop();
  15. res.push_back(t->val);
  16. p = t->right;
  17. }
  18. }
  19. return res;
  20. }
  21. };

题目总结

注意与前序遍历的区别和联系,因为中序遍历的顺序是左-根-右,故与前序不同的是把结点值加入结果res的步骤从if中移动到了else中。


后序遍历 递归版

编程思想

即借助系统栈,效率较低。二叉树的前序遍历规则: 1. 遍历左子树;2. 遍历右子树;3. 访问根结点;

编程实现

  1. class Solution {
  2. private:
  3. void rec(TreeNode* root,vector<int> &ret){
  4. if(root != NULL){
  5. rec(root->left,ret);
  6. rec(root->right,ret);
  7. ret.push_back(root->val);
  8. }
  9. }
  10. public:
  11. vector<int> postorderTraversal(TreeNode* root) {
  12. vector<int> ret;
  13. rec(root,ret);
  14. return ret;
  15. }
  16. };

编程总结

常规。


后序遍历 迭代版

编程思想

使用了一个辅助结点p,这种写法其实可以看作是一个模版,对应的还有前序和中序的模版写法,形式很统一,方便于记忆。
由于后序遍历的顺序是左-右-根,而前序遍历的顺序是根-左-右,二者其实还是很相近的,我们可以先在先序遍历的方法上做些小改动,使其遍历顺序变为根-右-左,然后翻转一下,就是左-右-根了。翻转的方法,是反向加入结果res,每次都在结果res的开头加入结点值,而改变先序遍历的顺序就只要改变一下入栈顺序,先左后右,这样出栈处理的时候就是先右后左了。一定要对比前序遍历记忆!!!

拓展:当访问一个结点*p时,栈中结点恰好为*p结点的所有祖先。从栈底到栈底结点再加上*p结点,刚好构成从根节点到*p结点的一条路径。这一特性非常重要,如求根结点到某结点的路径;求两个结点的最近公共祖先;均可用这个思想。

编程实现

  1. class Solution {
  2. public:
  3. vector<int> postorderTraversal(TreeNode* root) {
  4. vector<int> res;
  5. stack<TreeNode*> s;
  6. TreeNode *p = root;
  7. while (!s.empty() || p) {
  8. if (p) {
  9. s.push(p);
  10. res.insert(res.begin(), p->val); //反向添加,而前序是正向添加
  11. p = p->right;  //与前序对比
  12. }
  13. else {
  14. TreeNode *t = s.top();
  15. s.pop();
  16. p = t->left;
  17. }
  18. }
  19. return res;
  20. }
  21. };

编程总结

拓展思想很重要!!


层次遍历 版本1

编程思想

进行常规层次遍历,需要借助一个队列。
先将根节点入队,然后出队,访问该根结点,如果它有左子树,则将左子树根节点入队,形成下一层;如果它有右子树,则将右子树根结点入队,形成下一层。然后出队,访问该结点,如此反复,直至队列为空。看代码比较容易懂。
补充:
queue 的基本操作有:
入队,如例:q.push(x); 将x 接到队列的末端。
出队,如例:q.pop(); 弹出队列的第一个元素,注意,并不会返回被弹出元素的值。
访问队首元素,如例:q.front(),即最早被压入队列的元素。
访问队尾元素,如例:q.back(),即最后被压入队列的元素。
判断队列空,如例:q.empty(),当队列空时,返回true。
访问队列中的元素个数,如例:q.size()

编程实现

  1. class Solution {
  2. public:
  3. vector<vector<int>> levelOrder(TreeNode* root) {
  4.  
  5. vector<vector<int>>v;
  6. queue<TreeNode*>q;
  7. q.push(root); //根节点入队
  8. if(root == NULL)
  9. return v ;
  10. while(!q.empty()){ //队列不空
  11. vector<int>vv;
  12. queue<TreeNode*> next ; // 建立第二个队列 用来存放下一层的结点
  13. while(!q.empty()){ //遍历当前层的结点 这层循环是核心 其他都是为了满足OJ输出
  14.  
  15. TreeNode* tre = q.front() ;
  16. vv.push_back(tre->val); //访问该结点,为了满足输出要求,所以有点复杂,
  17. q.pop(); //对头元素出队
  18. if(tre->left!=NULL){ //它有左子树
  19. next.push(tre->left);
  20. }
  21. if(tre->right!=NULL){ //它有右子树
  22. next.push(tre->right);
  23. }
  24. }
  25. v.push_back(vv);
  26. q=next; // // 遍历完后进入下一层
  27. }
  28. return v;
  29. }
  30. };

编程总结

层次遍历往往要用到队列,要对队列的基本操作熟悉,注意二维向量的生成。


层次遍历 版本2

编程思想

给定一个二叉树,返回其节点值自底向上的层次遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)。

同版本一类似,只不过需要多一个栈,把每层返回的结点加入栈中,最后输出。

编程实现

  1. class Solution {
  2. public:
  3. vector<vector<int>> levelOrderBottom(TreeNode* root) {
  4. vector<vector<int>>v;
  5. stack<vector<int>>s;
  6. queue<TreeNode*>q;
  7. q.push(root); //根节点入队
  8. if(root == NULL)
  9. return v ;
  10. while(!q.empty()){ //队列不空
  11. vector<int>vv;
  12. queue<TreeNode*> next ; // 建立第二个队列 用来存放下一层的结点
  13.  
  14. while(!q.empty()){ //遍历每层的结点 这层循环是核心 其他都是为了满足OJ输出
  15.  
  16. TreeNode* tre = q.front() ;
  17. vv.push_back(tre->val); //访问该结点,为了满足输出要求,所以有点复杂,
  18. q.pop(); //对头元素出队
  19. if(tre->left!=NULL){ //它有左子树
  20. next.push(tre->left);
  21. }
  22. if(tre->right!=NULL){ //它有右子树
  23. next.push(tre->right);
  24. }
  25.  
  26. }
  27. s.push(vv); //将每层结点入栈
  28. //v.push_back(vv);
  29. q=next; // // 遍历完后进入下一层
  30. }
  31. while(!s.empty()){ //将每层结点倒序输出
  32. v.push_back(s.top());
  33. s.pop();
  34. }
  35. return v;
  36. }
  37. };

编程总结

注意其他数据结构的配合使用。

剑指offer 树的基本操作:四种遍历方式的更多相关文章

  1. 剑指Offer(二十四):二叉树中和为某一值的路径

    剑指Offer(二十四):二叉树中和为某一值的路径 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.ne ...

  2. 剑指Offer(三十四):第一个只出现一次的字符

    剑指Offer(三十四):第一个只出现一次的字符 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.net ...

  3. lua中for循环的四种遍历方式

    lua中for的四种遍历方式区别 table.maxn 取最大的整数key #table 从1开始的顺序整数最大值,如1,2,3,6 #table == 3   key,value pairs 取每一 ...

  4. list的四种遍历方式

    1.手先增强for循环和iterator遍历的效果是一样的,也就说 增强for循环的内部也就是调用iteratoer实现的,但是增强for循环 有些缺点,例如不能在增强循环里动态的删除集合内容.不能获 ...

  5. Map 的四种遍历方式

    Map 的四种遍历方式 import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class ...

  6. java集合四种遍历方式

    package conection; import java.util.Iterator;import java.util.LinkedList;import java.util.List; publ ...

  7. map的四种遍历方式

    map是Java中非常常用的一种数据结构,但map不同于set和list都继承自Collection接口. 所以map没有实现Collection的Iterator 方法,自身没有迭代器来遍历元素. ...

  8. Map集合的四种遍历方式

    很久以前写的代码,和上一个做比较吧!便于以后查看 import java.util.HashMap; import java.util.Iterator; import java.util.Map; ...

  9. java list 的 四种遍历方式

    在java中遍历一个list对象的方法主要有以下四种: 1. For Loop —— 普通for循环 2. Advanced For Loop —— 高级for循环 3. Iterator Loop ...

随机推荐

  1. Codeforces Edu Round 54 A-E

    A. Minimizing the String 很明显,贪心之比较从前往后第一个不一样的字符,所以可以从前往后考虑每一位,如果把它删除,他这一位就变成\(str[i + 1]\),所以只要\(str ...

  2. 自定义3D地图

    基于echarts的3D地图进行,直接将这代码粘贴到echarts的demo中即可呈现效果 var mygeo = { // 标准的geojson格式 "type": " ...

  3. STL——容器(List)list 数据的存取

    list.front(); //返回第一个元素 list.back(); //返回最后一个元素 1 #include <iostream> 2 #include <list> ...

  4. js上 初识JavaScript

    1.JavaScript简介 **JavaScript ** 是什么?(重点) Js是一种专门为网页交互设计的客户端(浏览器端)的脚本语言: Js与html和css有相似之处,都在浏览器端解析: Js ...

  5. Eureka系列(四) 获取服务Server端具体实现

    获取服务 Server端流程   我们先看下面这张图片,这张图片简单描述了下我们EurekaClient在调用EurekaServer 提供的获取服务Http接口,Server端实现接口执行的大致流程 ...

  6. Qt QChart 创建图表

    Qt QChart 创建图表 @ 目录 Qt QChart 创建图表 效果 流程 代码 1. 饼图 2. 柱图 3. 折/曲线图 4. 区域图 效果 流程 graph LR q(value 数据) q ...

  7. 记一次真实的webpack优化经历

    前言 公司目前现有的一款产品是使用vue v2.0框架实现的,配套的打包工具为webpack v3.0.整个项目大概有80多个vue文件,也算不上什么大型项目. 只不过每次头疼的就是打包所耗费的时间平 ...

  8. Azure Service Bus(一)入门简介

    一,引言 今天开始学习新的内容 Azure Service Bus(服务总线),其实也叫 "云消息服务",和 RabbitMQ,KafKa的一样都是作为消息通信服务,但是它们直接还 ...

  9. 简丽Framework-开篇

    简丽Framework-开篇 ​ 简丽Framework 是一个开源java Web开发框架. ​ 开源的框架.库.组件等比比皆是,每个开源产品都有它的定位和价值. ​ 简丽Framework的定位是 ...

  10. 论文阅读: A Review of Robot Learning for Manipulation: Challenges, Representations, and Algorithms

    机器人学习操纵综述:挑战,表示形式和算法 1.介绍 因此,研究人员专注于机器人应如何学习操纵周围世界的问题. 这项研究的范围很广,从学习个人操作技巧到人类演示,再到学习适用于高级计划的操作任务的抽象描 ...