给定n
整数,排列成一个圆圈,显示一种可以找到一个峰值的有效算法。一个峰值是一个不小于它旁边的两个数字的数字。
一种方法是遍历所有整数并检查每个整数以查看它是否是峰值。这就产生O(n)
了时间。似乎应该有一些方法来分而治之,以提高效率。
给定n
整数,排列成一个圆圈,显示一种可以找到一个峰值的有效算法。一个峰值是一个不小于它旁边的两个数字的数字。
一种方法是遍历所有整数并检查每个整数以查看它是否是峰值。这就产生O(n)
了时间。似乎应该有一些方法来分而治之,以提高效率。
好吧,Keith Randall 证明我错了。:)
这是用 Python 实现的 Keith 的解决方案:
def findPeak(aBase):
N = len(aBase)
def a(i): return aBase[i % N]
i = 0
j = N / 3
k = (2 * N) / 3
if a(j) >= a(i) and a(j) >= a(k)
lo, candidate, hi = i, j, k
elif a(k) >= a(j) and a(k) >= a(i):
lo, candidate, hi = j, k, i + N
else:
lo, candidate, hi = k, i + N, j + N
# Loop invariants:
# a(lo) <= a(candidate)
# a(hi) <= a(candidate)
while lo < candidate - 1 or candidate < hi - 1:
checkRight = True
if lo < candidate - 1:
mid = (lo + candidate) / 2
if a(mid) >= a(candidate):
hi = candidate
candidate = mid
checkRight = False
else:
lo = mid
if checkRight and candidate < hi - 1:
mid = (candidate + hi) / 2
if a(mid) >= a(candidate):
lo = candidate
candidate = mid
else:
hi = mid
return candidate % N
这是一个递归O(log n)
算法。
假设我们有一个数字数组,并且我们知道该段的中间数字不小于端点:
A[i] <= A[m] >= A[j]
对于 i,j 索引到数组中,并且m=(i+j)/2
. 检查端点和中点之间的元素,即索引x=(3*i+j)/4
和处的元素y=(i+3*j)/4
。如果A[x]>=A[m]
,则在区间上递归[i,m]
。如果A[y]>=A[m]
,则在区间上递归[m,j]
。否则,在区间上递归[x,y]
。
在每种情况下,我们都保持上述区间的不变量。最终我们得到一个大小为 2 的区间,这意味着我们找到了一个峰值(将是A[m]
)。
要将圆转换为数组,请取 3 个等距样本并调整自己的方向,使最大的(或与最大的并列的)位于区间的中间,而另外两个点是端点。运行时间是O(log n)
因为每个间隔都是前一个间隔大小的一半。
我已经掩盖了计算索引时如何舍入的问题,但我认为你可以成功地解决这个问题。
当你说“排列成一个圆圈”时,你的意思是像一个循环链表还是什么?从您描述数据集的方式来看,听起来这些整数是完全无序的,没有办法查看 N 个整数并得出关于其他任何整数的任何结论。如果是这种情况,那么蛮力解决方案是唯一可能的解决方案。
编辑:
好吧,如果您不关心最坏情况的时间,那么有一些更有效的方法可以做到这一点。天真的方法是查看 N i、 N i-1和 N i+1以查看 Ni 是否是一个峰值,然后重复,但你可以做得更好。
While not done
If N[i] < N[i+1]
i++
Else
If N[i]>N[i-1]
Done
Else
i+=2
(嗯,不完全是,因为你必须处理 N[i]=N[i+1] 的情况。但是非常相似。)
这至少可以防止您将 N i与 N i+1进行比较,将 1 添加到 i,然后将 N i与 N i-1进行冗余比较。不过,这是一个明显的边际收益。你仍然在通过数字前进,但没有办法解决这个问题;盲目地跳跃是没有帮助的,如果不花时间做实际的工作,就没有办法展望未来。