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++实现二叉树的中序遍历内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中auto_ptr智能指针的用法详解

    C++中auto_ptr智能指针的用法详解

    这篇文章主要介绍了C++中auto_ptr智能指针的用法详解的相关资料,需要的朋友可以参考下
    2016-07-07
  • C++深入探究类与对象之友元与运算符重载

    C++深入探究类与对象之友元与运算符重载

    友元就是让一个函数或者类,访问另一个类中的私有成员;打个比方,这相当于是说:朋友是值得信任的,所以可以对他们公开一些自己的隐私,运算符重载的实质就是函数重载或函数多态,运算符重载是一种形式的C++多态,目的在于让人能够用同名的函数来完成不同的基本操作
    2022-04-04
  • C++迷宫的实现代码

    C++迷宫的实现代码

    这篇文章主要为大家详细介绍了C++实现迷宫游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • C语言实现三子棋简单小游戏

    C语言实现三子棋简单小游戏

    这篇文章主要为大家详细介绍了C语言实现三子棋简单小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 用C++来解决3*3拼图的问题

    用C++来解决3*3拼图的问题

    这篇文章主要介绍了用C++来解决3*3拼图的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • 进程间通信之深入消息队列的详解

    进程间通信之深入消息队列的详解

    本篇文章是对消息队列的应用进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • OpenCV Matlab生成视频倒放功能

    OpenCV Matlab生成视频倒放功能

    这篇文章主要介绍了OpenCV Matlab生成视频倒放功能,大家都知道不少带声音视频的后缀名往往都是.mp4,那么如何获取里面的音频呢?本文通过实例代码给大家介绍的非常详细,需要的朋友参考下吧
    2022-01-01
  • STL 的string类怎么啦

    STL 的string类怎么啦

    在我们研究string类犯了什么毛病之前,还让我先说一下如何了解一个C++的类。我们要了解一个C++的类,一般来说,要从三个方面入手
    2013-11-11
  • C语言编程入门必背的示例代码整理大全

    C语言编程入门必背的示例代码整理大全

    这篇文章主要为大家整理并介绍了C语言编程必背的示例代码大全,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-11-11
  • C++构造函数详解

    C++构造函数详解

    这篇文章主要介绍了C++构造函数详解,上一篇文章我们介绍了定义了类,在使用之前,往往还需要对类进行初始化。这篇介绍的就是对类进行初始化的方法,需要的朋友可以参考一下
    2022-01-01

最新评论