272. Closest Binary Search Tree Value II
Description
Given the root of a binary search tree, a target value, and an integer k, return the k values in the BST that are closest to the target. You may return the answer in any order.
You are guaranteed to have only one unique set of k values in the BST that are closest to the target.
Idea
题目要求是在二叉搜索树中找到与target最接近的k个值。
最简单的做法:
直接inorder traversal得到整个树的list, 并在此list中找到第一个>=target的值,以此值为中心点向两边寻找最接近target的值。
该做法也可以改进成为一个dfs function: 把res作为parameter传进inorder traversal的function中, 并且res中只保留k个离target最近的数。res可以用deque实现。这样就可以当新遇到的数比res[0]离target近时,以O(1)的时间pop掉res[0]。但无论怎样,时间复杂度为O(n) (n 是 node的个树)空间复杂度为 O(k).
优化的算法:
利用两个stack, 一个用来存target之前的node, 另一个用来存target之后的node。得到这两个stack之后,每次只需要比较stack的最后一个数与target的距离,选最小的一个append到result里即可。
该方法并不需要遍历整个tree。在initialize stack的时候,只需要找到离target最近的前后node和其同枝的node即可。当每次从stack中取出node的时候,再继续把当前pop出node的前或后node加进stack即可。
时间复杂度在最差的情况下依然是O(n) 但当tree比较平衡时可以是O(h + k),因为每一层遍历是,stack中either append右子树的node or append左子树的node 所以对树的搜索是 O(h),O(k)是获取k个结果。
空间复杂度:O(n)
Code
brute-force
时间复杂度为O(n) (n 是 node的个树)空间复杂度为 O(k).
class Solution:
def closestKValues(self, root: Optional[TreeNode], target: float, k: int) -> List[int]:
value_list = self.inorder(root)
pos = self.binary_search(value_list, target)
res = []
left, right = pos - 1, pos
for _ in range(k):
if self.is_left_closer(value_list, target, left, right):
res.append(value_list[left])
left -= 1
else:
res.append(value_list[right])
right += 1
return res
def inorder(self, root):
if not root:
return []
return self.inorder(root.left) + [root.val] + self.inorder(root.right)
def binary_search(self, input_list, target): # find the first value >= target
start, end = 0, len(input_list) - 1
while start < end - 1:
mid = (start + end) // 2
if input_list[mid] < target:
start = mid
else:
end = mid
if input_list[start] >= target:
return start
return end
def is_left_closer(self, value_list, target, left, right):
if left < 0:
return False
if right > len(value_list) - 1:
return True
if abs(target - value_list[left]) <= abs(target - value_list[right]):
return True
return False
DFS
class Solution:
def closestKValues(self, root: Optional[TreeNode], target: float, k: int) -> List[int]:
res = collections.deque([])
self.inorder(root, res, target, k)
return res
def inorder(self, root, res, target, k):
if not root:
return
self.inorder(root.left, res, target, k)
if len(res) == k:
if abs(root.val - target) < abs(res[0] - target):
res.popleft()
else:
return
res.append(root.val)
self.inorder(root.right, res, target, k)
search on tree
class Solution:
def closestKValues(self, root: Optional[TreeNode], target: float, k: int) -> List[int]:
if not root:
return []
res, pre_stack, next_stack = [], [], []
while root:
if root.val >= target:
next_stack.append(root)
root = root.left
else:
pre_stack.append(root)
root = root.right
for _ in range(k):
next_diff = float('inf') if not next_stack else next_stack[-1].val - target
pre_diff = float('inf') if not pre_stack else target - pre_stack[-1].val
if next_diff > pre_diff:
res.append(pre_stack[-1].val)
self.get_pre(pre_stack)
else:
res.append(next_stack[-1].val)
self.get_next(next_stack)
return res
def get_next(self, next_stack):
curr = next_stack.pop()
node = curr.right
while node:
next_stack.append(node)
node = node.left
def get_pre(self, pre_stack):
curr = pre_stack.pop()
node = curr.left
while node:
pre_stack.append(node)
node = node.right