11 力扣热题刷题记录之第94题二叉树的中序遍历

系列文章目录

力扣刷题记录

文章目录

  • 系列文章目录
  • 前言
  • 一、背景
  • 二、我的思路
    • 1、递归
    • 2、迭代
  • 三、官方的思路
    • 1、迭代
    • 2、Morris 中序遍历
  • 总结


前言

每天进步一点点

一、背景

给定一个二叉树的根节点 root ,返回它的 中序 遍历。

二、我的思路

1、递归

从根节点,一直往左走,在这个过程中遇到的值用栈保留下来,知道走到最左边,然后开始输出(存到另一个数组里面)。再访问栈里的元素,再访问这个元素有无右孩子,循环如此。最后是直到没有右孩子了,就结束。
递归肯定是最简单的,但是这里要求返回一个数组,就是以前从来是直接打印的,这一次需要存储起来。

我就想着吧,声明一个全局的数组,然后每次计算的时候,用这个数组去接收,但是还是出问题了。(完事代码不见了)

之后我看了别人的代码,发现是用了两个函数,把数组作为函数的参数进行传递(为啥不能直接接收?下次我复原一下我的代码想法)

好了,通过的代码是:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
     
public: 
    void inorderTraversal2(TreeNode* root,vector<int>& in_order)
    {
     
        if(root==NULL) return;       

        inorderTraversal2(root->left,in_order);
        in_order.push_back(root->val);
        inorderTraversal2(root->right,in_order);
    }
    vector<int> inorderTraversal(TreeNode* root) {
          
        vector<int> in_order; 
        inorderTraversal2(root,in_order);

        return in_order;     
    }
};

2、迭代

从根节点,一直往左走,在这个过程中遇到的值用栈保留下来,直到走到最左边,然后开始输出(存到另一个数组里面)。再访问栈里的元素,出栈,后访问这个元素有无右孩子,循环如此。最后是栈空了,就结束。

代码如下:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
     
public: 
    vector<int> inorderTraversal(TreeNode* root) {
          
        vector<int> in_order;
        stack<TreeNode*> store; 
        if(root==NULL)
        {
     
            return {
     };
        }
        else store.push(root);
        while(!store.empty())
        {
     
            TreeNode* p=store.top()->left;
            store.top()->left=NULL;//根节点的左指针也置空
            while(p!=NULL)
            {
     
                store.push(p);

                //把进栈的结点左指针置空
                TreeNode* r=p;
                p=p->left;
                r->left=NULL;   
            }

            //左节点到头了,然后出栈进入数组
            TreeNode* q=store.top();
            in_order.push_back(q->val);
            store.pop();
          
           //访问右孩子,不空进栈
            if(q->right!=NULL)
                store.push(q->right);
        }
        return in_order;     
    }
};

三、官方的思路

递归的就不讲了,这里主要是将迭代和线索(Morris 中序遍历)。

1、迭代

大致思路大差不差,就是在第二个循环前面那里有些困扰。就是当访问过左子树时,再次回到根节点,由于我第二个循环进入是判断栈顶元素,所以又访问了一次左边元素,这明显是重复访问的。

于是我就把访问过左节点的元素左指针全部置为空,这样就不会进入第二个循环了。但是官方的思路就很巧妙,它进入第二个循环的判断是root是否为空,而不是取栈顶元素(看他左右两边访问过没有)。

看代码:

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

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/er-cha-shu-de-zhong-xu-bian-li-by-leetcode-solutio/
来源:力扣(LeetCode)

官方判断root,意味着上一次给root赋值为左子树最右边的那个空结点时,这个时候怎么回到根节点呢?因为第二个循环是判断root空不空,而此时root本身为空,所以当然进不了第二个循环,然后再次取栈顶元素(此时即为根节点),顺势访问了右子树。妙啊!!!

2、Morris 中序遍历

感觉就是线索二叉树。迭代的办法是用栈去保留中间结点,这里它直接改变孩子的右指针的指向,使之指向中序遍历的下一个元素,这样就不用栈来保存了,依次访问即可。

代码如下:

class Solution {
     
public:
    vector<int> inorderTraversal(TreeNode* root) {
     
        vector<int> res;
        TreeNode *predecessor = nullptr;

        while (root != nullptr) {
     
            if (root->left != nullptr) {
     
                // predecessor 节点就是当前 root 节点向左走一步,然后一直向右走至无法走为止
                predecessor = root->left;
                while (predecessor->right != nullptr && predecessor->right != root) {
     
                    predecessor = predecessor->right;
                }
                
                // 让 predecessor 的右指针指向 root,继续遍历左子树
                if (predecessor->right == nullptr) {
     
                    predecessor->right = root;
                    root = root->left;
                }
                // 说明左子树已经访问完了,我们需要断开链接
                else {
     
                    res.push_back(root->val);
                    predecessor->right = nullptr;
                    root = root->right;
                }
            }
            // 如果没有左孩子,则直接访问右孩子
            else {
     
                res.push_back(root->val);
                root = root->right;
            }
        }
        return res;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/er-cha-shu-de-zhong-xu-bian-li-by-leetcode-solutio/
来源:力扣(LeetCode)

总结

思路是比较清晰地,就是实现起来,要考虑到很多的细节,就容易卡住。我总喜欢把第一个元素单独拿出来放进去,但是好的代码总是一起判断的,本质区别在于,我不知道如何设置循环的终止条件,就是不知道要判断什么信息。这给我一个思路就是,写代码不要着急写,知道用循环或是递归的时候,先把出口搞明白,是比较重要的。

喜欢就点个赞叭!!!

你可能感兴趣的:(力扣刷题记录,leetcode,算法,栈)