二分查找的定義
二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。但是,折半查找要求線性表必須採用順序存儲結構,而且表中元素按關鍵字有序排列。
算法的要求
從上面的定義我們可以知道,滿足該算法的要求必須如下兩點:
- 必須採用順序存儲結構。
- 必須按關鍵字大小有序排列。
算法的步驟
其實,二分查找也還是比較容易理解的,大概就是一分爲二,然後兩邊比較,保留有效區間,繼續一分爲二查找,直到找到或者超出區間則結束,所以二分查找的基本步驟是:
- 確定要查找的區間
- 確定要二分時的參照點
- 區間內選取二分點
- 根據二分點的值,綜合左右區間情況以及求解的目的,捨去一半無用的區間
- 繼續在有效區間重複上面的步驟
算法源碼
這裏,我主要採用遞歸和非遞歸兩種方法實現,具體如下:
首先第一種是非遞歸的算法實現,算法如下:
/**
* 二分查找算法
* @param array $arr 待查找區間
* @param int $number 查找數
* @return int 返回找到的鍵
*/
function binary_search($arr, $number) {
// 非數組或者數組爲空,直接返回-1
if (!is_array($arr) || empty($arr)) {
return -1;
}
// 初始變量值
$len = count($arr);
$lower = 0;
$high = $len - 1;
// 最低點比最高點大就退出
while ($lower <= $high) {
// 以中間點作爲參照點比較
$middle = intval(($lower + $high) / 2);
if ($arr[$middle] > $number) {
// 查找數比參照點小,捨去右邊
$high = $middle - 1;
} else if ($arr[$middle] < $number) {
// 查找數比參照點大,捨去左邊
$lower = $middle + 1;
} else {
// 查找數與參照點相等,則找到返回
return $middle;
}
}
// 未找到,返回-1
return -1;
}
然後第二種是遞歸的算法實現,算法如下:
/**
* @param array $arr 待查找區間
* @param int $number 查找數
* @param int $lower 區間最低點
* @param int $high 區間最高點
* @return int
*/
function binary_search_recursion(&$arr, $number, $lower, $high) {
// 以區間的中間點作爲參照點比較
$middle = intval(($lower + $high) / 2);
// 最低點比最高點大就退出
if ($lower > $high) {
return -1;
}
if ($number > $arr[$middle]) {
// 查找數比參照點大,捨去左邊繼續查找
return binary_search_recursion($arr, $number, $middle + 1, $high);
} elseif ($number < $arr[$middle]) {
// 查找數比參照點小,捨去右邊繼續查找
return binary_search_recursion($arr, $number, $lower, $middle - 1);
} else {
return $middle;
}
}
算法的使用
需求是在一個排列好的區間($arr)中,查找一個數($number)的所在位置,所以,調用算法查找如下:
// 待查找區間
$arr = [1, 3, 7, 9, 11, 57, 63, 99];
// 非遞歸查找66所在的位置
$find_key = binary_search($arr, 57);
// 遞歸查找66所在的位置
$find_key_r = binary_search_recursion($arr, 57, 0, count($arr));
// 輸出打印
print_r($find_key);
print_r($find_key_r);
時間複雜度分析
在有序數組中如果用暴力的算法去查找,也就是逐個遍歷比較,那麼時間複雜度是O(n);但是,用二分查找後,因爲每次可以捨去一半查找區間,所以會將時間複雜度減少到O(logn),算法更優。
最後
又到了無聊的客套話時間,老規律,有問題直接留言,有想法直接說,有錯誤直接提出來,我都會及時回覆的,謝謝。