二进制搜索查找排序数组中的最低和最大元素而不是给定值?

时间:2015-10-31 18:51:51

标签: c++ algorithm binary-search

所以,我试图实现二进制搜索算法(尽可能通用,可以适应不同的情况)。我在互联网上搜索过这个,有些人使用while (low != high)和一些使用,while (low <= high)和其他一些非常令人困惑的条件。

因此,我开始编写代码来查找大于给定元素的第一个元素。我想知道是否有比这更优雅的解决方案?

主要代码:

#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <utility>
#include <algorithm>
#include <stack>
#include <queue>
#include <climits>
#include <set>
#include <cstring>

using namespace std;
int arr1[2000];
int n;
int main (void)
{
    int val1,val2;
    cin>>n;
    for (int i = 0; i < n; i++)
        cin>>arr1[i];
    sort(arr1,arr1+n); 
    cout<<"Enter the value for which next greater element than this value is to be found";   
    cin>>val1;
    cout<<"Enter the value for which the first element smaller than this value is to be found";
    cin>>val2;

    int ans1 = binarysearch1(val1);
    int ans2 = binarysearch2(val2);

    cout<<ans1<<"\n"<<ans2<<"\n";
    return 0;
}
int binarysearch1(int val)
{
    while (start <= end)
    {
        int mid = start + (end-start)/2;
        if (arr[mid] <= val && arr[mid+1] > val)
            return mid+1;
        else if (arr[mid] > val)
            end = mid-1;
        else
            start = mid+1;
    }
}

类似地,为了找到小于给定元素的第一个元素,

int binarysearch2(int val)
{

    while (start <= end)
    {
        int mid = start + (end-start)/2;
        if (arr[mid] >= val && arr[mid] < val)
            return mid+1;
        else if (arr[mid] > val)
            end = mid-1;
        else
            start = mid+1;
    }
}

当我必须修改这种抽象的二进制搜索时,我经常会感到非常困惑。如果有更简单的方法,请告诉我?谢谢!

5 个答案:

答案 0 :(得分:8)

正如您所说,有不同的方法来表达二元搜索的结束条件,它完全取决于您的两个限制的含义。让我解释一下,我认为它很容易理解,它可以让你为其他案例修改它而不用太多考虑。

让我先调用两个限制 最后。我们希望找到大于某个 x 的第一个元素。以下不变量将始终保持不变:

  

过去最后的每个元素都大于 x 以及之前的每个元素   第一个小或相等(相反的情况)。

请注意,不变量没有说明区间[第一个最后]。在没有进一步了解向量的情况下,限制的唯一有效初始化是首先 = 0并且最后 =向量的最后位置。这符合条件,因为 last 之后没有任何内容,而 first 之前没有任何内容,所以一切都是正确的。

由于区间[ first last ]未知,我们必须继续进行,直到它为空,然后更新限制。

int get_first_greater(const std::vector<int>& v, int x)
{
  int first = 0, last = int(v.size()) - 1;
  while (first <= last)
  {
    int mid = (first + last) / 2;
    if (v[mid] > x)
      last = mid - 1;
    else
      first = mid + 1;
  }
  return last + 1 == v.size() ? -1 : last + 1;
}

如您所见,我们只需要两种情况,因此代码非常简单。在每次检查时,我们都会更新限制,以保持我们的不变性。

当循环结束时,使用不变量我们知道 last + 1大于 x (如果存在),所以我们只需检查我们是否仍然我们的载体内部与否。

考虑到这一点,您可以根据需要修改二进制搜索。让我们改变它以找到小于 x 的最后一个。我们改变了不变量:

  

first 之前的每个元素都小于 x 和每个元素   在 last 之后大于或等于 x

有了这个,修改代码非常简单:

int get_last_smaller(const std::vector<int>& v, int x)
{
  int first = 0, last = int(v.size()) - 1;
  while (first <= last)
  {
    int mid = (first + last) / 2;
    if (v[mid] >= x)
      last = mid - 1;
    else
      first = mid + 1;
  }
  return first - 1 < 0 ? -1 : first - 1;
}

检查我们是否仅使用与之前相同的参数更改了运算符(&gt; =而不是&gt;)和返回值。

答案 1 :(得分:1)

很难编写正确的程序。一旦程序被验证为正确,就必须很少修改并重复使用。在那一行中,鉴于您使用的是C ++而不是C,我建议您尽可能使用std C ++库。您正在寻找的两个功能都是在算法中提供给您的。

http://en.cppreference.com/w/cpp/algorithm/lower_bound http://en.cppreference.com/w/cpp/algorithm/upper_bound 为您带来魔力,并且鉴于模板的强大功能,您应该能够通过添加其他实现排序的方法来使用这些方法。

HTH。

答案 2 :(得分:0)

为了部分回答问题,可以将实际比较(使用回调函数或类似函数)分解出来,具体取决于是否要搜索大于元素的第一个元素或第一个元素更小但是,在第一个代码块中,您使用

arr[mid] <= val && arr[mid+1] > val

而在第二个区块中,索引在第二个条件中移位

if (arr[mid] >= val && arr[mid] < val)

被省略,似乎不一致。

答案 3 :(得分:0)

下面是给定排序范围的元素和值的通用算法,它返回一对迭代器,其中第一个迭代器的值是排序范围中比输入值小的第一个元素,并且第二个迭代器的值是该范围中第一个比输入值大的元素。

如果返回的迭代器对指向范围的末尾,则表示输入的范围为空。

我已经尽可能地使它成为通用的,它也处理边缘情况和重复。

template<typename BidirectionalIterator>
std::pair<BidirectionalIterator, BidirectionalIterator>
lowhigh(BidirectionalIterator first, BidirectionalIterator last,
        typename std::iterator_traits<BidirectionalIterator>::value_type const &val) {
  if(first != last) {
    auto low = std::lower_bound(first, last, val);
    if(low == last) {
      --last;
      return std::make_pair(last, last);
    } else if(low == first) {
     if(first != last - 1) {
        return std::make_pair(first, std::upper_bound(low, last - 1, val) + 1);   
      } else {
        return std::make_pair(first, first);  
      }
    } else {
      auto up = std::upper_bound(low, last, val);
      return (up == last)? std::make_pair(low - 1, up - 1) : std::make_pair(low - 1, up);
    }
  }
  return std::make_pair(last, last);
}

LIVE DEMO

答案 4 :(得分:0)

你的搜索程序有一些错误[一个被彻底打破]。我已经清理了一下,但我从你的代码开始。注意:没有保证 - 这里已经很晚了,但这应该给你一个起点。注意&#34; lo / hi&#34;是标准命名法(例如,lo是你的开始,你好是你的结局)。另请注意,hi / lo设置为mid, mid + 1或mid-1

有争议的边缘案例。 while循环必须是&#34;&lt;&#34;或&#34; mid + 1&#34;将超过数组的末尾。

int
binarysearch_larger(const int *arr,int cnt,int val)
// arr -- array to search
// cnt -- number of elements in array
// val -- desired value to be searched for
{
    int mid;
    int lo;
    int hi;
    int match;

    lo = 0;
    hi = cnt - 1;

    match = -1;

    while (lo < hi) {
        mid = (hi + lo) / 2;

        if (arr[mid] <= val) && (arr[mid+1] > val)) {
            if ((mid + 1) < cnt)
                match = mid + 1;
            break;
        }

        if (arr[mid] > val)
            hi = mid;
        else
            lo = mid;
    }

    return match;
}

int
binarysearch_smaller(const int *arr,int cnt,int val)
// arr -- array to search
// cnt -- number of elements in array
// val -- desired value to be searched for
{
    int mid;
    int lo;
    int hi;
    int match;

    lo = 0;
    hi = cnt - 1;

    match = -1;

    while (lo < hi) {
        mid = (hi + lo) / 2;

        if (arr[mid] <= val) && (arr[mid+1] > val)) {
            match = mid;
            break;
        }

        if (arr[mid] > val)
            hi = mid;
        else
            lo = mid;
    }

    // the condition here could be "<=" or "<" as you prefer
    if ((match < 0) && (arr[cnt - 1] <= val))
        match = cnt - 1;

    return match;
}