【每日算法Day 76】经典面试题:中序遍历的下一个元素,5大解法汇总!

简介: 【每日算法Day 76】经典面试题:中序遍历的下一个元素,5大解法汇总!

题目链接

LeetCode 面试题 04.06. 后继者[1]

题目描述

设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。

如果指定节点没有对应的“下一个”节点,则返回 null。

示例1

输入:
root = [2,1,3], p = 1
  2
 / \
1   3
输出:
2

示例2

输入:
root = [5,3,6,2,4,null,null,1], p = 6
      5
     / \
    3   6
   / \
  2   4
 /   
1
输出:
null

题解

BST+递归

首先本题中的二叉树还是个二叉搜索树,也就是中序遍历是单调递增的,所以我们可以利用这个性质来简化查找过程。

  • 如果结点 p 的值大于等于 root 的值,说明 p 的后继结点在 root 右子树中,那么就递归到右子树中查找。
  • 如果结点 p 的值小于 root 的值,说明 proot 左子树中,而它的后继结点有两种可能,要么也在左子树中,要么就是 root
  • 如果左子树中找到了后继结点,那就直接返回答案。
  • 如果左子树中没有找到后继结点,那就说明 p 的右儿子为空,那么 root 就是它的后继结点。

BST+非递归

  • 如果 p 有右儿子,那么它的后继结点就是右子树的最左边的儿子。
  • 如果 p 没有右儿子,那么它的后继结点就是,沿着 p 往上到 root 的路径中,第一个左儿子在路径上的结点。因为这个结点的左子树中 p 是最右边的结点,是最大的,所以它就是 p 的后继结点。因为是二叉搜索树,我们就可以从根结点开始往 p 走,根据结点值的大小决定走的方向。

一般树+递归

那如果是一般的二叉树,中序遍历就不满足单调递增了,这时候我们就只能找出中序遍历的结点顺序,然后才能得到 p 的后继结点。

所以我们直接采用递归来做中序遍历就行了,中序遍历结果保存下来,最后取 p 的下一个结点。

一般树+非递归

当然还可以采用栈来做中序遍历,这样就是非递归了。同样结果保存下来,最后取 p 的下一个结点。

一般树+Morris遍历

如果看过我前两天的一道关于二叉搜索树的题解:

【每日算法Day 73】学妹大半夜私聊我有空吗,然后竟然做出这种事!


韦阳的博客:【每日算法Day 73】学妹大半夜私聊我有空吗,然后竟然做出这种事![2]

知乎专栏:【每日算法Day 73】学妹大半夜私聊我有空吗,然后竟然做出这种事![3]

那么你一定会知道这个 Morris 遍历算法,用常数空间来解决结点无法访问父结点的问题。这里就不细讲了,请直接看之前的题解。方法是一样的,用 Morris 遍历得到中序遍历,然后遍历一遍找到 p ,输出它的下一个结点就行了。

代码

BST+递归(c++)

class Solution {
public:
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        if (root == NULL || p == NULL) return NULL;
        if (p->val >= root->val) {
            return inorderSuccessor(root->right, p);
        } else {
            TreeNode* left = inorderSuccessor(root->left, p);
            return left ? left : root;
        }
    }
};

BST+非递归(c++)

class Solution {
public:
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        if (p->right) {
            p = p->right;
            while (p->left) p = p->left;
            return p;
        }
        TreeNode* res = NULL;
        while (root != p) {
            if (root->val < p->val) {
                root = root->right;
            } else {
                res = root;
                root = root->left;
            }
        }
        return res;
    }
};

一般树+递归(c++)

class Solution {
public:
    void inorder(TreeNode* root, vector<TreeNode*>& res) {
        if (root->left) inorder(root->left, res);
        res.push_back(root);
        if (root->right) inorder(root->right, res);
    }
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        vector<TreeNode*> res;
        inorder(root, res);
        res.push_back(NULL);
        for (int i = 0; i < res.size(); ++i) {
            if (res[i] == p) {
                return res[i+1];
            }
        }
        return NULL;
    }
};

一般树+非递归(c++)

class Solution {
public:
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        vector<TreeNode*> res;
        stack<TreeNode*> st;
        while (!st.empty() || root) {
            while (root) {
                st.push(root);
                root = root->left;
            }
            root = st.top();
            st.pop();
            res.push_back(root);
            root = root->right;
        }
        res.push_back(NULL);
        for (int i = 0; i < res.size(); ++i) {
            if (res[i] == p) {
                return res[i+1];
            }
        }
        return NULL;
    }
};

一般树+Morris遍历(c++)

class Solution {
public:
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        vector<TreeNode*> res;
        TreeNode *rightmost = NULL;
        while (root) {
            if (root->left) {
                rightmost = root->left;
                while (rightmost->right && rightmost->right != root) {
                    rightmost = rightmost->right;
                }
                if (rightmost->right != root) {
                    rightmost->right = root;
                    root = root->left;
                } else {
                    res.push_back(root);
                    rightmost->right = NULL;
                    root = root->right;
                }
            } else {
                res.push_back(root);
                root = root->right;
            }
        }
        res.push_back(NULL);
        for (int i = 0; i < res.size(); ++i) {
            if (res[i] == p) {
                return res[i+1];
            }
        }
        return NULL;
    }
};
相关文章
|
4月前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
85 3
|
26天前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
66 16
|
1月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
51 2
|
3月前
|
Java 编译器 程序员
Java面试高频题:用最优解法算出2乘以8!
本文探讨了面试中一个看似简单的数学问题——如何高效计算2×8。从直接使用乘法、位运算优化、编译器优化、加法实现到大整数场景下的处理,全面解析了不同方法的原理和适用场景,帮助读者深入理解计算效率优化的重要性。
56 6
|
3月前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
3月前
|
存储 缓存 算法
如何提高二叉树遍历算法的效率?
选择合适的遍历算法,如按层次遍历树时使用广度优先搜索(BFS),中序遍历二叉搜索树以获得有序序列。优化数据结构,如使用线索二叉树减少空指针判断,自定义节点类增加辅助信息。利用递归与非递归的特点,避免栈溢出问题。多线程并行遍历提高速度,注意线程安全。缓存中间结果,避免重复计算。预先计算并存储信息,提高遍历效率。综合运用这些方法,提高二叉树遍历算法的效率。
97 5
|
4月前
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩分享分库分表的基因算法设计,涵盖分片键选择、水平拆分策略及基因法优化查询效率等内容,助力面试者应对大厂技术面试,提高架构设计能力。
美团面试:百亿级分片,如何设计基因算法?
|
3月前
|
算法
树的遍历算法有哪些?
不同的遍历算法适用于不同的应用场景。深度优先搜索常用于搜索、路径查找等问题;广度优先搜索则在图的最短路径、层次相关的问题中较为常用;而二叉搜索树的遍历在数据排序、查找等方面有重要应用。
61 2
|
3月前
|
机器学习/深度学习 JSON 算法
二叉树遍历算法的应用场景有哪些?
【10月更文挑战第29天】二叉树遍历算法作为一种基础而重要的算法,在许多领域都有着不可或缺的应用,它为解决各种复杂的问题提供了有效的手段和思路。随着计算机科学的不断发展,二叉树遍历算法也在不断地被优化和扩展,以适应新的应用场景和需求。
106 0
|
4月前
|
机器学习/深度学习 算法 Java
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)

热门文章

最新文章