您的位置:首页 > 汽车 > 时评 > 算法力扣刷题 三十六【二叉树迭代遍历】

算法力扣刷题 三十六【二叉树迭代遍历】

2024/11/16 10:48:37 来源:https://blog.csdn.net/danaaaa/article/details/140267535  浏览:    关键词:算法力扣刷题 三十六【二叉树迭代遍历】

前言

记录三十五 介绍了二叉树基础,和递归法模版及遍历方式;
递归:代码简单,但要想清楚三步:

  • 确定参数和返回值;
  • 确定终止条件,并return什么?;
  • 终止条件外的逻辑,在哪里做到自己调用自己?——应该是出现嵌套时候,要重复操作的时候。

递归的问题:递归次数多,开的新栈多,内存空间占用大。

如果二叉树前中后序遍历使用非递归方法,怎么操作?


一、“输入”学习阶段

学习参考链接

解释迭代

重复执行一段代码,直到满足某个条件后停止。用循环可以实现重复;(递归也是重复)。

前序遍历

过程

模拟递归过程,用结构栈。

  • 先把根节点放到栈中,再取出根节点加入遍历数组;
  • 先放入右孩子,再放入左孩子。(左孩子先出来,说明先处理左子树)。
  • 直到栈为空。
  • 总结:取出根节点,拿右孩子和左孩子入栈交换。先访问到中节点,可以直接处理中节点;再访问到左孩子(后入栈),处理左子树;再访问到右孩子(先入栈),处理右子树。访问节点顺序=处理节点顺序

代码实现

/*** 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;}
};

上面的实现:把叶子结点的左右孩子是空,也入栈,但是不操作。

/*** 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结果。

代码实现

参考代码:

/*** 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等于空,说明没有中节点可以取。

代码实现

  • 根据思路,先自我实现:
    /*** 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不为空时,入栈顺序:右左中。
/*** 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,代表处理过,它下一轮该进resultst.push(nullptr);}else{st.pop();   //把空指针先弹出TreeNode* temp = st.top();//获取入数组的元素st.pop();   //弹出该元素result.push_back(temp->val); }}return result;}
};
  • 中序遍历:左中右。所以cur不为空时,入栈顺序:右中左。
/*** 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,代表处理过,它下一轮该进resultst.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不为空时,入栈顺序:中右左。
/*** 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,取到栈顶是空,代表要入栈。直到整个栈空。
    • 前序遍历:不为空,放入顺序:右左中;
    • 中序遍历:不为空,放入顺序:右中左;
    • 后序遍历:不为空,放入顺序:中右左;

(欢迎指正,转载标明出处)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com