概述
在介紹二分查詢之前,對於基於數字索引的陣列元素的查詢,我們可能第一反應都是遍歷這個陣列,直到給定陣列元素值和待查詢的值相等時,返回索引值並退出,否則一直遍歷到最後一個元素,如果還是沒有找到則返回 -1,這樣的查詢雖然是簡單粗暴了點,但是對於規模不大的資料集,也是沒什麼問題的,不過很明顯,對於 n 個元素的陣列,這種查詢的時間複雜度是 O(n),隨著資料規模的增加,效能會越來越差,設想如果資料集的長度是 40 億(約 2 的 32 次方),那麼最差的情況需要遍歷陣列 40 億次,簡直不敢想象需要花費多長時間!那有沒有效能搞好的演算法來解決這個問題呢?
在進一步探討這個問題之前,我們先來看一個生活中的例子。我們日常生活中,很多人應該有這種經歷,朋友、同學或者同事淘了個寶貝,神秘兮兮的過來讓大家猜多少錢,在約定一個價格範圍之後(比如 10-100),大家會七嘴八舌的猜起價格來:
同事A:新淘了個寶貝,猜猜多少錢?
同事B:50塊。
同事A:高了。
同事C:30塊。
同事D:40塊。
同事A:高了。
同事E:36塊。
同事A:對了。
如果我們用順序遍歷的邏輯,最差需要 91 次,才能猜到價格,現實生活中,沒人會這麼幹,我們採用上面這種邏輯,只需要 4 次就猜到價格了,快了幾十倍,而且資料量越大,優勢越明顯。基於這種思路,我們的演算法科學家提煉出了二分查詢演算法,幫助我們在給定資料集中快速定位要查詢的元素。
實現原理
所謂二分查詢,針對的是一個有序的資料集合(這點很重要),查詢思想有點類似分治思想。每次都透過跟區間的中間元素對比,將待查詢的區間縮小為之前的一半,直到找到要查詢的元素,或者區間被縮小為 0。注意到二分查詢針對的必須是已經排序過的有序陣列,否則不能使用該演算法。圖示如下:
示例程式碼
<?php
function binary_search($nums, $num)
{
return binary_search_internal($nums, $num, 0, count($nums) - 1);
}
function binary_search_internal($nums, $num, $low, $high)
{
if ($low > $high) {
return -1;
}
$mid = floor(($low + $high) / 2);
if ($num > $nums[$mid]) {
return binary_search_internal($nums, $num, $mid + 1, $high);
} elseif ($num < $nums[$mid]) {
return binary_search_internal($nums, $num, $low, $mid - 1);
} else {
return $mid;
}
}
$nums = [1, 2, 3, 4, 5, 6];
$index = binary_search($nums, 5);
print $index;
效能分析
很顯然,二分查詢的時間複雜度是 O(logn)。這是一個非常恐怖的數量級,有時候甚至比 O(1) 還要高效,比如我們要在開頭提到的 40 億個數字中查詢某一個元素,也只需要32次(2 的 32 次方是 40 億數量級),這真的是非常高效了,正因如此二分查詢線上性表結構中的應用非常廣泛。
但是使用二分查詢需要注意一個前提,那就是針對有序陣列,換言之,二分查詢適用於變動不是很頻繁的靜態序列集,如果序列集變動很頻繁,經常進行插入刪除操作,那麼就要不斷維護這個序列集的排序,這個成本也很高,因此,這種情況下就不適用二分查詢了,比如我們的資料庫查詢,增刪改查很頻繁,顯然不是透過二分查詢來進行查詢的。
本作品採用《CC 協議》,轉載必須註明作者和本文連結