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

简介: 设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。如果指定节点没有对应的“下一个”节点,则返回 null。

题目链接


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++)

classSolution {
public: 
TreeNode*inorderSuccessor(TreeNode*root, TreeNode*p) { 
if (root==NULL||p==NULL) returnNULL;   
if (p->val>=root->val) {   
returninorderSuccessor(root->right, p);   
        } else {         
TreeNode*left=inorderSuccessor(root->left, p); 
returnleft?left : root;  
        } 
    }
};

BST+非递归(c++)

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

一般树+递归(c++)

classSolution {
public:  
voidinorder(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 (inti=0; i<res.size(); ++i) {   
if (res[i] ==p) {     
returnres[i+1];   
            }     
        }     
returnNULL;   
    }
};

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

classSolution {
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 (inti=0; i<res.size(); ++i) {  
if (res[i] ==p) {    
returnres[i+1];   
            }    
        }      
returnNULL;  
    }
};

参考资料


[1]

LeetCode 面试题 04.06. 后继者: https://leetcode-cn.com/problems/successor-lcci/

[2]

【每日算法Day 73】学妹大半夜私聊我有空吗,然后竟然做出这种事!: https://godweiyang.com/2020/03/18/leetcode-99/

[3]

【每日算法Day 73】学妹大半夜私聊我有空吗,然后竟然做出这种事!: https://zhuanlan.zhihu.com/p/114143194

image.png

作者简介:godweiyang知乎同名华东师范大学计算机系硕士在读,方向自然语言处理与深度学习喜欢与人分享技术与知识,期待与你的进一步交流~

相关文章
|
XML Java 数据格式
Spring-实例化bean的四种方式
Spring-实例化bean的四种方式
602 0
|
Cloud Native Devops 持续交付
【云原生|云原生基础】什么是云原生?一文给你讲清楚!
【云原生|云原生基础】什么是云原生?一文给你讲清楚!
7317 1
|
7月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
1996 7
|
12月前
|
Prometheus 监控 Cloud Native
高频面题: 你们线上 QPS 多少?你 怎么知道的?
本文由45岁资深架构师尼恩撰写,针对高级开发和架构师面试中的高频问题提供详细解答。文章涵盖了QPS、TPS、RT等性能指标的定义及计算方法,详解了如何配置Prometheus与Grafana监控系统QPS,并提供了应对高并发场景(如双十一抢购)的系统部署策略。此外,还分享了多个大厂面试真题及解决方案,帮助读者在面试中充分展示技术实力,提升求职竞争力。建议收藏并深入学习,为面试做好充分准备。更多内容可参考《尼恩Java面试宝典》及相关技术圣经系列PDF。
|
前端开发 JavaScript
如何在 JavaScript 中访问和修改 CSS 变量?
【10月更文挑战第28天】通过以上方法,可以在JavaScript中灵活地访问和修改CSS变量,从而实现根据用户交互、页面状态等动态地改变页面样式,为网页添加更多的交互性和动态效果。在实际应用中,可以根据具体的需求和场景选择合适的方法来操作CSS变量。
542 12
|
存储 消息中间件 API
“论微服务架构及其应用”写作框架,软考高级,系统架构设计师
论微服务架构及其应用近年来,随着互联网行业的迅猛发展,公司或组织业务的不断扩张,需求的快速变化以及用户量的不断增加,传统的单块(Monolithic)软件架构面临着越来越多的挑战,已逐渐无法适应互联网时代对软件的要求。在这一背景下,微服务架构模式(MicroserviceArchitecturePattern)逐渐流行,它强调将单一业务功能开发成微服务的形式,每个微服务运行在一个进程中;采用HTTP等通用协议和轻量级API实现微服务之间的协作与通信。这些微服务可以使用不同的开发语言以及不同数据存储技术,能够通过自动化部署工具独立发布,并保持最低限制的集中式管理。
990 4
|
编译器 C++ 容器
【C++进阶】深入STL之vector:深入研究迭代器失效及拷贝问题
【C++进阶】深入STL之vector:深入研究迭代器失效及拷贝问题
|
人工智能 算法
图解:求逆序对数量(归并排序的应用)
图解:求逆序对数量(归并排序的应用)
【SVN】如何取消文件和SVN服务器的关联
【SVN】如何取消文件和SVN服务器的关联
232 0
|
分布式计算 数据可视化 Hadoop
大数据实战——基于Hadoop的Mapreduce编程实践案例的设计与实现
大数据实战——基于Hadoop的Mapreduce编程实践案例的设计与实现
3908 0

热门文章

最新文章