前言
记录三十五 介绍了二叉树基础,和递归法模版及遍历方式;
递归:代码简单,但要想清楚三步:
- 确定参数和返回值;
- 确定终止条件,并return什么?;
- 终止条件外的逻辑,在哪里做到自己调用自己?------应该是出现嵌套时候,要重复操作的时候。
递归的问题:递归次数多,开的新栈多,内存空间占用大。
如果二叉树前中后序遍历使用非递归方法,怎么操作?
一、"输入"学习阶段
解释迭代
重复执行一段代码,直到满足某个条件后停止。用循环可以实现重复;(递归也是重复)。
前序遍历
过程
模拟递归过程,用结构栈。
- 先把根节点放到栈中,再取出根节点加入遍历数组;
- 先放入右孩子,再放入左孩子。(左孩子先出来,说明先处理左子树)。
- 直到栈为空。
- 总结:取出根节点,拿右孩子和左孩子入栈交换 。先访问到中节点,可以直接处理中节点;再访问到左孩子(后入栈),处理左子树;再访问到右孩子(先入栈),处理右子树。访问节点顺序=处理节点顺序。
代码实现
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()){
TreeNode* cur = st.top();
st.pop();
if(cur != nullptr){
result.push_back(cur->val);
st.push(cur->right); //如果cur是叶子节点,把空也入栈,但是取到空的时候不操作。
st.push(cur->left);
}
}
return result;
}
};
上面的实现:把叶子结点的左右孩子是空,也入栈,但是不操作。
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if(root == nullptr) return result;
st.push(root);
while(!st.empty()){
TreeNode* cur = st.top();
st.pop();
result.push_back(cur->val);
if(cur->right) st.push(cur->right);
if(cur->left) st.push(cur->left);
}
return result;
}
};
这是参考代码,空就不入栈,所以可以在pop之后直接push_back。
后序遍历
过程
- 前序实现得到:中左右;
- 如果先放左孩子,再放右孩子;得到中右左;
- 把result最后整体reverse,得到左右中。
- 总结:先访问到中节点,可以直接处理中节点;再访问到右孩子(后入栈),处理右子树;再访问到左孩子(先入栈),处理左子树。访问节点顺序=处理节点顺序。最后reverse结果。
代码实现
参考代码:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode* > st;
if(root == nullptr) return result;
st.push(root);
while(!st.empty()){
TreeNode* cur = st.top();
st.pop();
result.push_back(cur->val);
if(cur->left) st.push(cur->left);
if(cur->right) st.push(cur->right);
}
reverse(result.begin(),result.end());
return result;
}
};
中序遍历
过程
有所区别原因:访问节点顺序不等于处理节点顺序。每一次先接触中间节点,但不能处理它,要找它的左子树;找到左子树,再处理它;再找右子树。
- 用栈来记录遍历过的元素,虽然访问到但是还没到要处理的时候。
- cur != 空,作为中节点,放入栈中;cur = cur->left;把left给到下一棒。
- 如果遇到空,说明左子树结束;需要从栈中取元素,放入result,st.pop();cur = cur->right;再把right给到下一棒。
- 当cur == 空且st栈为空,终止。cur==空,说明访问结束;st等于空,说明没有中节点可以取。
代码实现
-
根据思路,先自我实现:
cpp/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: vector<int> inorderTraversal(TreeNode* root) { vector<int> result; stack<TreeNode*> st; if(root == nullptr) return result; st.push(root); TreeNode* cur = root->left; while(1){ if(cur != nullptr){ st.push(cur); cur = cur->left; }else{ if(st.empty()) break; cur = st.top(); st.pop(); result.push_back(cur->val); cur = cur->right; } } return result; } };
-
对照参考代码:
去掉 if(st.empty()) break; while条件改为(cur != nullptr || !st.empty())
二、统一迭代遍历实现
思路学习
标记法
- 将访问的节点 放入栈中,把要处理的节点 也放入栈中但是标记要处理的节点。
- 如何标记?要处理的节点放入栈之后,紧接着放入一个空指针作为标记。
代码实现
- 前序遍历:中左右。所以cur不为空时,入栈顺序:右左中。
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if(root != nullptr) {st.push(root);}
while(!st.empty()){
TreeNode* cur = st.top();
if(cur != nullptr){
st.pop();
if(cur->right) st.push(cur->right);//先放入右孩子
if(cur->left) st.push(cur->left); //再放入左孩子
st.push(cur); //放入中节点,紧跟着NULL,代表处理过,它下一轮该进result
st.push(nullptr);
}else{
st.pop(); //把空指针先弹出
TreeNode* temp = st.top();//获取入数组的元素
st.pop(); //弹出该元素
result.push_back(temp->val);
}
}
return result;
}
};
- 中序遍历:左中右。所以cur不为空时,入栈顺序:右中左。
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if(root != nullptr) {st.push(root);}
while(!st.empty()){
TreeNode* cur = st.top();
if(cur != nullptr){
st.pop();
if(cur->right) st.push(cur->right);//先放入右孩子
st.push(cur); //放入中节点,紧跟着NULL,代表处理过,它下一轮该进result
st.push(nullptr);
if(cur->left) st.push(cur->left); //再放入左孩子
}else{
st.pop(); //把空指针先弹出
TreeNode* temp = st.top();//获取入数组的元素
st.pop(); //弹出该元素
result.push_back(temp->val);
}
}
return result;
}
};
- 后序遍历:左右中。所以cur不为空时,入栈顺序:中右左。
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
if(root != nullptr) st.push(root);
while(!st.empty()){
TreeNode* cur = st.top();
if(cur != nullptr){
st.push(nullptr); //先放中节点,直接放null,标记下
if(cur->right) st.push(cur->right);//再放右孩子
if(cur->left) st.push(cur->left);//再放左孩子
}else{
st.pop();
TreeNode* temp = st.top();
st.pop();
result.push_back(temp->val);
}
}
return result;
}
};
总结
本文学习迭代遍历二叉树。用循环+栈处理。
- 非统一的方法:
- 前序遍历:根节点先入栈,每出栈一个根节点,就把右左孩子放入。相当于交换。
- 后序遍历:中左右------中右左------左右中;
- 中序遍历:栈放遍历的元素,不为空,把left交到下一棒;为空,取出中节点;把right交到下一棒。
- 统一方法:标记法。要处理的节点后面紧跟着是null,取到栈顶是空,代表要入栈。直到整个栈空。
- 前序遍历:不为空,放入顺序:右左中;
- 中序遍历:不为空,放入顺序:右中左;
- 后序遍历:不为空,放入顺序:中右左;
(欢迎指正,转载标明出处)