二叉树的递归遍历
144.二叉树的前序遍历、145.二叉树的后序遍历、94.二叉树的中序遍历
文章讲解:二叉树的递归遍历
视频讲解:每次写递归都要靠直觉? 这次带你学透二叉树的递归遍历!
状态:搞清楚本题就一定要先搞清楚递归是什么,曾经可能需要靠栈来想想递归的工作流程,现在我觉得树结构就很好得向我们展示了递归这个方法。
思路
首先搞清楚前中后序的遍历方法,这里以前序遍历进行举例。
![](https://i-blog.csdnimg.cn/blog_migrate/5b1beab8dea593b521756091d8c7e77e.png#pic_center)
如果用递归的思路来思考前序遍历,其实就是每到一个结点,我们就对其先进行处理,然后分别去遍历左、右孩子。
要写明白递归,就要有三大要素:
- 确定递归的参数和返回值:这里我们遍历到结点处,通过数组进行存储,所以返回值是void,数组作为参数传进来即可:
void traversal(TreeNode* cur, vector<int> vec){
...
}
- 确定终止条件:在递归的过程中,如何算是递归结束了呢,当然是当前遍历的节点是空了,那么本层递归就要结束了,所以如果当前遍历的这个节点是空,就直接return,代码如下:
if (cur == NULL) return;
- 确定单层递归的逻辑:前序遍历是中左右的循序,所以在单层递归的逻辑,是要先取中节点的数值,代码如下:
vec.push_back(cur->val); // 中
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
剩下的中序遍历、后序遍历只要把遍历顺序换个位置即可:
//中序
traversal(cur->left, vec); // 左
vec.push_back(cur->val); // 中
traversal(cur->right, vec); // 右
//后序
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
vec.push_back(cur->val); // 中
CPP代码
//前序遍历
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
vec.push_back(cur->val); // 中
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
二叉树的迭代遍历
144.二叉树的前序遍历、145.二叉树的后序遍历、94.二叉树的中序遍历
文章讲解:二叉树的迭代遍历
视频讲解:写出二叉树的非递归遍历很难么?(前序和后序)、写出二叉树的非递归遍历很难么?(中序))
状态:迭代中的第一步没处理好,一直在想我怎么在迭代中让结点遍历起来呢?本质就是栈里面存储的其实不是结点数值,而是把整个结点都存储进去了。
迭代遍历总结两点:栈内存储的是结点,这样才知道如何结点是如何遍历起来的;把结点存储在栈的核心就是,只要不处理,就不应该弹出,处理的时候才弹出。
思路
这篇文章已经把思路解释的非常清楚了:二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法
前序遍历
- 搞清楚栈内到底存的是值还是结点:存储结点,如果存储值的话,我们没有手段去遍历二叉树了
记得提前存入根结点,如果没有根结点,直接返回空。
- 搞清楚循环体内如何去遍历整个二叉树:
同第三点的强调,栈顶元素是哪一个,我们才遍历哪一个
- 搞清楚在模拟出栈入栈过程中,我们关注的其实是栈顶结点,栈顶结点跑到哪里,我们就遍历哪个子树。
TreeNode* node = st.top();
st.pop();
if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
整体代码如下:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top(); // 中
st.pop();
result.push_back(node->val);
if (node->right) st.push(node->right); // 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return result;
}
};
后序遍历
前序遍历是中左右-----(调整代码左右循环)---->中右左----(反转result数组)---->左右中
我们的后序遍历就是左右中!所以前序遍历的代码很重要!
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> res;
if (root == nullptr) return res;
st.push(root);
while(!st.empty()) {
TreeNode* node = st.top();
st.pop();
res.push_back(node->val);
if(node->left) st.push(node->left);
if(node->right) st.push(node->right);
}
int left = 0;
int right = res.size() - 1;
while(left <= right) {
swap(res[left], res[right]);
left++;
right--;
}
return res;
}
};
// 关于反转result数组,还可以用STL算法
reverse(result.begin(), result.end());
中序遍历
中序遍历最难的点就在于,我们遍历的结点不是我们要处理的结点,我们遍历过该结点后可能要到后面才能去处理,这样应该怎么办呢?一个办法:借助指针
我们用指针来遍历整个二叉树,然后用栈来存储遍历的顺序。
再进一步,中序遍历的规矩就是,左孩子不是空,左孩子压栈,如果空了(左),就要处理栈顶元素(中),然后压入右孩子,如果右孩子是空,那就继续处理栈顶元素和栈顶的右孩子(右)。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<int> st;
vector<int> result;
TreeNode* cur = root;
while(cur != NULL|| !st.empty()){ //0 1 则 1
if(cur != NULL){
st.push(cur);
cur = cur->left;
}else{
cur = st.top();
st.pop();
result.push_bacl(cur->val);
cur->right;
}
}
return result;
}
};
二叉树的统一迭代法
文章讲解:二叉树的统一迭代法
状态:感觉有点厉害,以后有时间在进行研究。