LintCode 901. 二叉搜索树中最接近的值 II

题目描述

给定一棵非空二叉搜索树以及一个target值,找到 BST 中最接近给定值的 k 个数。

给出的target值为浮点数

你可以假设 k 总是合理的,即 k ≤ 总节点数

我们可以保证给出的 BST 中只有唯一一个最接近给定值的 k 个值的集合

假设是一棵平衡二叉搜索树,你可以用时间复杂度低于O(n)的算法解决问题吗( n 为节点个数)?


测试样例

输入:

{1}

0.000000

1

输出:

[1]

解释:

二叉树 {1},表示如下的树结构:

1

输入:

{3,1,4,#,2}

0.275000

2

输出:

[1,2]

解释:

二叉树 {3,1,4,#,2},表示如下的树结构:

  3

/  \

1    4

\

  2


解题思路


1、堆栈

设置两个stack,分别从两个方向寻找与target接近的节点,其中一个存储的值比target大,记为next_stack,另一个则不大于target,记为prev_stack。

先对两个栈进行初始化,当target小于当前节点时,将该节点入栈next_stack,同时将节点置为其左儿子;否则,将该节点入栈prev_stack,并将节点置为其右儿子;重复这个过程直至节点为空。

接下来进入循环,循环k次,代表每轮循环都会找到一个接近target的数。循环一开始先判断两个stack是否都为空,如果是,则跳出循环。

然后,比较两个stack的栈顶与target的接近程度(若其中一个栈为空,则将其余target的差值设置为一个极大值例如sys.maxsize),若prev_stack的栈顶元素与target较接近,则将其加入到结果列表中,同时要在该栈中找到下一个节点;否则,对next_stack进行同样的处理。

最后,待循环结束返回找到的k个数即可。

另外,这里说明下拿出栈顶元素后找到下一个节点的方法,以prev_stack为例:

prev_stack中的值是不大于target的,在初始化的时候。一直是往右子树的方向中找,从而去逼近target。于是,拿出栈顶元素后,就从栈顶的左儿子开始,从这个左儿子往其右子树的方向中去找,每找到一个节点都将其入栈,并将当前节点置为其右儿子,节点为空。

为什么要从栈顶的左儿子开始而不是从其父节点开始呢?因为栈顶是其父节点的右儿子,因此栈顶的左儿子虽比栈顶小但比其父节点还是大的,相比较而言更接近target,因为prev_stack中都是小于target的,所以在prev_stack中,越大就越接近target。

2、先序遍历

从根节点开始往左子树方向遍历,每遇到一个节点就加入栈中,同时将节点更新为左儿子,重复这个过程直至节点为空。

然后,进入循环,直至栈空。每次循环都弹出栈顶,将其加入到结果列表中,使得列表是从小到大排序的。

循环一开始,需要判断下列表中是否已经有超过k个数,如果是并且当前栈顶的这个数与target的接近程度不如列表中的倒数第k个数,那么就可以跳出循环了,因为栈后续的元素都比栈顶大,那么它们与target的接近程度就更加远了。

当栈顶加入到结果列表后,将其更新为右儿子,若右儿子不为空,则入栈,并从这个右儿子的左子树方向一直递归,每到一个节点就将其入栈,直至节点为空。

以上就是每次循环的操作,循环结束后返回结果列表的倒数前k个元素即为所求。


代码


1、堆栈

import sys

"""

Definition of TreeNode:

class TreeNode:

    def __init__(self, val):

        self.val = val

        self.left, self.right = None, None

"""

class Solution:

    """

    @param root: the given BST

    @param target: the given target

    @param k: the given k

    @return: k values in the BST that are closest to the target

    """

    def closestKValues(self, root, target, k):

        if not root or not k:

            return []

        closest_ks = []

        prev_stack, next_stack = self.bulid_stacks(root, target)


        for _ in range(k):

            if not (len(prev_stack) or len(next_stack)):

                break


            delta_prev = abs(prev_stack[-1].val - target) if prev_stack else sys.maxsize

            delta_next = abs(next_stack[-1].val - target) if next_stack else sys.maxsize


            if delta_prev < delta_next:

                closest_ks.append(self.get_prev(prev_stack))

            else:

                closest_ks.append(self.get_next(next_stack))


        return closest_ks


    def bulid_stacks(self, node, target):

        prev_stack, next_stack = [], []


        # 从两个方向逼近target

        while node:

            # next_stack往值小的方向走,但值比target大

            if target < node.val:

                next_stack.append(node)

                node = node.left

            # prev_stack往值大的方向走,但值比target小

            else:

                prev_stack.append(node)

                node = node.right


        return prev_stack, next_stack


    def get_prev(self, prev_stack):

        node = prev_stack.pop()

        value = node.val


        # prev_stack中,上次已经入栈了所有右子树,

        # 因此弹出当前节点后,要继续逼近target,

        # 就要从其左儿子的右子树开始

        node = node.left

        while node:

            prev_stack.append(node)

            node = node.right


        return value


    def get_next(self, next_stack):

        node = next_stack.pop()

        value = node.val


        # next_stack中,上次已经入栈了所有左子树

        # 因此弹出当前节点后,要继续逼近target,

        # 就要从其右儿子的左子树开始

        node = node.right

        while node:

            next_stack.append(node)

            node = node.left


        return value


2、先序遍历

"""

Definition of TreeNode:

class TreeNode:

    def __init__(self, val):

        self.val = val

        self.left, self.right = None, None

"""

class Solution:

    """

    @param root: the given BST

    @param target: the given target

    @param k: the given k

    @return: k values in the BST that are closest to the target

    """

    def closestKValues(self, root, target, k):

        ans = []

        stack = []


        # 先序遍历,从左路一直下去,栈顶获得最小值

        while root:

            stack.append(root)

            root = root.left


        while stack:

            node = stack.pop()


            # ans中的节点是根据值从小到大排序的

            # 若当前其中已经超过了k个,并且倒数第k个与target的差

            # 已经小于当前栈弹出的节点,那么就已经获得了最接近target的k个数了

            # 因为栈之后的节点与target的差都比栈顶的这个节点大

            if len(ans) >= k and abs(ans[-k] - target) < abs(node.val - target):

                break


            ans.append(node.val)

            node = node.right


            while node:

                stack.append(node)

                node = node.left


        # 最后返回倒数前k嘅数即为所求

        return ans[-k:]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。