上限和下限的基本二进制搜索之间的区别?

时间:2015-02-08 00:15:04

标签: c++ binary-search lower-bound upperbound

在文章http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=binarySearch中,作者讨论了二进制搜索。他区分了找到某些东西是真的最低值,以及某些东西是假的最高值。 被搜索的数组类似于:

false false false true true

我很好奇为什么这两个案例不同。为什么你不能找到真实的最低值,然后减去一个来找到最高值,这是假的?

Edit2:好的,所以我理解下限和下限。现在,我正在努力理解,当搜索大于或等于查询的最小整数时,为什么我们不能将if(mid>query)更改为if(mid>=query)并让它更低而不是上限。

编辑:这是文章所说的内容:

"现在我们终于找到实现二进制搜索的代码,如本节和前一节所述:

binary_search(lo, hi, p):
   while lo < hi:
      mid = lo + (hi-lo)/2
      if p(mid) == true:
         hi = mid
      else:
         lo = mid+1

   if p(lo) == false:
      complain                // p(x) is false for all x in S!

   return lo         // lo is the least x for which p(x) is true

...

如果我们想找到p(x)为假的最后一个x,我们会设计(使用与上面类似的基本原理):

binary_search(lo, hi, p):
   while lo < hi:
      mid = lo + (hi-lo+1)/2    // note: division truncates
      if p(mid) == true:
         hi = mid-1
      else:
         lo = mid

   if p(lo) == true:
      complain                // p(x) is true for all x in S!

   return lo         // lo is the greatest x for which p(x) is false

&#34;

3 个答案:

答案 0 :(得分:36)

二进制搜索的下限和上限是可以在不中断排序的情况下插入值的最低和最高位置。 (在C ++标准库中,这些边界将由引用可在其中插入值的元素的迭代器表示,但概念基本上没有更改。)

例如,采用有序范围

1 2 3 4 5 5 5 6 7 9

3的二进制搜索中,我们将

   v-- lower bound
1 2 3 4 5 5 5 6 7 9
     ^-- upper bound

5的二进制搜索中:

       v-- lower bound
1 2 3 4 5 5 5 6 7 9
             ^-- upper bound

如果元素不在范围内,则下限和上限相同。在8的二进制搜索中:

                 v-- lower bound
1 2 3 4 5 5 5 6 7 9
                 ^-- upper bound

您引用短语的文章的作者用等于&#34;小于&#34;并且&#34;大于&#34;所以在搜索5时,

       v-- lower bound
t t t t f f f f f f      <-- smaller than?
1 2 3 4 5 5 5 6 7 9
f f f f f f f t t t      <-- greater than?
             ^-- upper bound

在所有这些情况下,C ++迭代器将引用绑定后面的元素。也就是说:

  • 在搜索3时,std::lower_bound返回的迭代器将引用3,而来自std::upper_bound的迭代器将引用4
  • 在搜索5时,std::lower_bound返回的迭代器将引用第一个5,而来自std::upper_bound的迭代器将引用6 < / LI>
  • 在搜索8时,两者都会引用9

这是因为插入的C ++标准库中的约定是传递一个迭代器,该迭代器引用应该插入新元素的元素。例如,在

之后
std::vector<int> vec { 1, 3, 4, 5, 5, 5, 6, 7, 9 };
vec.insert(vec.begin() + 1, 2);

vec将包含1, 2, 3, 4, 5, 5, 5, 6, 7, 9std::lower_boundstd::upper_bound遵循此约定,以便

vec.insert(std::lower_bound(vec.begin(), vec.end(), 5), 5);
vec.insert(std::upper_bound(vec.begin(), vec.end(), 8), 8);

按照需要工作,并将vec排序。

更一般地说,这是在C ++标准库中指定范围的方式的表达式。范围的起始迭代器引用范围的第一个元素(如果有),而结束迭代器引用范围末尾后面的元素(如果有)。查看它的另一种方法是std::lower_boundstd::upper_bound返回的迭代器跨越搜索范围中与搜索元素等效的元素范围。

如果元素不在范围内,则此范围为空,以便lower_boundupper_bound返回相同的迭代器,否则lower_bound返回引用第一个元素的迭代器搜索范围等于搜索值,而upper_bound返回一个迭代器,指向直接位于最后一个元素后面的元素(如果有)。

答案 1 :(得分:1)

如果数组总是

false … true …

然后,除非您在index 0找到true,否则您找到的索引之前的索引将始终为false。如上所述,另一个边界情况是,如果找不到true。然后,最高的false将是数组的最后一部分。

答案 2 :(得分:0)

如果没有true或没有false值,这两种算法的情况明显不同,这在代码片段中实际上非常明显:如果找到最低值的话值为true并从此位置减去1以找到产生false的最高值,因为没有这样的对象,会产生不正确的结果。由于算法简单地针对处理直接定位适当元素的不同元素而不是具有特殊情况,因此也避免了必须处理特殊情况,减少了代码量。由于特殊情况代码往往只针对每个算法调用执行一次,因此它可能比避免特殊情况稍差。这是值得衡量的事情。

请注意,尽管标记了C ++,但代码示例并不是C ++。因此,它不是惯用的C ++。 C ++中用于实现lower_bound()upper_bound()之类的典型方法是使用适当的迭代器。这些算法不会抱怨&#34;如果没有合适的元素,因为它们只是为迭代器生成一个合适的位置,即std::lower_bound()的开头的迭代器和std::upper_bound()的过去的迭代器。