2015-02-08 39 views
6

在文章http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=binarySearch中,作者讨论了二分查找。他区分找到某些事情是真的最低值和假的事物的最高值。 数组被搜索看起来类似:基本二进制搜索上下限之间的区别?

假假假真真

我很好奇,为什么这两种情况是不同的。为什么你不能找到真正的最低值,然后减去一个来找出最高的值是错误的?编辑2:好的,所以我理解更低的上限。现在,我在努力理解,当搜索大于或等于查询的最小整数时,为什么我们不能将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 

...

如果我们想找到最后x其中p(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 

。“

+2

嗯,即时假设二进制搜索暗示该集合看起来像 ** false .... false true ... true **无论什么 – 2015-02-08 00:18:08

+0

该文章即时提到意味着这是这种情况,如果我们是执行二进制搜索;我相信这也是二进制搜索甚至适用于这种情况的必要条件。 – 2015-02-08 00:27:46

+0

@DietmarKühl当然,但你不能轻易检查,像 '如果(LO == 0 &&工程(LO)==真)返回false'? – 2015-02-08 00:29:30

回答

24

二进制搜索的下限和上限是可以在不破坏顺序的情况下插入值的最低和最高位置。 (在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 

到你提到的短语在相当于而言,这所有的文章的作者“小于”和“大于”以便在搜索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返回的迭代器会参考3std::upper_bound的人会参考4
  • 在寻找5,通过std::lower_bound返回的迭代器会参照第一5std::upper_bound的人会参考6
  • 在寻找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搜索值返回一个指向最后一个元素后面的元素(如果有的话)的迭代器。如果你找到最低值,其中值是true和减去1:

+0

啊,我没有考虑多个值与查询相同的情况。但是,在你的第三个例子中,当元素不在范围内时,是不是上界9和下界7? – 2015-02-08 00:32:20

+0

在C++标准库术语中,你从'lower_bound'和'upper_bound'得到的迭代器都会引用9,因为在这个元素是可以插入8的最低和最高位置之前。不过,元素真正可以插入的地方将永远是其中的一个缺口或末端。 – Wintermute 2015-02-08 00:35:30

+0

'lower_bound'和'upper_bound'按照stdlib中的通用迭代器约定行事 - 对于'vector :: insert'来说是一样的,在传递'vec.begin()+ 1'的时候会使它插入新元素在当前第二个元素之前,以及其他类似的上下文。这样就可以将'lower_bound'和'upper_bound'的结果直接传递给这些函数,并让它们做正确的事情。 – Wintermute 2015-02-08 00:39:24

1

如果阵列将永远是

false … true … 

那么一个你会发现永远是假的,除非你在index 0找到真正的前指数。如上面我的评论所述,另一个边界案例是,如果您没有找到true。然后,最高的false将是数组的最后一部分。

+0

如果检查是否可以用简单的布尔值来处理这两个问题?例如,'if(array [0] == true || array [array.size] == false)return false'?另外,代码中的更改如何解决这个问题? – 2015-02-08 00:33:21

+0

@JoeBob这就是问题所在。如果'x'是'true'的索引,'x-1'不一定是'false'的边界。你需要说'如果x> 0 &&!array [x-1]'(第二部分可选)。 – royhowie 2015-02-08 00:34:57

0

这两种算法中,如果有任何true或没有false值从代码片段其实是相当明显的应发生什么情况明显不同从这个位置找到最高值产生false产生了不正确的结果,因为没有这样的对象。由于算法只针对处理定位适当元素的不同元素而不是特殊情况,因此也避免了必须处理特殊情况,从而减少了代码量。由于特殊情况代码往往只对每个算法调用执行一次,因此它可能会比避免特殊情况稍差。这是值得衡量的事情。

请注意,代码示例不是C++,尽管问题被标记为C++。因此它不是惯用的C++。 C++中实现类似lower_bound()upper_bound()的典型方法是使用适当的迭代器。如果没有合适的元素,这些算法就不会“投诉”,因为它们只是产生适当位置的迭代器,即迭代器为std::lower_bound()的开始和std::upper_bound()的过去末端迭代器。

+0

啊,我标记了它正是因为这个原因,C++。我不太确定lower_bound是否应该返回最小的元素,而不是查询,或最大的元素是否小于查询。另外,我并不完全明白你的意思,“因为特殊情况代码往往只对每个算法调用执行一次,所以它可能会比避免特殊情况稍差。”它会如何表现稍差?一个if语句将是两者之间的唯一区别,所以差异可以忽略不计。 – 2015-02-08 01:36:50