有人可以确认我的比特序列算法中的最小元素是否正确吗?

时间:2013-09-26 23:59:36

标签: java c++ algorithm data-structures

我有一个与我的序列,首先单调递减,然后单调递增以下形式,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];

如果我的算法是否正确,有人可以确认我吗?

3 个答案:

答案 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]