迈出第一步,需要勇气
Contents
-
- 0 捋清概念
- 1 层次遍历
- 1. 1 二叉树的层次遍历(102)
- 1.2 二叉树的层次遍历 II(107)
- 1.3.二叉树的层平均值(637)
- 1.4.找树左下角的值(513)
- 2 前中后序遍历
- 2.1 递归模板实现
- 2.2 非递归实现二叉树的前序遍历(144)
- 2.3 非递归实现二叉树的后序遍历(145)
- 2.4 非递归实现二叉树的中序遍历(94)
- 3 总结
ambience [ˈæmbiəns]
n. 环境; 气氛; 格调;
0 捋清概念
首先看一下二叉树的前中后序遍历是以怎么样的访问顺序规定的。规则是这样的:前序遍历:中左右
中序遍历:左中右
后序遍历:左右中
![3e9292492f1c974d2eb7c7c6a52d498f.png](https://img-blog.csdnimg.cn/img_convert/3e9292492f1c974d2eb7c7c6a52d498f.png)
- 前序遍历(中左右):5 4 1 2 6 7 8
- 中序遍历(左中右):1 4 2 5 7 6 8
- 后序遍历(左右中):1 2 4 7 8 6 5
- 前序遍历(递归法,迭代法)
- 中序遍历(递归法,迭代法)
- 后序遍历(递归法,迭代法)
- 层次遍历(迭代法)
1 层次遍历
1. 1 二叉树的层次遍历(102)
题目: 给你一个二叉树,请你返回其按层序遍历得到的节点值。(即逐层地,从左到右访问所有节点)。示例:class Solution {public: vector> levelOrder(TreeNode* root) { queue que; vector> res; if(root != NULL) que.push(root); while(!que.empty()){ int size = que.size(); vector temp; for(int i=0; i TreeNode* node = que.front(); que.pop(); temp.push_back(node->val); if(node->left) que.push(node->left); if(node->right) que.push(node->right); } res.push_back(temp); } return res; }};
1.2 二叉树的层次遍历 II(107)
示例:reverse(res.begin(),res.end())
实现。
class Solution {public: vector> levelOrderBottom(TreeNode* root) { queue que; vector> res; if(root != NULL) que.push(root); while(!que.empty()){ vector temp; int size = que.size(); for(int i=0; i TreeNode * node = que.front(); que.pop(); temp.push_back(node->val); if(node->left) que.push(node->left); if(node->right) que.push(node->right); } res.push_back(temp); } reverse(res.begin(), res.end()); return res; }};
1.3.二叉树的层平均值(637)
题目: 给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。 示例:class Solution {public: vector averageOfLevels(TreeNode* root) { queue que; if(root != NULL) que.push(root); vector res; while(!que.empty()){ int size = que.size(); double sum = 0; for(int i=0; i TreeNode* temp= que.front(); que.pop(); sum += temp->val; if(temp->left) que.push(temp->left); if(temp->right) que.push(temp->right); } res.push_back(sum / size); } return res; }};
1.4.找树左下角的值(513)
题目: 给定一个二叉树,在树的最后一行找到最左边的值。
示例:
class Solution {public: int dep(TreeNode* node){ if(node == NULL) return 0; return max(dep(node->left), dep(node->right)) + 1; } int findBottomLeftValue(TreeNode* root) { int depth = dep(root); queue que; if(root !=NULL) que.push(root); for(int i=0; i int size = que.size(); for(int j=0; j if(i == depth -1 && !que.empty()) return (que.front())->val; TreeNode* node = que.front(); que.pop(); if(node->left) que.push(node->left); if(node->right) que.push(node->right); } } return 0; }};
2 前中后序遍历
Leetcode中以下三道题是关于树的前中后序遍历,且面试中常常出现。如下对比着来看这三种遍历。- 二叉树的前序遍历(144)
- 二叉树的后序遍历(145)
- 二叉树的中序遍历(94)
2.1 递归模板实现
递归三部曲(每次使用递归考虑的三个点):确定递归函数的参数和返回值
因为要打印出前序遍历节点的数值,所以参数里需要传入节点和一个vector用于存放节点的值,函数不需要有返回值,故返回类型为void;
确定递归基
当节点为空时,直接return即可;
确定单层递归的逻辑
前序遍历是中左右的循序,所以单层递归的逻辑是要先取中节点的值存入vector中,然后再递归左、右子节点即可。
class Solution {public: void handdle(TreeNode* node, vector& res){ # 注意这里的形参res类型为引用哦! if(node == NULL) return; # 以下为单层递归的逻辑,只需根据前中后序遍历树的节点顺序即可。 res.push_back(node->val); # 中 handdle(node->left, res); # 左 handdle(node->right, res); # 右 } vector preorderTraversal(TreeNode* root) { vector out; handdle(root, out); return out; }};
2.2 非递归实现二叉树的前序遍历(144)
思路: 前序遍历是中左右,每次先处理的是中间节点,那么先将跟节点放入栈中,然后将右孩子加入栈,再加入左孩子。 为什么要先加入右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。 动画理解:(来自代码随想录)![942881f318b3659b1eeb98632cd81787.gif](https://img-blog.csdnimg.cn/img_convert/942881f318b3659b1eeb98632cd81787.gif)
class Solution {public: vector preorderTraversal(TreeNode* root) { vector res; stack stk; if(root != NULL) stk.push(root); while(!stk.empty()){ TreeNode* cur = stk.top(); stk.pop(); res.push_back(cur->val); # 中 if(cur->right) stk.push(cur->right); # 右 if(cur->left) stk.push(cur->left); # 左 } return res; }};
2.3 非递归实现二叉树的后序遍历(145)
思路: 对于二叉树的前序遍历,其节点访问顺序为中左右;而对于二叉树的后序遍历,其节点访问顺序为左右中,可以将迭代实现前序遍历中的左右顺序颠倒,变成中右左,然后将结果翻转即可变为左右中,及得到二叉树的后序遍历结果。![e510ce462b1ec3c45a75deb2a15fb8da.png](https://img-blog.csdnimg.cn/img_convert/e510ce462b1ec3c45a75deb2a15fb8da.png)
class Solution {public: vector<int> postorderTraversal(TreeNode* root) { vector<int> res; stack stk; if(root != nullptr) stk.push(root); while(!stk.empty()){ TreeNode* cur = stk.top(); stk.pop(); res.push_back(cur->val); if(cur->left) stk.push(cur->left); if(cur->right) stk.push(cur->right); } reverse(res.begin(), res.end()); return res; }};
2.4 非递归实现二叉树的中序遍历(94)
思路: 咦,为什么后序遍历仅仅是改一下前序遍历的节点访问顺序就可以,而中序遍历不行呢? 对于我们总说的三种顺序,在实现时总是先获得中间(根)节点的。首先前序遍历比较简单,是因为首先拿到中节点的值,把它存入容器中后就可以将它从栈中pop掉;而后序遍历利用了顺序的对称性,使得仅仅改变节点访问顺序而得以简单实现。对于中序遍历,首先拿到中节点的值,需要将该值压入栈中之后再去获得该值的左子节点,即首先拿到的值不可以被直接处理,故中序遍历比较麻烦一些。动画理解:(来自代码随想录)![12e1af9ace26ed663a2543381d6a1c97.gif](https://img-blog.csdnimg.cn/img_convert/12e1af9ace26ed663a2543381d6a1c97.gif)
class Solution {public: vector inorderTraversal(TreeNode* root) { vector res; stack stk; TreeNode* cur = root; while(cur != nullptr || !stk.empty()){ if(cur!=nullptr){ stk.push(cur); cur = cur->left; # 左 } else{ cur = stk.top(); stk.pop(); res.push_back(cur->val); # 中 cur = cur->right; # 右 } } return res; }};
3 总结
对于层次遍历的迭代实现,记得使用 队列存放每一层的节点值,记得利用queue.size()获取每一层的节点个数;层次遍历没有递归实现; 对于前中后序遍历的递归实现,把一个 模板背会就搞定了; 对于前中后序遍历的迭代实现,首先记得使用 栈,其次前序遍历和后序遍历有相似性,而中序遍历需要不断寻找每个节点的左子节点,遇到空时返回当前栈顶元素,此时的栈顶元素即为我们所说的"中",返回"中"之后再去访问"右"即可。![400b6a06135ff3ccc2696a4a223450e8.gif](https://img-blog.csdnimg.cn/img_convert/400b6a06135ff3ccc2696a4a223450e8.gif)
往期推荐
![400b6a06135ff3ccc2696a4a223450e8.gif](https://img-blog.csdnimg.cn/img_convert/400b6a06135ff3ccc2696a4a223450e8.gif)
【数据结构】—— 列表 / 链表
![400b6a06135ff3ccc2696a4a223450e8.gif](https://img-blog.csdnimg.cn/img_convert/400b6a06135ff3ccc2696a4a223450e8.gif)
![400b6a06135ff3ccc2696a4a223450e8.gif](https://img-blog.csdnimg.cn/img_convert/400b6a06135ff3ccc2696a4a223450e8.gif)
【数据结构】Leetcode栈与队列经典题
![400b6a06135ff3ccc2696a4a223450e8.gif](https://img-blog.csdnimg.cn/img_convert/400b6a06135ff3ccc2696a4a223450e8.gif)
【数据结构】Leetcode——树 递归经典题