二叉树遍历

二叉树的遍历主要有四种:

前序、中序、后序和层序

遍历的实现方式主要是:

递归和非递归

递归遍历的实现非常容易,非递归的实现需要用到栈,难度系数要高一点。

1.二叉树节点的定义

二叉树的每个节点由节点值、左子树和右子树组成。

class TreeNode{
public:int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(NULL), right(NULL) {}
}

2.二叉树的遍历方式

前序遍历:先访问根节点,再访问左子树,最后访问右子树

中序遍历:先访问左子树,再访问根节点,最后访问右子树

后序遍历:先访问左子树,再访问右子树,最后访问根节点

层序遍历:每一层从左到右访问每一个节点。

举例说明:(以下面的二叉树来说明这四种遍历)

前序遍历:ABDFGHIEC
中序遍历:FDHGIBEAC
后序遍历:FHIGDEBCA
层序遍历:ABCDEFGHI

3.前序遍历

递归版本

按照遍历的
顺序很容易就能写出下列代码:

以下代码均在leetcode测试通过,二叉树前序遍历的原题链接:戳我!leetcode直通车!上车啦!

vector<int> preorderTraversal(TreeNode* root){vector<int> ret;dfsPreOrder(root,ret);return ret;
}
void dfsPreOrder(TreeNode* root,vector<int> &ret){if(root==NULL) return;ret.push_back(root->val);//存储根节点if(root->left!=NULL) dfsPreOrder(root->left,ret);//访问左子树if(root->right!=NULL) dfsPreOrder(root->right,ret);//访问右子树
}

非递归版本

非递归版本需要利用辅助栈来实现

1.首先把根节点压入栈中
2.此时栈顶元素即为当前根节点,弹出并访问即可
3.把当前根节点的右子树和左子树分别入栈,考虑到栈是先进后出,所以必须右子树先入栈,左子树后入栈
4.重复2,3步骤,直到栈为空为止

vector<int> preorderTraversal(TreeNode* root) {vector<int> ret;if (root==NULL) return ret;stack<TreeNode*> st;st.push(root);while(!st.empty()){TreeNode* tp = st.top();//取出栈顶元素st.pop();ret.push_back(tp->val);//先访问根节点if(tp->right!=NULL) st.push(tp->right);//由于栈时先进后出,考虑到访问顺序,先将右子树压栈if(tp->left!=NULL) st.push(tp->left);//将左子树压栈}return ret;
}

4.中序遍历

递归版本

中序遍历的访问顺序依次是左子树->根节点->右子树,按照递归的思想依次访问即可

以下代码均在leetcode测试通过,二叉树中序遍历的原题链接:戳我!leetcode直通车!上车啦!

vector<int> inorderTraversal(TreeNode* root) {vector<int> ret;inorder(root,ret);return ret;
}
void inorder(TreeNode* p,vector<int>& ret)
{if(p==NULL) return;inorder(p->left,ret);//访问左子树ret.push_back(p->val);//访问根节点inorder(p->right,ret);//访问右子树
}

非递归版本

中序遍历的非递归版本比前序稍微复杂一点,除了用到辅助栈之外,还需要一个指针p指向下一个待访问的节点

如果p非空,则将p入栈,p指向p的左子树
如果p为空,说明此时左子树已经访问到尽头了,弹出当前栈顶元素,进行访问,并把p设置成p的右子树的左子树,即下一个待访问的节点

vector<int> inorderTraversal(TreeNode* root) {vector<int> ret;TreeNode* p = root;stack<TreeNode*> st;while(!st.empty()||p!=NULL){if(p){//p非空,代表还有左子树,继续st.push(p);p=p->left;}else{//如果为空,代表左子树已经走到尽头了p = st.top();st.pop();ret.push_back(p->val);//访问栈顶元素if(p->right) {st.push(p->right);//如果存在右子树,将右子树入栈p = p->right->left;//p始终为下一个待访问的节点}else p=NULL;}}return ret;
}

5.后序遍历

递归版本

递归版本还是一样,按照访问顺序来写代码即可。

以下代码均在leetcode测试通过,二叉树后序遍历的原题链接:戳我!leetcode直通车!上车啦!

vector<int> inorderTraversal(TreeNode* root) {vector<int> ret;inorder(root,ret);return ret;}
void inorder(TreeNode* p,vector<int>& ret)
{if(p==NULL) return;inorder(p->left,ret);//访问左子树inorder(p->right,ret);//访问右子树ret.push_back(p->val);//访问根节点
}

非递归版本

采用一个辅助栈和两个指针p和r,p代表下一个需要访问的节点,r代表上一次需要访问的节点

1、如果p非空,则将p入栈,p指向p的左子树

2、如果p为空,代表左子树到了尽头,此时判断栈顶元素

如果栈顶元素存在右子树且没有被访问过(等于r代表被访问过),则右子树入栈,p指向右子树的左子树
如果栈顶元素不存在或者已经被访问过,则弹出栈顶元素,访问,然后p置为null,r记录上一次访问的节点p

vector<int> postorderTraversal(TreeNode* root) {vector<int> ret;TreeNode* p = root;stack<TreeNode*> st;TreeNode* r = NULL;while(p||!st.empty()){if(p){st.push(p);p = p -> left;}else{p = st.top();if(p->right&&p->right!=r){p = p->right;st.push(p);p = p->left;}else {p = st.top();st.pop();ret.push_back(p->val);r= p;p = NULL;}}}return ret;
}

还有另一种解法,大家可以看看前序遍历的非递归版本,访问顺序依次是根节点->左子树->右子树,如果将压栈顺序改动一下,可以很容易得到根节点->右子树->左子树,观察这个顺序和后序遍历左子树->右子树->根节点正好反序。

vector<int> postorderTraversal(TreeNode* root) {vector<int> ret;if(root==NULL) return ret;stack<TreeNode*> st;st.push(root);while(!st.empty()){TreeNode* tmp = st.top();ret.push_back(tmp->val);//先访问根节点st.pop();if(tmp->left!=NULL) st.push(tmp->left);//再访问左子树if(tmp->right!=NULL) st.push(tmp->right);//最后访问右子树}reverse(ret.begin(),ret.end());//将结果反序输出return ret;
}

6.层序遍历

层序遍历,即按层序从左到右输出二叉树的每个节点。如例子中的A(第一层)BC(第二层)DE(第三层)FG(第四层)HI(第五层)

层序遍历需要借助队列queue来完成,因为要满足先进先去的访问顺序。具体思路看代码:

以下代码均在leetcode测试通过,二叉树层序遍历的原题链接:戳我!leetcode直通车!上车啦!

vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> ret;if(root==NULL) return ret;queue<TreeNode*> que;que.push(root);while(!que.empty()){vector<int> temp;queue<TreeNode*> tmpQue;//存储下一层需要访问的节点while(!que.empty())//从左到右依次访问本层{TreeNode* tempNode = que.front();que.pop();temp.push_back(tempNode->val);if(tempNode->left!=NULL) tmpQue.push(tempNode->left);//左子树压入队列if(tempNode->right!=NULL) tmpQue.push(tempNode->right);//右子树压入队列}ret.push_back(temp);que=tmpQue;//访问下一层}return ret;
}

7.其他经典考题

根据前序和中序遍历来构造二叉树

前序遍历的顺序是:根节点->左子树->右子树,中序遍历的顺序时:左子树->根节点->右子树。

在前序遍历中第一个节点为根节点,然后去中序遍历中找到根节点,则其左边为左子树,右边为右子树

例如前序遍历ABC,中序遍历BAC,在前序遍历中找到根节点A,在中序遍历中A的左边B为左子树,右边C为右子树。

然后一次递归下去,就可以把整棵数构造出来了。

以下代码均在leetcode测试通过,构造二叉树的原题链接:戳我!leetcode直通车!上车啦!

typedef vector<int>::iterator vi;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {if(preorder.empty()||inorder.empty()) return (TreeNode*)NULL;vi preStart = preorder.begin();vi preEnd = preorder.end()-1;vi inStart = inorder.begin();vi inEnd = inorder.end()-1;return constructTree(preStart,preEnd,inStart,inEnd);
}
TreeNode* constructTree(vi preStart,vi preEnd,vi inStart,vi inEnd)
{if(preStart>preEnd||inStart>inEnd) return NULL;//前序遍历的第一个节点为根节点TreeNode* root = new TreeNode(*preStart);if(preStart==preEnd||inStart==inEnd) return root;vi rootIn = inStart;while(rootIn!=inEnd){//在中序遍历中找到根节点if(*rootIn==*preStart) break;else ++rootIn;}root->left = constructTree(preStart+1,preStart+(rootIn-inStart),inStart,rootIn-1);//递归构造左子树root->right = constructTree(preStart+(rootIn-inStart)+1,preEnd,rootIn+1,inEnd);//递归构造右子树return root;
} 

根据中序和后序遍历构造二叉树

与上面的题目比较相似,后序遍历中最后一个节点为根节点,然后在中序遍历中找到根节点,左边为左子树,右边为右子树。

以下代码均在leetcode测试通过,构造二叉树的原题链接:戳我!leetcode直通车!上车啦!

TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {if(inorder.empty()||postorder.empty()) return NULL;return constructTree(inorder,postorder,0,inorder.size()-1,0,postorder.size()-1);
}
TreeNode* constructTree(vector<int>& inorder, vector<int>& postorder, int inStart,int inEnd,int postStart,int postEnd)
{if(postStart>postEnd||inStart>inEnd) return NULL;TreeNode* root = new TreeNode(postorder[postEnd]);if(postStart==postEnd||inStart==inEnd) return root;int i ;for(i = inStart ;i<inEnd;i++)//在中序遍历中找到根节点{if(inorder[i]==postorder[postEnd]) break;}root->left = constructTree(inorder,postorder,inStart,i-1,postStart,postStart+i-inStart-1);//递归构造左子树root->right = constructTree(inorder,postorder,i+1,inEnd,postStart+i-inStart,postEnd-1);//递归构造右子树return root;
}

求二叉树的深度

采用深度优先搜索,可以很容易计算出深度

以下代码均在leetcode测试通过,二叉树的深度的原题链接:戳我!leetcode直通车!上车啦!

int maxDepth(TreeNode* root) {return DfsTree(root);
}
int DfsTree(TreeNode* root){if(root==NULL) return 0;int left = DfsTree(root->left);//左子树的深度int right = DfsTree(root->right);//右子树的深度return left>right?left+1:right+1;//比较左右子树的深度,取最大值
}

判断是否为平衡二叉树

利用上面求深度的思想,求出左右子树的深度,判断它们相差是否大于1,如果大于则返回false。

以下代码均在leetcode测试通过,判断平衡二叉树的原题链接:戳我!leetcode直通车!上车啦!

bool isBalanced(TreeNode* root) {return dfsTree(root)!=-1;
}
int dfsTree(TreeNode* root)
{if(root==NULL) return 0;int left = dfsTree(root->left);//求左子树的深度if(left == -1) return -1;//返回-1代表左子树不平衡int right = dfsTree(root->right);//求右子树的深度if(right== -1) return -1;//返回-1代表右子树不平衡if(abs(left-right)>1) return -1;//如果左右子树均平衡,则判断它们是否相差小于等于1return max(left,right)+1;//返回该根节点树的深度
}

原博客地址:http://blog.csdn.net/terence1212/article/details/52182836

全面剖析【二叉树】的各类遍历方法相关推荐

  1. 二叉树中序遍历方法实现

    对于二叉树的遍历,先序的方式是比较简单的,但是中序和后序的方式还是有点麻烦的,这里先给出一个用C++stack的遍历方式: 1.如果当前结点不为空把当前结点压入栈p=p->left转向其左孩子 ...

  2. 二叉树的六种遍历方法汇总(转)

    原文地址 ,两种思路都不错 第一种 前序 void preOrder(Node *p) //非递归 {if(!p) return;stack<Node*> s;Node *t;s.push ...

  3. 广度优先遍历类似于二叉树的_二叉树的各种遍历方法的简单解释

    二叉树顾名思义,最多两个孩子. 一般规定一个二叉树,因为节点间有相互连接的原因,所以只要给定根节点,那么顺着寻找左孩子和右孩子便可以遍历到所有的节点,这就是遍历的直观解释. 而遍历分为深度遍历和广度遍 ...

  4. resultset不支持循环遍历_二叉树的各种遍历方法的简单解释

    二叉树顾名思义,最多两个孩子. 一般规定一个二叉树,因为节点间有相互连接的原因,所以只要给定根节点,那么顺着寻找左孩子和右孩子便可以遍历到所有的节点,这就是遍历的直观解释. 而遍历分为深度遍历和广度遍 ...

  5. 二叉树的各种遍历方法!(JAVA)

    接着上次对二叉树的创建,获取,删除,添加 这次我们做一下查找二叉树的最小的键和最大的键. 一.查找二叉树中最小的键和最大的键 在某些情况下,我们需要查找出树中存储所有元素的键的最小值,这里我们设计如下 ...

  6. 数据结构(二十)二叉树的递归遍历算法

    一.二叉树的遍历的定义 1.二叉树的遍历(traversing binary tree)是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问依次且仅被访问依次.树的结点之间不存在 ...

  7. python非递归前序遍历二叉树_Python非递归实现二叉树的后续遍历

    leetcode 145. Binary Tree Postorder Traversal 思路一: 使用一个栈stack保存经过的根结点,另一个栈flag保存每个结点的右子树是否遍历: 如果根结点存 ...

  8. Python实现二叉树的三种深度遍历方法!

    python代码实现了二叉树,这次将会实现二叉树的几种遍历方法,来更好的解析二叉树的结构特点.分别是一种广度遍历,和三种深度遍历方法:先序遍历,中序遍历,后序遍历.下面是代码实现: 1.先序遍历 遍历 ...

  9. 数据结构与算法 | 二叉树四种的遍历方法(递归与非递归)

    二叉树的遍历是指从根节点出发,按照某种次序依次访问二叉树的所有节点,使得每个节点被访问且只访问一次. 而一般有四种遍历方法: 前序.中序.后序.层序,下面就分别讲一下四个遍历的思路以及代码实现 例如我 ...

最新文章

  1. 汪潮涌:AI创业落地为王,技术和算法难以成为核心壁垒
  2. 对抗攻击最新研究:仅修改「一个像素」即可骗过神经网络!
  3. Hyper-V 2016 系列教程25 配置NFS 存储服务器
  4. spring cloud 学习之 服务注册和发现(Eureka)
  5. python协程实时输出_python协程
  6. SQLite 日期 时间
  7. 如何根据SAP Spartacus的页面快速找到实现的Angular Component
  8. 网络视频贴片广告全面推行第三方监测
  9. Onew积极开拓国际市场,为全球用户提供全方位金融服务
  10. [Luogu] P1939 【模板】矩阵加速(数列)
  11. mvc 怎么把后台拼接好的div写到前台_MVC 从后台页面 取前台页面传递过来的值的几种取法...
  12. java x锁_基于Java名称的锁?
  13. 改写教科书!Science揭示:为什么你体内的癌细胞没发展成癌症?
  14. Linux之nmap扫描多网段
  15. (vivo)安卓神器xposed框架Root安装指南
  16. 游戏引擎设计的技术及详解
  17. win11 如何将搜狗输入法设置成默认输入法
  18. Linux 邻居子系统介绍
  19. 软件测试工程师应届生工资,软件测试工程师薪水平均是什么水平?前景发展如何?...
  20. 基于图像的三维重建系统概览

热门文章

  1. BERT:代码解读、实体关系抽取实战
  2. 《研磨设计模式》chap22 装饰模式Decorator(1)模式简介
  3. C++中的 c_str() 函数
  4. 347. 前 K 个高频元素(哈希表)
  5. buu [BJDCTF 2nd]cat_flag
  6. 解决关于 ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it....报错
  7. MySQL—外连接查询
  8. System.img是如何打包的
  9. 【Web安全】通过机器学习破解验证码图片
  10. 应用session对象实现用户登录