2015-02-08 85 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