我有一个与我的序列,首先单调递减,然后单调递增以下形式,f(1)> f(2)> f(3)> f(4).......> f(k-1)> f(k)< f(k + 1)< f(k + 2)< .......< f(n)和我希望能够找到元素k使得f(k)是序列中的最小值并且在O(lgn)时间内,为了解决这个问题,我设计了以下基于二进制搜索的算法,可以有人告诉我算法是否正确 - :
用于确定二进制搜索将在何处递归的属性 - :
FindMin(F , lo , hi)
if(lo == hi) return F[lo];
int mid = lo + (hi-lo)/2;
// recurse to the left
if(F[mid] < F[mid+1]) return FindMin(F , lo , mid);
// recurse to the right
if(F[mid] < F[mid - 1]) return FindMin(F , mid , hi);
return F[mid];
如果我的算法是否正确,有人可以确认我吗?
答案 0 :(得分:2)
您当前的算法不正确,但您非常接近它。在这里,我将展示您的算法可能出错的地方。
考虑数组[3,2,1,2]
。
首先假设您致电FindMin(F, 0, 3)
FindMin(F, 0, 3) --mid = 1 --Check F[1] False --Check F[1] True, call FindMin(F, 1, 3) ----mid = 2 ----Check F[2] True, call FindMin(F, 1, 2) ------mid = 1 ------Check F[1] False ------Check F[1] True, call FindMin(F, 1, 2) --------mid = 1 --------Check F[1] False --------Check F[1] True, call FindMin(F, 1, 2) ... This will continue forever until out of memory
您可以稍微更改一下以使其正确无误:
FindMin(F, lo, hi){
if(lo==hi) return F[lo];
int mid = lo + (hi-lo)/2 // Actually you can change this into (lo+hi)/2
if(F[mid] > F[mid+1]) return FindMin(F, mid+1, hi) // Change the comparison and recursive call!
if(F[mid] > F[mid-1]) return FindMin(F, lo, mid-1) // Change the comparison and recursive call!
// If we reach here, that means F[mid-1] > F[mid] < F[mid+1]
return F[mid]
}
虽然正如@Joni所说,你需要处理边界案件。仅检查F[mid+1]
即可。我保证以下代码不会出现任何越界错误并且更正:
FindMin(F, lo, hi){
if(lo==hi) return F[lo]; // Line 1
int mid = (lo+hi)/2 // Line 2
if(F[mid] > F[mid+1]) return FindMin(F, mid+1, hi) // Line 3
else return FindMin(F, lo, mid) // Line 4
}
使用hi
作为数组中的最后一个索引
第1行是基本情况。
第2行计算中间索引。请注意mid < hi
此处为mid == hi
,因为lo == hi
隐含{1}},已在第1行中介绍过。因此mid
永远不会指向数组中的最后一个索引。因此,检查F[mid+1]
第3行检查F[mid] > F[mid+1]
是否为F[mid]
,然后F[lo..mid-1]
不能成为答案,因为它大于某个数字,F[mid+1..hi]
也会更大,所以答案必须在FindMin(F, mid+1, hi)
。所以请致电mid+1 > lo
。请注意mid+1..hi
,因此范围lo..hi
小于F[mid] < F[mid+1]
。
第4行:如果我们到达此处,则表示F[lo..mid]
。所以答案可以在FindMin(F, lo, mid)
的任何地方。所以请致电mid < hi
。请注意,由于FindMin(F, lo, mid)
,FindMin(F, lo, hi)
与FindMin
不同。更具体地说,范围减小,如第3行中的情况。
组合第3行和第4行,每次调用{{1}}的范围都在缩小范围内,因此算法应在一段时间后停止,这将在第1行中停止。
答案 1 :(得分:1)
假设mid = k
。然后第一个条件成立,你重复lo, mid
。在该部分中,元素按降序排列,它将遵循第二个条件,直到您在k-1, k
上重复出现。但mid = k-1 + (k-(k-1))/2 = k-1
按整数除法。这个不对。它将继续满足第二个条件 - k-1, k
上的无限循环。
看起来你的比较标志是倒退的。
答案 2 :(得分:1)
算法的基本原理是正确的:使用中间点来确定最小值是向右还是向左。
但这并不完全正确。要想看看mid
是最小元素时会发生什么:自F[mid] < F[mid+1]
以及F[mid] < F[mid-1]
以来,你永远无法返回最小值;你进入无限递归。虽然有一个简单的解决方法。
您必须记住的另一件事是,如果最小值是序列中的第一个或最后一个元素会发生什么:由于索引超出范围,您无法计算F[mid-1]
或F[mid+1]