算法通关村第七关黄金挑战——迭代实现二叉树的前中后序遍历

大家好,我是怒码少年。

今天讲用迭代法实现二叉树的深度查询(前中后序遍历),想想大一下的时候我还完全看不懂呢哈哈。

前序遍历

也就是中左右,访问本结点之后再访问本结点的左子树,然后再访问右子树。所以在代码中我们也是这样处理。

使用一个指针node负责遍历结点的话,如果此时它已经遍历完所有的左子树了,它要怎么开始遍历右子树呢?

答:如果能找到左子树的父结点,我们就能找到右子树了不是吗😎

所以我们在遍历左子树的时候,需要用一个数据结构保存上一个遍历的结点(也就是当前结点的父结点),要访问右子树的时候,就从中取出父结点,然后通过node=node->right找到右子树。

因为存入顺序与取出顺序相反,所以我们使用栈来实现。

这里我们用vector保存已经遍历完成的结点。

vector<int> preOrderTraversal(TreeNode* root) {
    vector<int> res;  // 存储结果的 vector

    // 如果根结点为空,直接返回空的 vector
    if (root == nullptr) {
        return res;
    }

    stack<TreeNode*> stk;  // 用于存储暂时还未遍历右子树的结点
    TreeNode* node = root;  // 从根结点开始遍历

    // 循环条件:栈不为空或者当前结点不为空
    while (!stk.empty() || node != nullptr) {
        // 先处理左子树
        while (node != nullptr) {
            res.push_back(node->val);  // 将当前结点的值加入结果 vector
            stk.push(node);  // 将当前结点保存到栈中
            node = node->left;  // 前往下一个左子结点
        }

        // 左子树遍历完毕,开始遍历右子树
        node = stk.top();  // 取出栈顶元素,即最近访问过的结点
        stk.pop();  // 弹出栈顶元素
        node = node->right;  // 前往右子结点
    }

    return res;  // 返回结果 vector
}

中序遍历

左中右。思路大致和前序遍历的是一样的,建议看到这里想停下来自己想想。

和前序遍历不同的是,中序遍历是先遍历左子树再遍历该左子树的父结点,然后再遍历右子树。所以我们先用一个遍历指针node找到最左结点,这个过程我们需要用一个数据结构保存该结点的父结点(便于访问父节点和右结点)。

vector<int>  OrderTraversal(TreeNode* root) {
    vector<int> res;
    if (root == nullptr) {
        return res;
    }
    stack<TreeNode*> stk;
    TreeNode* node = root;

    while (node != nullptr || !stk.empty()) {
        while (node != nullptr) {
            //保存
            stk.push(node);
            //找到最左的那个结点
            node = node->left;
        }
        node = stk.top();
        stk.pop();
        res.push_back(node->val);
        node = node->right;
    }
    return res;
}

后序遍历

左右中。后序遍历与前面两个有一个很大的不同。例如我们看下面这个二叉树:


它的后序遍历是[8、13、9、15、7、20、3]。也就是先访问左子树再访问右子树,然后再访问父结点。

如果用前序遍历的思路,访问左子树之后,就要通过栈中保存的父结点访问右子树,然后再访问父结点,这时你就会发现父结点需要被访问两次。这显然是一个栈做不到的。

后序遍历的常见方法有:反转法,访问标记法(栈中结点被访问了两次才能出栈),Morris算法。这里详细讲讲反转法。

反转法

将上图所示二叉树的后序遍历顺序反转一下,得到[3、20、7、15、9、13、8]。观察发现,这个遍历顺序是中右左,本质上是和前序遍历是一样的,只不过是先访问右子树再访问左子树,再把顺序反转一下不就得到后序遍历的顺序了吗😁。

这个方法可以巧妙避开直接后序遍历的难处,代码实现如下:

vector<int> postOrderTraversal(TreeNode* root) {
    vector<int> res;
    if (root == nullptr) {
        return res;
    }
    stack<TreeNode*> stk;
    TreeNode* node = root;
    while (!stk.empty() || node != nullptr) {
        //先处理右子树
        while (node != nullptr) {
            res.push_back(node->val);
            stk.push(node);
            node = node->right;
        }
        //再处理左子树
        node = stk.top();
        stk.pop();
        node = node->left;
    }
    //反转数组得到后序遍历顺序
    reverse(res.begin(), res.end());
    return res;
}

END

记得数据结构课上有一道实验报告就是实现这个算法,当时好像使用指针标记父结点,各种指针指来指去,我是真的看不下去,现在总算补上了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值