C++实现LeetCode(94.二叉树的中序遍历)

 更新时间:2021年07月21日 14:32:33   作者:Grandyang  
这篇文章主要介绍了C++实现LeetCode(94.二叉树的中序遍历),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

[LeetCode] 94. Binary Tree Inorder Traversal 二叉树的中序遍历

Given a binary tree, return the inorder traversal of its nodes' values.

Example:

Input: [1,null,2,3]
1
\
2
/
3

Output: [1,3,2]

Follow up: Recursive solution is trivial, could you do it iteratively?

二叉树的中序遍历顺序为左-根-右,可以有递归和非递归来解,其中非递归解法又分为两种,一种是使用栈来接,另一种不需要使用栈。我们先来看递归方法,十分直接,对左子结点调用递归函数,根节点访问值,右子节点再调用递归函数,代码如下:

解法一:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> res;
        inorder(root, res);
        return res;
    }
    void inorder(TreeNode *root, vector<int> &res) {
        if (!root) return;
        if (root->left) inorder(root->left, res);
        res.push_back(root->val);
        if (root->right) inorder(root->right, res);
    }
};

下面再来看非递归使用栈的解法,也是符合本题要求使用的解法之一,需要用栈来做,思路是从根节点开始,先将根节点压入栈,然后再将其所有左子结点压入栈,然后取出栈顶节点,保存节点值,再将当前指针移到其右子节点上,若存在右子节点,则在下次循环时又可将其所有左子结点压入栈中。这样就保证了访问顺序为左-根-右,代码如下:

解法二: 

// Non-recursion
class Solution {
public:
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> res;
        stack<TreeNode*> s;
        TreeNode *p = root;
        while (p || !s.empty()) {
            while (p) {
                s.push(p);
                p = p->left;
            }
            p = s.top(); s.pop();
            res.push_back(p->val);
            p = p->right;
        }
        return res;
    }
};

下面这种解法跟 Binary Tree Preorder Traversal 中的解法二几乎一样,就是把结点值加入结果 res 的步骤从 if 中移动到了 else 中,因为中序遍历的顺序是左-根-右,参见代码如下:

解法三:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> s;
        TreeNode *p = root;
        while (!s.empty() || p) {
            if (p) {
                s.push(p);
                p = p->left;
            } else {
                p = s.top(); s.pop();
                res.push_back(p->val);
                p = p->right;
            }
        }
        return res;
    }
};

下面来看另一种很巧妙的解法,这种方法不需要使用栈,所以空间复杂度为常量,这种非递归不用栈的遍历方法有个专门的名字,叫 Morris Traversal,在介绍这种方法之前,先来引入一种新型树,叫 Threaded binary tree,这个还不太好翻译,第一眼看上去以为是叫线程二叉树,但是感觉好像又跟线程没啥关系,后来看到网上有人翻译为螺纹二叉树,但博主认为这翻译也不太敢直视,很容易让人联想到为计划生育做出突出贡献的某世界著名品牌,后经热心网友提醒,应该叫做线索二叉树。先来看看维基百科上关于它的英文定义:

A binary tree is threaded by making all right child pointers that would normally be null point to the inorder successor of the node (if it exists), and all left child pointers that would normally be null point to the inorder predecessor of the node.

就是说线索二叉树实际上是把所有原本为空的右子节点指向了中序遍历顺序之后的那个节点,把所有原本为空的左子节点都指向了中序遍历之前的那个节点。那么这道题跟这个线索二叉树又有啥关系呢?由于既不能用递归,又不能用栈,那如何保证访问顺序是中序遍历的左-根-右呢。原来需要构建一个线索二叉树,需要将所有为空的右子节点指向中序遍历的下一个节点,这样中序遍历完左子结点后,就能顺利的回到其根节点继续遍历了。具体算法如下:

1. 初始化指针 cur 指向 root

2. 当 cur 不为空时

  - 如果 cur 没有左子结点

      a) 打印出 cur 的值

    b) 将 cur 指针指向其右子节点

  - 反之

     将 pre 指针指向 cur 的左子树中的最右子节点 

     * 若 pre 不存在右子节点

          a) 将其右子节点指回 cur

        b) cur 指向其左子节点

     * 反之

      a) 将 pre 的右子节点置空

      b) 打印 cur 的值

      c) 将 cur 指针指向其右子节点

解法四:

class Solution {
public:
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> res;
        if (!root) return res;
        TreeNode *cur, *pre;
        cur = root;
        while (cur) {
            if (!cur->left) {
                res.push_back(cur->val);
                cur = cur->right;
            } else {
                pre = cur->left;
                while (pre->right && pre->right != cur) pre = pre->right;
                if (!pre->right) {
                    pre->right = cur;
                    cur = cur->left;
                } else {
                    pre->right = NULL;
                    res.push_back(cur->val);
                    cur = cur->right;
                }
            }
        }
        return res;
    }
};

其实 Morris 遍历不仅仅对中序遍历有用,对先序和后序同样有用。所以对二叉树的三种常见遍历顺序(先序,中序,后序)就有三种解法(递归,非递归,Morris 遍历),总共有九段代码呀,熟练掌握这九种写法才算初步掌握了树的遍历挖

到此这篇关于C++实现LeetCode(94.二叉树的中序遍历)的文章就介绍到这了,更多相关C++实现二叉树的中序遍历内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用Qt实现监听网页是否响应并导出Excel表

    使用Qt实现监听网页是否响应并导出Excel表

    Qt导出数据到excel,方法有很多,下面这篇文章主要给大家介绍了关于使用Qt实现监听网页是否响应并导出Excel表的相关资料,文中通过代码示例介绍的非常详细,需要的朋友可以参考下
    2023-11-11
  • 简述C语言中system()函数与vfork()函数的使用方法

    简述C语言中system()函数与vfork()函数的使用方法

    这篇文章主要介绍了简述C语言中system()函数与vfork()函数的使用方法,是C语言入门学习中的基础知识,需要的朋友可以参考下
    2015-08-08
  • C++一个函数如何调用其他.cpp文件中的函数

    C++一个函数如何调用其他.cpp文件中的函数

    这篇文章主要介绍了C++一个函数如何调用其他.cpp文件中的函数问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • C++字符串反转的几种方法

    C++字符串反转的几种方法

    通过不同的方法,实现对所输入字符串的反转,具有一定的参考价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-06-06
  • C++11 call_once 和 once_flag的使用与区别

    C++11 call_once 和 once_flag的使用与区别

    本文主要介绍了C++11 call_once 和 once_flag的使用与区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • 基于C语言实现计算生辰八字五行的示例详解

    基于C语言实现计算生辰八字五行的示例详解

    生辰八字,简称八字,是指一个人出生时的干支历日期;年月日时共四柱干支,每柱两字,合共八个字。这篇文章主要介绍了C语言实现计算生辰八字五行的示例代码,需要的可以参考一下
    2023-03-03
  • VC++实现选择排序算法简单示例

    VC++实现选择排序算法简单示例

    这篇文章主要介绍了VC++实现选择排序算法简单示例,代码简洁易懂,有助于读者对数据结构与算法的学习,需要的朋友可以参考下
    2014-08-08
  • C语言实现电子邮件地址验证程序

    C语言实现电子邮件地址验证程序

    这篇文章主要介绍了C语言实现电子邮件地址验证程序,利用的是POSIX正则表达式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2015-11-11
  • 基于Protobuf C++ serialize到char*的实现方法分析

    基于Protobuf C++ serialize到char*的实现方法分析

    本篇文章是对Protobuf C++ serialize到char*的实现方法进行了详细的分析介绍。需要的朋友参考下
    2013-05-05
  • OpenCV实现鼠标框选并显示框选区域

    OpenCV实现鼠标框选并显示框选区域

    这篇文章主要为大家详细介绍了OpenCV实现鼠标框选并显示框选区域,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08

最新评论