如何进行二进制搜索间隔

时间:2016-10-02 19:07:02

标签: c++ search binary-search

让我说我有一系列间隔对。 v = {(1,4),(5,10),(11,13),(14,25)}

现在让我们说我的号码是20,我想分开那对是20,或者我想删除这对(14,25)并添加新的两对 - (14,20)和(21,25)。如何在c ++中执行此操作,或者只能如何搜索这些间隔并查找数字20的区间。

我的想法是创建对的向量,我将保持我的间隔,但我不知道如何分割间隔,我必须在日志时间,所以我必须使用二元搜索。

3 个答案:

答案 0 :(得分:0)

在这种情况下搜索使用

std::upper_bound() or std::lower_bound()

获取要更改并继续的间隔索引。我在codeforces上解决了一个类似的问题,但我只是想告诉索引数字落在哪里。

答案 1 :(得分:0)

以下是@karan建议的使用std::lower_bound的可能解决方案;如果使用向量,搜索是在日志时间而不是插入。

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

int main() {
  std::vector<std::pair<int, int> > input = { {1,4}, {5,10}, {11,13}, {14,25} };
  struct LessInterval {
    bool operator()(const std::pair<int, int>& fst, const std::pair<int, int>& snd)
      {  return fst.second < snd.first; }
  };
  int key = 20;

  // lower_bound uses a dichotomic search in log_2(N)
  auto locateIter = std::lower_bound(input.begin(), input.end(),
      std::make_pair(key, key), LessInterval());
  if (locateIter != input.end()) {
    // modifications on vector can be linear in N; you can consider std::set instead 
    if (locateIter->first <= key && key <= locateIter->second) {
      if (key == locateIter->first) {
        if (locateIter->second == key)
          input.erase(locateIter);
        else
          locateIter->first = key+1;
      }
      else if (key == locateIter->second)
        locateIter->second = key-1;
      else {
        int second = locateIter->second;
        locateIter->second = key-1;
        input.insert(locateIter+1, std::make_pair(key+1, second));
      };
    }
  };

  std::cout << '{';
  bool isFirst = true;
  for(const auto& interval : input) {
    if (!isFirst)
      std::cout << ", ";
    std::cout << '(' << interval.first << ',' << interval.second << ')';
    isFirst = false;
  };
  std::cout << std::endl;
  return 0;
}

答案 2 :(得分:0)

插入排序的数组数据结构不能比O(N)快,所以你需要其他东西,比如树,即std::setstd::map。这些类具有内置的排序和搜索功能。他们的成员lower_boundupper_bound适合查找间隔。

有几种方法可以表示树中的间隔。您可以定义自己的class interval,也可以使用std::pair。最简单的方法是使用std::map<int, int>,它是由第一个成员排序的std::pair<int, int>树。我建议一个查找表,其中间隔上限是键,间隔下限是值。

map::upper_bound接受一个数字并返回(迭代器)第一个大于它的条目。这适用于半开间隔:不是记录从14到25(包括14和25)的闭合间隔[14,25],而是使用半开区间[14,26],其从14到26不包括。当您搜索25时,upper_bound将看到26并返回正确的条目。

intervals[ 26 ] = 14; // Insert interval [14, 25]

intervals[ 21 ] = 14; // Insert interval [14, 20]
intervals[ 26 ] = 21; // Cut [14, 25] down to [21, 25]

请注意,此策略不会检查给定数字是否大于范围的下限,因此您在调用upper_bound后手动检查。

typedef std::map< int, int > interval_set;
typedef std::pair< const int, int > interval; // {one greater than top, bottom}

bool split_interval( interval_set & s, int p ) {
    interval_set::iterator it = s.upper_bound( p );
    if ( p < it->second ) return false; // Interval doesn't exist. Bail out.

    s.insert( interval{ p + 1, it->second } ); // Insert a new lower-part interval.
    it->second = p; // Cut the lower part from the existing interval.
    return true;
}

http://coliru.stacked-crooked.com/a/bd30949018cb4a00