LeetCode 2476. Closest Nodes Queries in a Binary Search Tree
周賽320。這題Q2就有點過分了,同時要求對二分搜尋樹以及二分搜的理解,缺一不可。
題目
輸入一棵二分搜尋樹的根節點root,共有n個正整數節點。
生成一個大小為n的二維整數陣列answer,其中answer[i] = [mini, maxi]:
- mini為小於等於於queries[i]的最大節點值
- maxi為大於等於於queries[i]的最小節點值
回傳answer陣列。
解法
二分搜尋樹正如其名,就是拿來給你做二分搜的。但是題目可沒說這是平衡樹,他可能是一直線的 linked list,直接在上面找值會退化成線性時間,複雜度變成O(MN),直逼10^11次運算。
一份測資要拿來搜尋M次,不妨利用二分搜尋樹本身的特性,以中序dfs將其轉換成大小N的 有序整數陣列再進行二分搜。遍歷樹的時間為O(N),不清楚特性的朋友也可以隨便次序取值, 最後再花一次O(N log N)排序。
接下來要找第最後一個小於等於queries[i]的整數,以及第一個大於等於queries[i]的整數。
先找到第一個大於等於q[i]的索引位置idx,分類討論四種結果:
- 所有數字都小於q[i],所以idx等於N,將max設為-1,而min就是idx-1
- 正好存在等於q[i]的數,兩者都等於q[i]
- 因為已經過濾掉idx等於q[i]的情況,若idx的數大於q[i],則idx-1一定小於q[i]
- 若idx為0,則不存在小於q[i]的數,設min為-1
總共對大小N的陣列做M次二分搜,時間複雜度O(M log N)。忽略保存答案的O(M),儲存所有節點空間為O(N)。
class Solution:
def closestNodes(self, root: Optional[TreeNode], queries: List[int]) -> List[List[int]]:
a=[]
def dfs(o):
if not o:return
dfs(o.left)
a.append(o.val)
dfs(o.right)
dfs(root)
N=len(a)
ans=[]
for q in queries:
idx=bisect_left(a,q)
if idx==N:
ans.append([a[idx-1],-1])
elif a[idx]==q:
ans.append([q,q])
elif idx>0:
ans.append([a[idx-1],a[idx]])
else:
ans.append([-1,a[idx]])
return ans
如果覺得分類討論很麻煩,也可以改成兩次二分搜:
- 找第一個大於等於q[i]的索引
- 找最後一個小於等於q[i]的索引
只要超出陣列邊界(等於N或是小於0)則設為-1。
class Solution:
def closestNodes(self, root: Optional[TreeNode], queries: List[int]) -> List[List[int]]:
a=[]
def dfs(o):
if not o:return
dfs(o.left)
a.append(o.val)
dfs(o.right)
dfs(root)
N=len(a)
ans=[]
for q in queries:
mx=mn=-1
idx=bisect_left(a,q)
if idx<N:
mx=a[idx]
idx=bisect_right(a,q)-1
if idx>=0:
mn=a[idx]
ans.append([mn,mx])
return ans