在旋转阵列中找到最小值

时间:2015-07-11 15:44:17

标签: c++ algorithm vector stl binary-search

我试图找到已旋转的已排序数组中的最小元素。

示例:

     1 2 3 4 5 => sorted

     3 4 5 1 2 => Rotated => Find the min in this in O(log n)

我试过写代码:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int bs(vector<int> a)
{
    int l =0, r = a.size()-1;

    while(l<=r)
    {
        int mid = l + (r-l)/2; // Mid guaranteed to be in range

        if(a[mid-1]>a[mid]) return a[mid];

        if(a[mid]>a[l])
        {
            l=mid+1 ; continue;
        }
        else
        {
            r = mid-1 ; //Mid has been checked
        }
    }
}

int main()
{
  vector<int> a = {1,2,3,5,6,7,8,9,10};
  rotate(a.begin(),a.begin()+4,a.end()); //  Make 6 the head 
  for(auto x:a) cout<<x<<endl;
  cout <<"Min is " <<bs(a) <<endl;
  return 0;
}

我得到了输出:

6
7
8
9
10
1
2
3
5
Min is 3

,这显然是错误的。我想我正在操纵二进制搜索条件以适应我的问题,但我无法弄清楚出了什么问题。

我的方法类似于this,我认为我在逻辑上是正确的,这当然是错误的思考。

5 个答案:

答案 0 :(得分:1)

你有正确的策略,但没有清楚地考虑不变量。

我将假设元素是不同的。没有它,就不能在O(log n)时间内完成。 (考虑除了单个1之外所有元素都为0的情况。)

如果a[0] < a[size-1],则没有有效轮换,因此a[0]是最小值。

否则,有两个不断增加的运行a[0]<a[1]<...<a[k-1]a[k]<a[k+1]<...<a[size-1],我们也知道a[k-1]>a[k]

我们想找到k。

开始 - 正如你所做的那样 - 用猜测的括号[0,size-1]。

不变量是此括号必须始终包含k。当然这对于初始支架来说确实如此!

为确保终止,我们必须在每次迭代期间将其缩小。当间隔只有一个元素,即lo == hi时,我们就有了答案。

计算一个新的猜测就像你展示的那样,

int mid = (lo + hi) / 2;

现在,有什么可能性?中位于[0,k-1]或[k,size-1]。

如果是前者,那么我们知道mid&lt; = k-1。我们可以在保持不变量的同时制作括号[mid + 1,hi]。请注意,这总是使支架更小,确保终止。

如果是后者,我们知道mid&gt; = k,所以可以使用[lo,mid]。注意我们不能说[lo,mid-1],因为mid 可能等于k,打破了不变量。

这引起了另一个担忧。如果mid的计算产生mid == hi,那么新的括号与旧的相同。我们没有进步和无限循环。令人高兴的是,这种情况永远不会发生,因为(lo + hi) / 2 < hi只要lo < hi

最后一个难题是如何判断哪个运行mid。这很容易。如果a[mid] >= a[0],我们知道它位于第一次运行中。否则它就在第二个。

在代码中包装所有这些:

if (a[0] < a[size - 1]) return 0;
int lo = 0, hi = size - 1;
while (lo < hi) {
  int mid = (lo + hi) / 2;
  if (a[mid] >= a[0])
    lo = mid + 1;
  else
    hi = mid;
}
return lo;

答案 1 :(得分:0)

只需使用

找到旋转点
auto sorted_end = is_sorted_until(a.begin(), a.end());

来自cppreference的说明:

  

检查范围[first, last)并找到从头开始的最大范围,其中元素按升序排序。

要以两种旋转方式获得最小值,请使用

min(a.front(), *is_sorted_until(a.begin(), a.end()))

这小于O(n)但不是O(log n)

编辑由于您关联了SO帖子,我正在翻译C++

中的答案
int findMin(vector<int> & arr) {
    int low = 0;
    int high = arr.size() - 1;
    while (arr[low] > arr[high]) {
        int mid = (low + high) >> 1;
        if (arr[mid] > arr[high])
            low = mid + 1;
        else
            high = mid;
    }
    return arr[low];
}

http://ideone.com/BlzrWj处查看它。

答案 2 :(得分:0)

这有赋值的气味,所以没有标准库。

我的裁员是通过寻找不连续性找到支点:

int findpivot(vector<int> a)
{
    int left = 0;
    int right = a.size() - 1;

    while (a[left] > a[right])
    {
        int mid = (right + left) / 2;
        if (a[mid] > a[right])
        { 
            left = mid + 1;
        }
        else
        {
            right = mid;
        }
    }
    return left;
}

然后我会看到低位的枢轴两侧的第一个值。运行找到的函数(事后看来,&#34; Duh!&#34;)它总是返回最低值的索引,因为我在转轴处寻找不连续的方式。

最终结果与Java解决方案相同,只是我返回了轴心点,Java解决方案在轴心点返回了值。

关于第二个问题,为什么OP被低估? OP没有被投票。 OP的问题是。

好的,为什么OP的问题被低估?

我能想出的最佳答案是这个问题是重复的。 OP发现副本并没有正确实现它。这限制了这个问题对未来的问题的有用性。

答案 3 :(得分:0)

试试这个:

#include <iostream>
#include <vector>
#include <stdio.h>
#include <algorithm>
#include <math.h>

using namespace std;
int findMinInRotatedSortedVec(vector<int>v, int start, int end)
{
    if (start == end) return v[start];
    int mid = (end-start)/2+start;
    if (v[start] == v[mid]) {
        if (v[mid] == v[end]) {
            int min1 = findMinInRotatedSortedVec(v, start, mid);
            int min2 = findMinInRotatedSortedVec(v, mid+1, end);
            return (min1 > min2) ? min2 : min1;
        }
        if (v[mid] < v[end]) return v[start];
    }
    if ((v[start] < v[mid]) && (v[mid] <= v[end])) return v[start];
    if (v[start] > v[mid]) return findMinInRotatedSortedVec(v, start, mid);
    return findMinInRotatedSortedVec(v, mid+1, end);
}

int main() {
    vector<int> v (70);
    std::iota(v.begin(), v.end(), 31);
    rotate(v.begin(),v.begin()+43,v.end());
    cout <<"Min is " << findMinInRotatedSortedVec(v,0,v.size()-1) <<endl;
  return 0;
}

答案 4 :(得分:-1)

<algorithm>提供了一种内置方法,用于查找名为min_element的最小元素(除了max和minmax之外)。

你可以像这样使用它:

std::vector<int>::iterator result = std::min_element(std::begin(a), std::end(a));
std::cout << "Min is " << std::distance(std::begin(a), result);

并且根本不必使用你的bs()方法。