單比較每迭代二進制搜索的要點是什麼?你能解釋它是如何工作的嗎?如何更好地理解每比較一次比較二進制搜索?
回答
二元搜索有兩個原因,每次迭代一次比較。性能不太重要的是 。提前檢測完全匹配,每次迭代使用兩個 比較節省循環的平均一次迭代,而 (假設比較涉及重要工作),每次迭代比較一次 ,幾乎減半了每次迭代完成的工作。
二進制搜索一個整數數組,它可能無論如何沒有什麼區別 。即使是相當昂貴的比較,漸近地表現也是一樣的,在大多數情況下,可能不會有價值追求的是一半而不是一個負值。此外,昂貴的比較常常被編碼爲返回負數,零或正的<
,==
或>
的函數,因此無論如何你都可以得到兩者的比較。
每次迭代比較一次進行二分搜索的重要原因是 ,因爲您可以獲得比僅僅一些等號匹配更有用的結果。主要 搜索你可以做的是...
- 一鍵>目標
- 一鍵> =目標
- 一鍵==目標
- 最後關鍵<目標
- 最後關鍵< =目標
- 最後一個關鍵==目標
這些都減少到相同的基本算法。理解這個足夠好的 ,你可以很容易地編寫所有的變體並不困難,但我沒有真正看到一個很好的解釋 - 只有僞代碼和數學證明。這是我試圖解釋的 。
有一些遊戲的想法是儘可能接近目標 沒有超調。將其改爲「下衝」,這就是「Find First>」所做的。考慮搜索過程中某個階段的範圍...
| lower bound | goal | upper bound
+-----------------+-------------------------+--------------
| Illegal | better worse |
+-----------------+-------------------------+--------------
仍然需要搜索當前上限和下限之間的範圍。 我們的目標是(通常)在某處,但我們還不知道在哪裏。 關於上限以上項目的一個有趣的觀點是,他們在 合法的意義上說他們比目標更重要。我們可以說,當前上限以上的項目 是我們迄今爲止最好的解決方案。我們甚至可以在一開始就說這個 ,儘管在那個位置可能沒有物品 - 從 的意義上說,如果沒有有效的範圍內解決方案,那麼不是 的最佳解決方案就是過去的上限。
在每次迭代時,我們選擇一個項目來比較上下限。 對於二分查找,這是一個舍入的中途項目。對於二叉樹搜索,它是由樹的結構決定的 。原理是一樣的。
由於我們正在搜索的項目大於我們的目標,我們使用Item [testpos] > goal
比較測試項目 。如果結果是錯誤的,我們會超出(或低於 )我們的目標,所以我們保留現有的最佳解決方案,並調整我們的下限。如果結果是真的,我們已經找到了一個新的最好的解決方案,所以我們調整上限以反映這一點。無論採用哪種方式,我們都不希望再次比較該測試項目,因此我們調整我們的 範圍以消除(僅僅是)從測試項目中搜索的範圍。因爲這個不小心,通常會導致無限循環。
通常,使用半開範圍 - 包含下限和獨佔上限。使用此係統,上限索引處的項目不在 搜索範圍內(至少不是現在),但它是是最好的解決方案。 移動下限時,將其移動到testpos+1
(以排除您僅從 測試的項目)。當你移動上限時,你將它移動到 testpos(無論如何,上限是獨佔的)。
if (item[testpos] > goal)
{
// new best-so-far
upperbound = testpos;
}
else
{
lowerbound = testpos + 1;
}
在上限和下限之間的範圍內是空的(採用半開, 當兩個具有相同指數),你的結果是你最近最迄今爲止在 液,只需在你上面的上限(即在半開放的 的上限索引處)。
,所以該完整的算法是...
while (upperbound > lowerbound)
{
testpos = lowerbound + ((upperbound-lowerbound)/2);
if (item[testpos] > goal)
{
// new best-so-far
upperbound = testpos;
}
else
{
lowerbound = testpos + 1;
}
}
若要更改first key > goal
到first key >= goal
,你從字面上切換 比較運營商在if
線。 相對運算符和目標可以由單個參數替代 - 一個謂詞函數,如果(且僅當)其參數位於目標的大於一側時返回true。
這給你「第一>」和「第一> =」。要獲得「first ==」,請使用「first> =」,並在循環退出後添加相等性檢查。
對於「last <」等,其原理與上述相同,但範圍爲 反映。這只是意味着你調整了邊界調整(但不包括 評論)以及更改運算符。但在此之前,請考慮以下內容...
a > b == !(a <= b)
a >= b == !(a < b)
另外...
- 位置(最後的鍵<目標)=位置(第一密鑰> =目標) - 1個
- 位置(最後的鍵< =目標)=位置(第一密鑰>目標) - 1
當我們在搜索過程中移動我們的界限時,雙方都會朝目標移動,直到他們相遇。還有剛剛低於下限特殊的項目,就像有隻超過上限時...
while (upperbound > lowerbound)
{
testpos = lowerbound + ((upperbound-lowerbound)/2);
if (item[testpos] > goal)
{
// new best-so-far for first key > goal at [upperbound]
upperbound = testpos;
}
else
{
// new best-so-far for last key <= goal at [lowerbound - 1]
lowerbound = testpos + 1;
}
}
因此,在某種程度上,我們同時運行兩個互補的搜索。當上限和下限相遇時,我們在該單一邊界的每一邊都有一個有用的搜索結果。
對於所有情況,原始「想象」出界 最佳到此位置是您的最終結果(在搜索範圍內沒有匹配)。在做最後的==
檢查 第一個==和最後一個==情況之前,需要檢查它。這也可能是有用的行爲 - 例如如果 您正在尋找插入目標項目的位置,則在現有項目的 末尾添加該位置是正確的,如果所有現有項目 都小於您的目標項目。
一對夫婦對testpos的選擇筆記...
testpos = lowerbound + ((upperbound-lowerbound)/2);
首先,這將不會溢出,不像比較明顯((lowerbound + upperbound)/2)
。它也適用於指針以及整數 索引。
其次,該部門被假定爲圓形。下降非負數 是確定的(所有你可以肯定在C),因爲差異始終是非負數 無論如何。
這是一個方面,如果您使用非半開放式 範圍,可能需要謹慎,但是 - 確保測試位置在搜索範圍內,而不僅僅是在外面(在已經找到的最佳範圍之一上)目前爲止的職位)。
最後,在二叉樹搜索範圍的移動是隱性和 選擇testpos
內置於樹的結構(可能是 不平衡),但同樣的原則也適用於搜索是什麼這樣做。在這個 的情況下,我們選擇我們的子節點縮小隱式範圍。對於第一場比賽 的情況,要麼我們找到了一個新的較小的最佳匹配(找到較小的孩子,希望找到一個更小,更好的孩子),或者我們已經超過(希望恢復的更高的孩子)。再次,可以通過切換比較運算符來處理四種主要情況。
順便說一句 - 有更多可能的操作符用於該模板參數。考慮按年份和月份排序的數組。也許你想找到特定年份的第一個項目。要做到這一點,編寫一個比較年份並忽略月份的比較函數 - 如果年份相同,則目標相當於平等,但目標值可能與密鑰的類型不同,該密鑰甚至沒有月份值比較。我認爲這是一個「部分關鍵比較」,並將其插入到二進制搜索模板中,並獲得我認爲的「部分關鍵搜索」。
編輯以下段落用於說「1999年12月31日等於2000年2月1日」。除非整個範圍也被認爲是平等的,否則這是行不通的。重點是開始和結束日期日期的所有三個部分都不相同,因此您不需要處理「部分」鍵,但被認爲等同於搜索的鍵必須在容器中形成一個連續的塊,這通常意味着在可能的鍵的有序集合中連續的塊。
它不完全是「部分」鍵。您的自定義比較可能會考慮到1999年12月31日等於2000年1月1日,但所有其他日期不同。關鍵在於自定義比較必須與排序的原始關鍵字一致,但考慮到所有不同的值可能不會太挑剔 - 它可以將一系列關鍵字視爲「等價類」。
關於我真的應該包括前界一個額外的音符,但也許我還沒有想過這個問題在當時這種方式。
思考邊界的一種方法是它們根本不是項目。一個綁定的兩個項目之間的邊界線,所以你可以很容易地爲編號的邊界線,你可以編號的項目...
| | | | | | | | |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ |
| |0| | |1| | |2| | |3| | |4| | |5| | |6| | |7| |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ |
| | | | | | | | |
0 1 2 3 4 5 6 7 8
明顯界限的編號與項目的編號。只要你從左到右編號,並用相同的方式爲你的項目編號(在這種情況下從零開始),結果與常見的半開公約相同。
將有可能選擇一箇中間範圍將範圍精確地平分爲兩個,但這不是二進制搜索的功能。對於二進制搜索,您可以選擇一個要測試的項目 - 不是綁定。該項目將在此迭代中進行測試,不得再次測試,因此它將從兩個子範圍中排除。
| | | | | | | | |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ |
| |0| | |1| | |2| | |3| | |4| | |5| | |6| | |7| |
| +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ | +-+ |
| | | | | | | | |
0 1 2 3 4 5 6 7 8
^
|<-------------------|------------->|
|
|<--------------->| | |<--------->|
low range i hi range
所以在算法testpos
和testpos+1
是翻譯項目索引,約束指數的兩種情況。當然,如果兩個邊界相等,則該範圍內沒有項目可供選擇,因此循環無法繼續,唯一可能的結果就是該邊界值。
上面顯示的範圍只是仍然需要搜索的範圍 - 我們打算在久經考驗的和已證明的更高範圍之間關閉的差距。
在此模型中,二分查找搜索兩個有序種類值之間的邊界 - 分類爲「較低」和歸類爲「較高」的那些邊界。謂詞測試將一個項目分類。沒有「平等」等級 - 等於鍵值是較高等級(對於x[i] >= key
)或較低等級(對於x[i] > key
)的一部分。
- 1. 二進制搜索比較器
- 2. 二進制搜索和eps比較
- 3. 二進制搜索比較數
- 4. 二進制比較
- 5. 二進制搜索的比較次數的復發關係
- 6. 如何使用二進制搜索比較x509certificates
- 7. 二進制數比較
- 8. 在C++上的二進制搜索與比較
- 9. 二進制搜索優化的比較數量方面
- 10. 我可以比二進制搜索做得更好嗎?
- 11. 比較二進制整數ruby
- 12. MySQL:高效的二進制值比較
- 13. 比較兩個二進制向量
- 14. 圖靈機比較二進制
- 15. C比較二進制數與xor
- 16. 比較兩個二進制文件
- 17. 二進制字符串比較
- 18. 構建二叉搜索樹時的比較次數
- 19. 什麼比較方法比較好?
- 20. C#使用比較器進行二進制搜索的對象列表
- 21. 兩次比較
- 22. 比較兩次
- 23. 爪哇 - 比較線性搜索和二分搜索的性能
- 24. MySQL比較二進制排序與二進制字符串
- 25. iOS - 如何比較兩次?
- 26. 如何計算比較器/可比較器中的比較次數?
- 27. 如何分割一串二進制數來比較每個數字?
- 28. 如何在Python中進行安全的二進制比較?
- 29. 理解分配/比較vb.net
- 30. 理解的NSString比較
+1寫得很好的解釋,先生。 – WhozCraig 2013-02-19 05:14:55