二叉树中查找后继节点问题

二叉树,查找,后继,节点,问题 · 浏览次数 : 246

小编点评

**思路一:中序遍历递归解法** - 使用 List 收集中序遍历的节点。 - 遍历一遍 List,找到给定节点的下一个节点。 **思路二:Morris 遍历** - 使用 Stack 实现中序遍历。 - 标记遍历到的节点 p。 - 如果标志位设置过,则下一个遍历的位置就是后继节点。 **思路三:利用二叉搜索树的特性** - 如果目标节点的右孩子不为空,则目标节点右树最左节点就是目标节点的后继节点。 - 遍历过程中,如果当前节点的值大于目标节点的值,则先记录下当前节点(有可能是备选答案,但没有确定有没有更接近目标值的选择)。 - 遍历的节点往左边移动,如果当前节点的值小于目标节点的值,一定不是后继,遍历的节点往右边移动。 - 如果当前节点的值等于目标节点的值,说明一定找到了后继(因为这个过程中可以确定当前节点没有右孩子,所以,到这一步,肯定是通过后继过来的,或者后继为 null)。

正文

二叉树中查找后继节点问题

作者:Grey

原文地址:

博客园:二叉树中查找后继节点问题

CSDN:二叉树中查找后继节点问题

题目描述

给定一个二叉查找树,以及一个节点,求该节点在中序遍历的后继,如果没有则返回 null

题目链接见:LintCode 448 · Inorder Successor in BST

思路一,利用中序遍历递归解法,使用 List 收集中序遍历的节点,然后遍历一遍 List,找到给定节点的下一个节点即可,中序遍历的递归方法代码很简单,参考二叉树的先,中,后序遍历(递归,非递归)

完整代码如下

public class Solution {

    public static TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        List<TreeNode> ans = new ArrayList<>();
        if (root == null) {
            return null;
        }
        in2(root, ans);
        boolean find = false;
        for (TreeNode c : ans) {
            if (c == p) {
                find = true;
            } else if (find) {
                return c;
            }
        }
        return null;
    }

    private static void in2(TreeNode root, List<TreeNode> ans) {
        if (root == null) {
            return;
        }
        in2(root.left, ans);
        ans.add(root);
        in2(root.right, ans);
    }
}

时间复杂度 O(N),空间复杂度 O(N)

同样,中序遍历可以使用迭代方法来写,思路和递归方法一样,标记遍历到的节点 p,然后设置已遍历的标志位,如果标志位设置过,则下一个遍历到的元素就是后继节点。

完整代码如下,核心就是把中序遍历的递归解改成迭代

public class Solution {
    public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        if (root == null) {
            return null;
        }
        boolean flag = false;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while (!stack.isEmpty() || cur != null) {
            if (cur != null) {
                stack.push(cur);
                cur = cur.left;
            } else {
                cur = stack.pop();
                if (cur == p) {
                    // 遍历到当前位置,记录一下
                    flag = true;
                } else if (flag) {
                    // 下一次遍历的位置,就是后继节点
                    return cur;
                }
                cur = cur.right;
            }
        }
        return null;
    }
}

思路二,使用 Morris 遍历实现中序遍历,这样可以让空间复杂度达到 O(1),时间复杂度依旧 O(N)。Morris 遍历的内容参考:Morris 遍历实现二叉树的遍历。完整代码如下

public class Solution {
    public TreeNode inorderSuccessor(TreeNode head, TreeNode p) {
        if (head == null) {
            return null;
        }
        TreeNode ans = null;
        TreeNode cur = head;
        TreeNode mostRight;
        boolean find = false;
        while (cur != null) {
            mostRight = cur.left;
            if (mostRight != null) {
                while (mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;
                }
                if (mostRight.right == null) {
                    mostRight.right = cur;
                    cur = cur.left;
                    continue;
                } else {
                    mostRight.right = null;
                }
            }
            if (find) {
                ans = cur;
                find = false;
            }
            if (cur == p) {
                find = true;
            }
            cur = cur.right;
        }
        return ans;
    }
}

思路三,

利用二叉搜索树的特性,如果目标节点的右孩子不为空,则目标节点右树最左节点就是目标节点的后继节点,示例如下

image

如果目标节点右孩子为空,则只需要找第一个大于目标节点值的节点即可,根据二叉搜索树的性质,每个节点的右孩子都比当前节点值大,每个节点的左孩子都比当前节点值小。

在遍历过程中,

如果当前节点的值大于目标节点的值,则先记录下当前节点(有可能是备选答案,但是不确定有没有更接近目标值的选择),然后遍历的节点往左边移动,

如果当前节点的值小于目标节点的值,一定不是后继,遍历的节点往右边移动。

如果当前节点的值等于目标节点的值,说明一定找到了后继(因为这个过程中可以确定当前节点没有右孩子,所以,到这一步,肯定是通过后继过来的,或者后继为 null),直接 break 即可。

空间复杂度O(1),时间复杂度O(h),其中 h 为二叉树的高度。

完整代码如下

public class Solution {
        public static TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        if (p == null) {
            return null;
        }
        if (p.right != null) {
            return rightLeftMost(p.right);
        }
        TreeNode successor = null;
        while (root != null) {
            if (root.val > p.val) {
                successor = root;
                root = root.left;
            } else if (root.val < p.val) {
                root = root.right;
            } else {
                break;
            }
        }
        return successor;
    }

    private static TreeNode rightLeftMost(TreeNode p) {
        while (p.left != null) {
            p = p.left;
        }
        return p;
    }
}

类似问题

特殊的树结构,每个结点包含父节点的指针,返回二叉树中指定结点的后继节点。

二叉树结构定义如下:

class Node {
 V value;
 Node left;
 Node right;
 Node parent;
}

主要思路

节点的右子树不为空,则为右子树的最左节点;

如果右子树为空 当前节点如果是父节点的左孩子,则后继节点是其父节点;

当前节点如果是其父节点的右孩子,则继续找当前节点父节点的父节点,一直找到无父节点的那个节点,就是当前结点的后继结点。

如果使用中序遍历,时间复杂度O(N)

优化后的时间复杂度是O(K)

其中 K 为当前节点和后继节点的距离

完整代码如下

public class Code_SuccessorNode {
  public static class Node {
    public int value;
    public Node left;
    public Node right;
    public Node parent;

    public Node(int v) {
      value = v;
    }
  }
  public static Node getSuccessorNode(Node node) {
    if (node == null) {
      return node;
    }
    if (node.right != null) {
      return getLeftMost(node.right);
    } else { // 无右子树
      Node parent = node.parent;
      while (parent != null && parent.right == node) { // 当前节点是其父亲节点右孩子
        node = parent;
        parent = node.parent;
      }
      return parent;
    }
  }

  public static Node getLeftMost(Node node) {
    if (node == null) {
      return null;
    }
    while (node.left != null) {
      node = node.left;
    }
    return node;
  }
}

更多

算法和数据结构笔记

与二叉树中查找后继节点问题相似的内容:

二叉树中查找后继节点问题

二叉树中查找后继节点问题 作者:Grey 原文地址: 博客园:二叉树中查找后继节点问题 CSDN:二叉树中查找后继节点问题 题目描述 给定一个二叉查找树,以及一个节点,求该节点在中序遍历的后继,如果没有则返回 null 题目链接见:LintCode 448 · Inorder Successor i

Vue - 入门

零:前端目前形势 前端的发展史 HTML(5)、CSS(3)、JavaScript(ES5、ES6):编写一个个的页面 -> 给后端(PHP、Python、Go、Java) -> 后端嵌入模板语法 -> 后端渲染完数据 -> 返回数据给前端 -> 在浏览器中查看 Ajax的出现 -> 后台发送异步请

MySQL基础4-数据查询

一、DQL介绍 DQL全称:Data Query Language(数据查询语言),用来查询数据库中表的记录。 关键字:select 二、DQL语法 select 字段列表 from 表名列表 where 条件列表 group by 分组字段列表 having 分组后条件列表 order by 排序

递归在多级数据结构中的简单应用

哈喽,我是小码,半年多没更新了,这段时间换了新工作,工作也很忙。后续会尽量多写点,坚持确实是一件很难,很酷的事情。最近在公司负责开发商品有关的开发,商品包含类型、款式等属性,而类型可能有一级类型、二级类型甚至是三级类型,针对这种多级分类,这就就不好使用简单的查询了。之前也写了一篇文章,Java递归实

【转帖】MySQL索引

数据表如何用索引快速查找 索引是 排好序的快速查找的数据结构 索引存储在文件系统中 索引的文件存储形式与存储引擎有关 索引数据结构:可以是二叉树、红黑树、Hash表、B-Tree、B+Tree 1、二叉树 使用索引的如下图:(如果是使用二叉树结构)每一个节点都存放数据行的磁盘地址【快速定位到数据】

前端开发中的二分查找算法

在前端开发中,处理和搜索大量数据时,效率是一个关键因素。二分查找算法是一种高效的查找算法,适用于在有序数组中快速找到目标值。本文将详细介绍二分查找算法的基本原理、实现方法及其在前端开发中的应用。 什么是二分查找? 二分查找(Binary Search)是一种在有序数组中查找目标值的算法。它通过不断将

详解二分查找

二分法详解 大家好,我是Weekoder! 这是我的第一篇文章,如果有做的不好的地方,请见谅,我会尽力改正。 本文中的图片截取于网络视频,非恶意搬运。 二分法,是一个高效的算法,查找一个数的时间复杂度只需要\(O(\log n)\),大大优化了朴素算法(从头到尾地遍历)\(O(n)\)的线性复杂度。

二分查找 | C++

以此题为例:P2249 【深基13.例1】查找 二分查找 对于一个单调不降的序列 \(S\),传统查找的复杂度是 \(O(|S|)\),即 \(O(n)\). 有时候序列 \(S\) 中的元素特别多,或者你希望尽量减小复杂度,那么,有没有复杂度更低的方法呢? 理论上是不行的,因为读入的复杂度已经达到

文心一言 VS 讯飞星火 VS chatgpt (102)-- 算法导论9.3 8题

八、用go语言,设 X[1..n]和 Y[1..n]为两个数组,每个都包含n个有序的元素。请设计一个 O(lgn)时间的算法来找出数组 X和Y中所有 2n 个元素的中位数。 文心一言: 要在 O(lgn) 时间内找出两个有序数组 X 和 Y 中所有元素的中位数,可以使用二分查找算法。以下是用 Go

[转帖]各种数据结构性能的比较

数据结构包括数组、链表、栈、二叉树、哈希表等等 数据结构优点缺点数组插入快查找慢、删除慢、大小固定有序数组查找快插入慢、删除慢、大小固定栈后进先出存取其他项很慢队列先进先出存取其他项很慢链表插入、删除快查找慢二叉树查找、插入、删除快算法复杂(删除算法)红黑树查找、插入、删除快算法复杂hash表存取极