詳細描述
二分查詢的搜尋過程從陣列的中間元素開始,如果中間元素正好是要查詢的元素,則搜尋過程結束;如果某一特定元素大於或者小於中間元素,則在陣列大於或小於中間元素的那一半中查詢,而且跟開始一樣從中間元素開始比較。如果在某一步驟陣列為空,則代表找不到。這種搜尋演算法每一次比較都使搜尋範圍縮小一半。
二分查詢詳細的執行步驟如下:
- 在有序表中,取中間記錄作為比較物件;
- 若給定值與中間記錄的關鍵字相等,則查詢成功;
- 若給定值小於中間記錄的關鍵字,則在中間記錄的左半區繼續查詢;
- 若給定值大於中間記錄的關鍵字,則在中間記錄的右半區繼續查詢;
- 不斷重複步驟 1~4,直到查詢成功,或所有查詢區域無記錄,查詢失敗為止。
演算法圖解
問題解疑
二分查詢演算法有哪些侷限性?
二分查詢演算法需要按照下標隨機訪問。所以更適合陣列結構,而不適合連結串列結構,陣列按照下標隨機訪問資料的時間複雜度是 \(O(1)\),而連結串列隨機訪問的時間複雜度是 \(O(n)\)。
二分查詢針對的是有序數。二分查詢只能用在插入、刪除操作不頻繁,一次排序多次查詢的場景中,針對動態變化的資料集合,二分查詢將不再適用。
資料量太小不適合二分查詢。在一個大小為 10 的陣列中查詢一個元素,不管用二分查詢還是順序遍歷,查詢速度都差不多,只有資料量比較大的時候,二分查詢的優勢才會比較明顯。
資料量太大也不適合二分查詢。二分查詢是作用在陣列這種資料結構之上的,太大的資料用陣列儲存比較吃力,也就不能用二分查詢了。
二分查詢演算法有哪些變形?
- 查詢第一個值等於給定值的元素
- 查詢最後一個值等於給定值的元素
- 查詢第一個大於等於給定值的元素
- 查詢最後一個小於等於給定值的元素
程式碼實現
查詢介面
package cn.fatedeity.algorithm.search;
public interface Search {
int search(int[] numbers, int target);
}
二分查詢類
package cn.fatedeity.algorithm.search;
/**
* 二分查詢類
*/
public class BinarySearch implements Search {
private int search(int[] numbers, int target, int left, int right) {
if (left > right) {
return -1;
} else if (left == right) {
if (numbers[left] == target) {
return left;
} else {
return -1;
}
}
if (target < numbers[left] || target > numbers[right]) {
return -1;
}
int mid = (left + right) >> 1;
if (numbers[mid] > target) {
return this.search(numbers, target, left, mid - 1);
} else if (numbers[mid] < target) {
return this.search(numbers, target, mid + 1, right);
} else {
return mid;
}
}
@Override
public int search(int[] numbers, int target) {
return this.search(numbers, target, 0, numbers.length - 1);
}
}