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

題目描述

給定一棵非空二叉搜索樹以及一個(gè)target值,找到 BST 中最接近給定值的 k 個(gè)數(shù)。

給出的target值為浮點(diǎn)數(shù)

你可以假設(shè) k 總是合理的,即 k ≤ 總節(jié)點(diǎn)數(shù)

我們可以保證給出的 BST 中只有唯一一個(gè)最接近給定值的 k 個(gè)值的集合

假設(shè)是一棵平衡二叉搜索樹,你可以用時(shí)間復(fù)雜度低于O(n)的算法解決問(wèn)題嗎( n 為節(jié)點(diǎn)個(gè)數(shù))?


測(cè)試樣例

輸入:

{1}

0.000000

1

輸出:

[1]

解釋:

二叉樹 {1},表示如下的樹結(jié)構(gòu):

1

輸入:

{3,1,4,#,2}

0.275000

2

輸出:

[1,2]

解釋:

二叉樹 {3,1,4,#,2},表示如下的樹結(jié)構(gòu):

? 3

/? \

1? ? 4

\

? 2


解題思路


1、堆棧

設(shè)置兩個(gè)stack,分別從兩個(gè)方向?qū)ふ遗ctarget接近的節(jié)點(diǎn),其中一個(gè)存儲(chǔ)的值比target大,記為next_stack,另一個(gè)則不大于target,記為prev_stack。

先對(duì)兩個(gè)棧進(jìn)行初始化,當(dāng)target小于當(dāng)前節(jié)點(diǎn)時(shí),將該節(jié)點(diǎn)入棧next_stack,同時(shí)將節(jié)點(diǎn)置為其左兒子;否則,將該節(jié)點(diǎn)入棧prev_stack,并將節(jié)點(diǎn)置為其右兒子;重復(fù)這個(gè)過(guò)程直至節(jié)點(diǎn)為空。

接下來(lái)進(jìn)入循環(huán),循環(huán)k次,代表每輪循環(huán)都會(huì)找到一個(gè)接近target的數(shù)。循環(huán)一開始先判斷兩個(gè)stack是否都為空,如果是,則跳出循環(huán)。

然后,比較兩個(gè)stack的棧頂與target的接近程度(若其中一個(gè)棧為空,則將其余target的差值設(shè)置為一個(gè)極大值例如sys.maxsize),若prev_stack的棧頂元素與target較接近,則將其加入到結(jié)果列表中,同時(shí)要在該棧中找到下一個(gè)節(jié)點(diǎn);否則,對(duì)next_stack進(jìn)行同樣的處理。

最后,待循環(huán)結(jié)束返回找到的k個(gè)數(shù)即可。

另外,這里說(shuō)明下拿出棧頂元素后找到下一個(gè)節(jié)點(diǎn)的方法,以prev_stack為例:

prev_stack中的值是不大于target的,在初始化的時(shí)候。一直是往右子樹的方向中找,從而去逼近target。于是,拿出棧頂元素后,就從棧頂?shù)淖髢鹤娱_始,從這個(gè)左兒子往其右子樹的方向中去找,每找到一個(gè)節(jié)點(diǎn)都將其入棧,并將當(dāng)前節(jié)點(diǎn)置為其右兒子,節(jié)點(diǎn)為空。

為什么要從棧頂?shù)淖髢鹤娱_始而不是從其父節(jié)點(diǎn)開始呢?因?yàn)闂m斒瞧涓腹?jié)點(diǎn)的右兒子,因此棧頂?shù)淖髢鹤与m比棧頂小但比其父節(jié)點(diǎn)還是大的,相比較而言更接近target,因?yàn)閜rev_stack中都是小于target的,所以在prev_stack中,越大就越接近target。

2、先序遍歷

從根節(jié)點(diǎn)開始往左子樹方向遍歷,每遇到一個(gè)節(jié)點(diǎn)就加入棧中,同時(shí)將節(jié)點(diǎn)更新為左兒子,重復(fù)這個(gè)過(guò)程直至節(jié)點(diǎn)為空。

然后,進(jìn)入循環(huán),直至棧空。每次循環(huán)都彈出棧頂,將其加入到結(jié)果列表中,使得列表是從小到大排序的。

循環(huán)一開始,需要判斷下列表中是否已經(jīng)有超過(guò)k個(gè)數(shù),如果是并且當(dāng)前棧頂?shù)倪@個(gè)數(shù)與target的接近程度不如列表中的倒數(shù)第k個(gè)數(shù),那么就可以跳出循環(huán)了,因?yàn)闂:罄m(xù)的元素都比棧頂大,那么它們與target的接近程度就更加遠(yuǎn)了。

當(dāng)棧頂加入到結(jié)果列表后,將其更新為右兒子,若右兒子不為空,則入棧,并從這個(gè)右兒子的左子樹方向一直遞歸,每到一個(gè)節(jié)點(diǎn)就將其入棧,直至節(jié)點(diǎn)為空。

以上就是每次循環(huán)的操作,循環(huán)結(jié)束后返回結(jié)果列表的倒數(shù)前k個(gè)元素即為所求。


代碼


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 = [], []


? ? ? ? # 從兩個(gè)方向逼近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中,上次已經(jīng)入棧了所有右子樹,

? ? ? ? # 因此彈出當(dāng)前節(jié)點(diǎn)后,要繼續(xù)逼近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中,上次已經(jīng)入棧了所有左子樹

? ? ? ? # 因此彈出當(dāng)前節(jié)點(diǎn)后,要繼續(xù)逼近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中的節(jié)點(diǎn)是根據(jù)值從小到大排序的

? ? ? ? ? ? # 若當(dāng)前其中已經(jīng)超過(guò)了k個(gè),并且倒數(shù)第k個(gè)與target的差

? ? ? ? ? ? # 已經(jīng)小于當(dāng)前棧彈出的節(jié)點(diǎn),那么就已經(jīng)獲得了最接近target的k個(gè)數(shù)了

? ? ? ? ? ? # 因?yàn)闂V蟮墓?jié)點(diǎn)與target的差都比棧頂?shù)倪@個(gè)節(jié)點(diǎn)大

? ? ? ? ? ? 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


? ? ? ? # 最后返回倒數(shù)前k嘅數(shù)即為所求

? ? ? ? return ans[-k:]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容