这是一种查找理论最大值和最小值以确定子列表是否甚至能够保存搜索值的方法。有了它,你可以修剪一些树枝。
你的方法和这个的区别:
在这里,我们看两半。但也短路不可能的分支仍然尝试实现 O(log n)
from math import ceil
from typing import List
def th_min_max(x: List[int], li: int, ri: int) -> int:
"""
Find the theoretical maximum and minimum values
for all elements between two known "anchor" values
x is the data, where each abs(x[i] - x[i+1] <= 1)
li, ri are the indexes of the anchors.
"""
assert li <= ri
# these are known values at the anchor locations.
v1 = x[li]
v2 = x[ri]
# so how high/low can the numbers inbetween go?
# the theoretical maximum is a sustained increase followed by a sustained decrease.
num_changes = ri - li
# some of the numbers are used to bridge difference between v1, v2
remaining = num_changes - abs(v1 - v2)
assert remaining >= 0, f"The input array does not meet constraints; look at [{li}] --> {v1} and [{ri}] --> {v2}"
# so now we are look at maximum increase/decrease possible once we've bridged
# the gap between the two known numbers.
# half the remaining changes have to be used to 'undo' the initial changes
# if there is an odd number, the remaining one can be max/min,
# so use the ceil( ) function to round up.
biggest_delta = int(ceil(remaining / 2))
return (min(v1, v2) - biggest_delta, max(v1, v2) + biggest_delta)
有了 min/max 函数,我们可以做一个分区算法:
def find(x: List[int], li: int, ri: int, search: int) -> int:
"""
finds _some_ index where the search value is found in x
between index li and index ri; -1 if not found.
"""
# first, if li = ri, this is a trivial is this it? question
if li == ri:
return li if x[li] == search else -1
# second, figure out if this range could possibly have the number
mn, mx = th_min_max(x, li, ri)
if search < mn:
# value being looked for _cannot_ be in range [li, ri]
return -1
if search > mx:
# value being looked for _cannot_ be in range [li, ri]
return -1
mid = int((li + ri)/2)
# we ma have to look both ways, but let's start on the
# side with the anchor value closer to search value
if abs(x[li] - search) <= abs(search - x[ri]):
# go left first, then right.
first = find(x, li, mid, search)
return first if first >= 0 else find(x, mid + 1, ri, search)
else:
# go right first, then left
first = find(x, mid + 1, ri, search)
return first if first >= 0 else find(x, li, mid, search)
测试您的输入:
d = [1,2,3,4,5,4,3,3,2,3,4,5,6,7,8]
for search in d:
res = find(d, 0, len(d)-1, search)
print(search, res, d[res])
-->
1 0 1
2 1 2
3 6 3
4 5 4
5 11 5
4 5 4
3 6 3
3 6 3
2 1 2
3 6 3
4 5 4
5 11 5
6 12 6
7 13 7
8 14 8