4.2 深度优先遍历的 迭代法的统一实现

前面介绍了深度优先遍历的 , 前中后序 三种遍历方式的迭代实现, 但是却没有向 递归那样的 统一实现;

原因 在于, 使用迭代方式的遍历过程中, 有两点注意

  1. 访问节点( 将节点压入栈中);
  2. 处理节点(将节点从栈中取出, 然后将节点中的数据部分存入到结果集中);

在前序遍历中, 访问节点与 处理节点这两个步骤 可以同步进行, 而中序遍历则不行;

难道 二叉树前后中序遍历的迭代法实现,就不能风格统一么(即前序遍历 改变代码顺序就可以实现中序 和 后序)

迭代法的 统一实现思想

造成上述, 访问节点与 处理节点不能同步的原因时:

  1. 访问节点时, 是将节点压入栈中;
  2. 处理节点时, 是将节点的数值 存入到 结果集中;
    这两个步骤, 是将节点 存入到 不同的对象中;

那么 迭代法 统一实现的思想便是:

  1. 将访问节点 与 处理节点 都放入栈中;
  2. 但是处理节点 放入栈中之后,需要做标记;

leetcode: 144 前序遍历
94
145

1. 前序遍历的迭代;

1.1 统一的步骤

统一的方式:

根节点入栈:
循环开始:

取出栈顶节点的引用赋值为当前节点;
条件判断, 若当前节点不为空, 开始访问节点(节点入栈):

  1. 弹出栈顶节点,
  2. 条件判断,当前节点的右子树不空, 将当前节点右子树 入栈;
  3. 条件判断, 当前节点的左子树不空, 将当前节点的左子树入栈;
  4. 当前节点入栈,
  5. 标记节点入栈,(使用空节点作为标记节点)

否则,当前节点为空节点, 开始处理节点(节点中数值,存入结果集中)

  1. 弹出栈顶节点;(空节点,为标记节点, 代表移除空节点后, 此时的栈顶节点才是 需要处理的 中间节点)
  2. 当前节点 赋值为 栈顶节点的引用;
  3. 弹出栈顶节点;
  4. 将当前节点中的数据值 存入到结果集中;

返回结果集;

1.2 code

class Solution1{
public:
     vector<int>  preorder(TreeNode*  root){
         vector<int> result;
         stack<TreeNode*> st;

         if(root == nullptr) return  result ;
         st.push(root);

         while(!st.empty()){
             TreeNode* cur_node = st.top();

             if(cur_node != nullptr){// 如果当前节点,不为空,开始访问节点, 即节点入栈;
                 st.pop();
                 if(cur_node->right) st.push(cur_node->right); //  当前节点的右子树存在, 右子树入栈;
                 if(cur_node->left)  st.push(cur_node->left); //  左子树存在, 左子树与入栈;

                 st.push(cur_node);  // 当前节点入栈;
                 st.push(nullptr);  //   标记节点入栈;
             }else{//   当前节点为空, 表示这个节点为标记节点, 移除该标记节点后, 可以处理节点了, 将节点中的数值保存到结果集中;
                 st.pop();
                 cur_node = st.top();
                 result.push_back(cur_node->val);
                 st.pop();
             }

         }
         return result;
     }
};

2. 中序遍历的迭代;

2.1 步骤

统一的方式:

根节点入栈:
循环开始:

取出栈顶节点的引用赋值为当前节点;
条件判断, 若当前节点不为空, 开始访问节点(节点入栈):

  1. 弹出栈顶节点,
  2. 条件判断,当前节点的右子树不空, 将当前节点右子树 入栈;
  3. 当前节点入栈,
  4. 标记节点入栈, (使用空节点作为标记节点)
  5. 条件判断, 当前节点的左子树不空, 将当前节点的左子树入栈;

否则,当前节点为空节点, 开始处理节点(节点中数值,存入结果集中)

  1. 弹出栈顶节点;( 空节点,为标记节点, 代表移除空节点后, 此时的栈顶节点才是 需要处理的 中间节点)
  2. 当前节点 赋值为 栈顶节点的引用;
  3. 弹出栈顶节点;
  4. 将当前节点中的数据值 存入到结果集中;

返回结果集;

2.2 code

#include "vector"
#include "stack"

using namespace std;

struct TreeNode{
    int val;
    TreeNode* left;
    TreeNode* right;

    TreeNode(int x): val(x), left(nullptr), right(nullptr){}
};



class  Solution2 {// inorder;
public:
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> result;
        stack<TreeNode *> st;

        if (root != nullptr) st.push(root);
        while (!st.empty()) {
            TreeNode *cur_node = st.top();

            if (cur_node != nullptr) {
                st.pop();  // 弹出节点,
                if (cur_node->right) st.push(cur_node->right);// 当前节点的右子树入栈,开始访问当前节点的右子树;;

                st.push(cur_node); //  当前节点入栈;
                st.push(nullptr); // 标记节点入栈;

                if (cur_node->left) st.push(cur_node->left);
            } else {  // 否则, 当前节点为空节点, 开始处理节点,
                st.pop();//  弹出空节点,这是标记节点, 代表栈顶节点移除空节点后, 此时的栈顶节点是需要处理,存入结果集中的;
                cur_node = st.top();
                st.pop();
                result.push_back(cur_node->val);
                }
        }
        return result;
    }
};

3. 中序遍历的迭代;

3.1 步骤

统一的方式:

根节点入栈:
循环开始:

取出栈顶节点的引用赋值为当前节点;
条件判断, 若当前节点不为空, 开始访问节点(节点入栈):

  1. 弹出栈顶节点,
  2. 当前节点入栈,
  3. 标记节点入栈, (使用空节点作为标记节点)
  4. 条件判断,当前节点的右子树不空, 将当前节点右子树 入栈;
  5. 条件判断, 当前节点的左子树不空, 将当前节点的左子树入栈;

否则,当前节点为空节点, 开始处理节点(节点中数值,存入结果集中)

  1. 弹出栈顶节点;( 空节点,为标记节点, 代表移除空节点后, 此时的栈顶节点才是 需要处理的 中间节点)
  2. 当前节点 赋值为 栈顶节点的引用;
  3. 弹出栈顶节点;
  4. 将当前节点中的数据值 存入到结果集中;

返回结果集;

3.2 code

#include "vector"
#include "stack"

using namespace std;

struct TreeNode{
    int val;
    TreeNode* left;
    TreeNode* right;

    TreeNode(int x): val(x), left(nullptr), right(nullptr){}
};



class Solution3{
public:
    vector<int>  preorder(TreeNode*  root){
        vector<int> result;
        stack<TreeNode*> st;

        if(root == nullptr) return  result ;
        st.push(root);

        while(!st.empty()){
            TreeNode* cur_node = st.top();

            if(cur_node != nullptr){// 如果当前节点,不为空,开始访问节点, 即节点入栈;
                st.pop();

                st.push(cur_node);  // 当前节点入栈;
                st.push(nullptr);  //   标记节点入栈;
                if(cur_node->right) st.push(cur_node->right); //  当前节点的右子树存在, 右子树入栈;
                if(cur_node->left)  st.push(cur_node->left); //  左子树存在, 左子树与入栈;

            }else{//   当前节点为空, 表示这个节点为标记节点, 移除该标记节点后, 可以处理节点了, 将节点中的数值保存到结果集中;
                st.pop();
                cur_node = st.top();
                result.push_back(cur_node->val);
                st.pop();
            }

        }
        return result;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值