二叉树迭代(非递归)遍历方法

35 篇文章 3 订阅
9 篇文章 0 订阅


所有方法都可以在力扣上验证
前序题目链接
中序题目链接
后序题目链接

结构定义

	// Definition for a binary tree node.
	struct TreeNode {
	    int val;
	    TreeNode *left;
	    TreeNode *right;
	    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
	};

思路

递归转换为非递归需要用到栈,而前中后三种遍历方式都有一种统一的思路,只不过后序比较特殊一点
这里先给出大体的框架。
vt是遍历的结果,st存放的是遍历过程中的结点,对root进行操作,根据需要让它转到左孩子或者右孩子,并且在遇到空结点的时候做出相应操作

	vector<int> func(TreeNode* root){
		vector<int> vt;
		if(!root)return vt;
		stack<TreeNode*> st;
		while(root||!st.empty()){
			if(root){
				根据需要操作
			}
			else{
				根据需要操作
			}
		}
	}

前序迭代

方法一 通用方法

    vector<int> preorderTraversal(TreeNode* root) {
        vector<int>vt;
        if(!root)return vt;
        stack<TreeNode*>st;
        while(root||!st.empty()){
            if(root){
            	//访问根结点
                vt.push_back(root->val);
                //根节点入栈,在root为空时退回到该子树的根结点
                st.push(root);
                //遍历左子树
                root=root->left;
            }
            else{
            	//取出该子树的根节点
                root=st.top();
                st.pop();
                //访问右子树
                root=root->right;
            }
        }
        return vt;
    }

可以模拟一下加深印象
执行结果
在这里插入图片描述

方法二

先序遍历还有一个方法,结构很像递归遍历

    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> vt;
        if(!root) return vt;
        stack<TreeNode*> st;
        st.push(root);
        while(!st.empty())
        {
        	//取栈顶,访问根节点
            TreeNode* node=st.top();
            st.pop();
            vt.push_back(node->val);
            //右孩子先入栈,这样能保证左孩子先被访问
            if(node->right) st.push(node->right);
            if(node->left) st.push(node->left);
        }
        return vt;
    }

运行结果
在这里插入图片描述

中序迭代

方法一 通用方法

这里在遇到空结点的时候才访问栈顶元素,建议多模拟几遍

    vector<int> inorderTraversal(TreeNode* root) {
        vector<int>vt;
        stack<TreeNode*>st;
        while(root||!st.empty()){
            if(root){
            	//先入栈,后面访问
                st.push(root);
                //访问左子树
                root=root->left;
            }
            else{	//此时root走到了空结点
            	//访问栈顶元素,也就是当前左子树的根节点
                root=st.top();
                st.pop();
                vt.push_back(root->val);
                //访问右子树,执行同样的操作
                root=root->right;
            }
        }
        return vt;
    }

后序迭代

方法一 通用方法

后序迭代的最关键的地方就是根节点的访问时机,由于后序遍历是按照 左-右-中 的顺序,所以迭代过程中我们应该先处理左子树,返回到根节点,再处理右子树,再访问根节点。迭代过程会遇到两次根节点,而第二次才能访问根节点。这里处理方法有很多,比如借助标记结点,借用辅助栈,更改树的结构等等。不过我觉得最简洁的方法还是借助标记结点。
先写伪代码

    vector<int> inorderTraversal(TreeNode* root) {
    	while(root存在 或者 栈非空){
    		if(root){
    			入栈当前结点
    			root往左走
    		}
    		else{
    			root=栈顶
    			if(root的右孩子不存在 或者 标记节点就是当前结点的右孩子){
 					出栈
 					访问root
 					标记节点置为当前结点
 					root=NULL,因为当前根节点的左右子树和当前结点已经访问完了,应该退回到上一层,继续取栈顶	
    			}
    			else{
					root往右走
				}
    		}
    	}
    }

下面是代码

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

运行结果
在这里插入图片描述

方法二

这里方法二其实也很像递归,先入左孩子,再入右孩子,最后翻转vt就是正确的遍历结果了

    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> vt;
        stack<TreeNode*> st;
        st.push(root);
        TreeNode* t=NULL;
        while(!st.empty()){
            t=st.top();
            st.pop();
            vt.push_back(t->val);
            if(t->left) st.push(t->left);
            if(t->right) st.push(t->right);
        }
        reverse(vt.begin(),vt.end());
        return vt;
    }

在这里插入图片描述

颜色标记法

力扣上一个精选评论写的方法
其核心思想如下:

  • 使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色。
  • 如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点依次入栈。
  • 如果遇到的节点为灰色,则将节点的值输出。

下面是中序的代码,至于先序和后序只需要调整入栈顺序即可

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        WHITE, GRAY = 0, 1
        res = []
        stack = [(WHITE, root)]
        while stack:
            color, node = stack.pop()
            if node is None: continue
            if color == WHITE:
                stack.append((WHITE, node.right))
                stack.append((GRAY, node))
                stack.append((WHITE, node.left))
            else:
                res.append(node.val)
        return res
作者:hzhu212
链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/yan-se-biao-ji-fa-yi-chong-tong-yong-qie-jian-ming/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

下面是评论区里的c++代码

class Solution {
public:
	vector<int> inorderTraversal(TreeNode* root) {
        vector<int>ans;
		int white = 0;
		int gray = 1;
		stack<pair<int, TreeNode*>>s;
		s.push(make_pair(white,root));
		while (!s.empty())
		{
			int color = s.top().first;
			TreeNode* t = s.top().second;
			s.pop();
			if (t == NULL) continue;
			if (color == white)
			{
				if(t->right)s.push(make_pair(white, t->right));
				s.push(make_pair(gray, t));
				if(t->left)s.push(make_pair(white, t->left));
			}
			else ans.push_back(t->val);
		}
		return ans;
	}
};

模拟了一下,确实是很简洁的方法,只不过结点会入栈两次,消耗大了点

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值