力扣面试题 04.06. 后继者
思路分析:题目要求寻找二叉搜索树中节点为p节点的下一个节点(中序遍历:先遍历左子树再中间节点再右子树)。
解法1:按照二叉树的中序遍历,把遍历的节点依次存入到队列中,再依次取出,当取出的节点等于p时,再取后一个节点就是要求的节点。
代码如下:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
TreeNode res = null;
//队列,用于存放中序遍历的节点
Queue<TreeNode> queue = new ArrayDeque<>();
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
//进行中序遍历
mInoderTraversal(root,p);
//依次取出节点
while (!queue.isEmpty()){
//找到p节点并且p节点不是最后一个节点
if(queue.poll().val == p.val && !queue.isEmpty()){
res = queue.poll();
}
}
return res;
}
//中序遍历
private void mInoderTraversal(TreeNode node, TreeNode p) {
if(node==null) return;
mInoderTraversal(node.left,p);
//存放该节点
queue.offer(node);
mInoderTraversal(node.right,p);
}
}
这种方法是一种比较通用的方法。但是时间复杂度比较高:
可以对这种方法进行优化,首先不需要遍历全部节点,在搜索到p节点的下一个节点后就可以停止递归了,不需要后续的搜索。
问题1:如何判断当前节点已经是p节点的后一个节点。可以在每次处理节点时判断是否到达p节点,如果到达了p节点,那么把p节点用一个变量temp存下来,那么下一次遍历(走到p节点的下一个节点)时,temp的值就从null变为了p,那么当前的node就是所要找的节点了。
问题2:这个时候如何终止后续的遍历操作。这个时候保存了目标节点后,可以将flag置为true,处理每个节点的时候都判断一下flag,如果flag的值改变了,那么就return,就不处理当前节点了,并且由于后续的节点在处理时都会判断flag,那么后面的节点都不会做判断等操作,就实现了提前终止。
代码如下:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
TreeNode res = null;
//队列,用于存放中序遍历的节点
boolean flag = false;
TreeNode temp = null;
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
//进行中序遍历
mInoderTraversal(root,p);
return res;
}
//中序遍历
private void mInoderTraversal(TreeNode node, TreeNode p) {
if(node==null) return;
mInoderTraversal(node.left,p);
//遍历每个节点前先判断是否处理该节点
//说白了就是一个提前终止后续的遍历
if(flag) return;
//由于每次处理顺序就是二叉树的中序遍历,当temp赋值后
//中序遍历的下一个节点就会执行
if(temp!=null){
res = node;
//提前终止
flag = true;
}
//存放该节点
if(node.val == p.val){
temp = node;
}
mInoderTraversal(node.right,p);
}
}
时间复杂度会减少很多:
解法2:由于题目是二叉搜索树,那么对二叉搜索树的中序遍历就是将二叉搜索树中的数据从小到大排列,那么p节点的下一个节点的值肯定是大于p节点的值的,并且是大于里面最小的,说白了就是大于p节点的值并且最接近p节点的值。那么就可以对二叉搜索树进行搜索,稍微修改一下之前的搜索代码就可以。之前的搜索代码如下:
while (root != null){
//如果和当前节点相等,已经搜索到
if (root.val == target){
ans = root;
break;
}else if(root.val < target){//大于当前节点的值,走右边
root= root.right;
}else{//小于当前节点的值,走左边
root= root.left;
}
}
那么这道题目是找大于target的节点,如果当前节点大于target,那么当前节点的值减少一点,就走左,并且更新res(res存放大于target的节点,每次左移或者右移就是接近target)。如果当前节点的值小于等于target,那么必须走右,因为要大于target,那么此时搜索代码如下:
while (root != null){
if (root.val > target){
ans = root;
root = root.left;
}else{
root = root.right;
}
}
完整代码我就复制官方代码了:(官方只是先搜索一种情况,就是节点p有直接右节点,那么直接右节点就是比p大的节点,在往左走,就是去寻找最接近p的节点,这种思想可以在二叉搜索树的删除节点操作里面看到)
class Solution {
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
TreeNode successor = null;
if (p.right != null) {
successor = p.right;
while (successor.left != null) {
successor = successor.left;
}
return successor;
}
TreeNode node = root;
while (node != null) {
if (node.val > p.val) {
successor = node;
node = node.left;
} else {
node = node.right;
}
}
return successor;
}
}
作者:LeetCode-Solution
链接:https://leetcode.cn/problems/successor-lcci/solution/hou-ji-zhe-by-leetcode-solution-6hgc/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。