我已经尝试使用谷歌搜索这个没有太大成功...我确定这个问题的技术名称或类似的问题,但我似乎无法找到答案。
给定一个整数列表L
,严格增加,然后严格减少,找到该列表的最大值和最小值。
例如,L
可能是{1 2 3 4 5 4 3 2}
或{2 4 5 7 3}
。
为了找到最小值,我说最小的整数必须是左端点或右端点,所以只需比较端点,然后返回最小的端点,给出恒定的时间。
为了找到最大值,我建议基本上使用递归二进制搜索来找到点L[x]
,使得L[x] > L[x-1]
和L[x] > L[x+1]
,给出摊销的lg(n)时间。他似乎并不喜欢这个答案,而且对我来说似乎相当幼稚,所以我想知道是否有一些我不知道的事情。
感谢您的帮助!
编辑:
我在python中的解决方案:
def Max(L):
n = len(L)-1
if n == 0:
return L[0]
if L[n/2] > L[n/2 - 1] and L[n/2] > L[n/2 + 1]:
return L[n/2]
elif L[n/2] < L[n/2 + 1]:
return Max(L[n/2:])
else:
return Max(L[:n/2])
答案 0 :(得分:4)
好的,看了一下,你有两个选择。更简单的是ternary search.它的基本要点是,你找到了通过的两个数字1/3(x)和2/3(y)。如果x < y,那么最大值不能在前三分之一。如果x> y,它不能在最后三分之一。将它与基本案例的简单检查相结合,您就拥有了一个递归算法。
现在,它仍然是O(log(n)),因此每次调用的比较为一半,但只有2/3的消除,你真的从2 * log(base 2)(n)比较到2 * log(base 3)(n)比较。从理论上讲,这些是等价的。实际上,除非你没有随机访问,例如功能,否则你获得的数量并不多。
更复杂的是Fibonacci search.另请参阅here.它与三元搜索类似,除了选择1/3和2/3作为断点之外,还有一个完整的奇特过程涉及斐波纳契数。它仍然是O(log(n)),所以除非你没有直接的随机访问权限,否则它可能不值得实施。
答案 1 :(得分:3)
我假设 list 是一个数组---否则就像线性搜索一样,正如DeepYellow指出的那样。
稍微不同的策略应该能够减少找到最大值所需的平均比较次数的一半。策略是在列表中识别具有特定条件的间隔:左端点,右端点和中点,中点处的列表值大于任一端点处的列表值。这个结构---中点最高,有两个包围点定义区间---是在搜索中建立和保留的不变量。将中间点称为当前最大值。
要建立不变量,初始端点可以只是列表的末尾。检查中点的列表值。如果它大于端点,那么这些将作为搜索的起点。否则,递归获取列表的左半部分(如果中点的值低于左点)或右半部分(如果中点的值低于右点的值)并再次检查中点。
要实现搜索,请首先检查间隔左半部分的中点。如果这大于前一个中点,则将前一个中点定义为间隔的右端点,将新中点定义为当前最大值。如果左中点小于当前最大值,则在间隔的右半部分执行相同的操作。如果右中点较大,则右中点变为当前最大值,旧中点变为新左端点。否则,当前最大值不变,但左中点成为新的左端点,右中点成为新的左端点。
平均而言,你将在左半部分获得新的中点,进行1/3的比较,否则需要2/3的比较。平均而言,只有一半。
这是Python中的一个实现:
def find_max(lst, lend, midp, rend):
assert lst[lend] < lst[midp] and lst[rend] < lst[midp], \
"Invariant violated, invalid sequence"
lmid = (lend + midp) // 2
rmid = (rend + midp + 1) // 2
if lend + 2 == rend:
return midp
elif lst[lmid] > lst[midp]:
return find_max(lst, lend, lmid, midp)
elif lst[rmid] > lst[midp]:
return find_max(lst, midp, rmid, rend)
else:
return find_max(lst, lmid, midp, rmid)
def init_invariant(lst, lend, rend):
assert lend + 1 < rend, "Invariant violated, invalid sequence"
midp = (lend + rend) // 2
if lst[midp] < lst[lend]:
return init_invariant(lst, lend, midp)
elif lst[midp] < lst[rend]:
return init_invariant(lst, midp, rend)
else:
return lend, midp, rend
def maximize(lst):
lend, midp, rend = init_invariant(lst, 0, len(lst)-1)
return find_max(lst, lend, midp, rend)
答案 2 :(得分:0)
如果L是一个列表(通常意义上是链表),那么二进制搜索的速度很慢,因为寻找第i个元素是O(N)。在这种情况下,我想不出更快的方法,而不是返回列表中的后继者小于其自身的第一个整数。
编辑:
好的,假设L是随机访问(如下面评论中要求的海报)。您不能进行SIMPLE二进制搜索,因为这需要对列表进行排序,而这显然不是。所以二元搜索显然是错误的 - 因此面试官的不满。
但是你可以在L的变换上进行二元搜索:
T[i] = If L[i+1]-L[i] > 0 then 0 else 1
此变换列表正在增加,搜索1个元素的下限表示L的正确索引。
例如,如果L = {1 2 3 4 5 4 3 2}
,则T = {0 0 0 0 1 1 1}
我不主张实际创造T,这将是愚蠢的。但是应该调整二分搜索的谓词,并且搜索的范围应该缩短一,因为T的元素比L少一个。