1.二分法查找定義
二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。 它是對一組有序的數字中進行查找,傳遞相應的數據,進行比較查找到與原數據相同的數據,查找到了返回對應的數組下標,沒有找到返回-1;
優點: 比較次數少,查找速度快,平均性能好 ,佔用系統內存較少
缺點: 待查表爲有序表,插入刪除困難
適用: 不經常變動而查找頻繁的有序列表
2.二分查找的思想
生活中二分查找的思想無處不在。一個最常見的就是猜數遊戲,我隨機寫一個 0 到 99 的數,然後你來猜我寫的是什麼。猜的過程中,我會告訴你每次是猜大了還是猜小了,直到猜中爲止。假如我寫的數是 23,猜數過程如下所示。
次數 | 猜測範圍 | 中間數 | 對比大小 |
---|---|---|---|
第1次 | 0-99 | 49 | 49>23 |
第2次 | 0-48 | 24 | 24>23 |
第3次 | 0-23 | 11 | 11<23 |
第4次 | 12-23 | 17 | 17<23 |
第5次 | 18-23 | 20 | 20<23 |
第6次 | 21-23 | 22 | 22<23 |
第7次 | 23 | √ |
最多只需要 7 次就猜出來了,這個過程是很快的。同理,要查找某個數據是否在給定的數組中,我們同樣也可以利用這個思想。
二分查找針對的是一個有序的數據集合,查找思想有點類似於分治,每次都通過和中間元素進行比較,將待查找區間縮小爲之前的一半,直到找到要查找的元素或者區間縮小爲 0 爲止。
3.簡單二分查找的算法實現
kotlin
版
//第一種:
/**
* @param array 有序數組
* @param left 數組低地址下標
* @param right 數組高地址下標
* @param key 查找元素
*/
fun binarySearch(data: IntArray, left: Int, right: Int, key: Int): Int {
var low = left
var high = right
while (low <= high) {
val mid = (low + high) / 2
when {
key == data[mid] -> return mid
key > data[mid] -> low = mid + 1
else -> high = mid - 1
}
}
return -1
}
//第二種:遞歸法:
/**
* @param array 有序數組
* @param left 數組低地址下標
* @param right 數組高地址下標
* @param key 查找元素
*/
fun binarySearch(data: IntArray, left: Int, right: Int, key: Int): Int {
while (left <= right) {
val mid = (left + right) / 2
return when {
key == data[mid] -> mid
key > data[mid] -> binarySearch(data, mid + 1, right, key)// left = mid + 1
else -> binarySearch(data, left, mid - 1, key) //right = mid - 1
}
}
return -1
}
注意事項
-
循環退出條件 left <= right
-
mid = left + ((right-left) >> 1),用移位運算優化計算性能
-
left 和 right 的更新分別是 mid+1 和 mid-1
-
Java版
public static void main(String[] args) {
int srcArray[] = {3, 5, 11, 17, 21, 23, 28, 30, 32, 50, 64, 78, 81, 95, 101};
System.out.println(binSearch(srcArray, 0, srcArray.length - 1, 111));
System.out.println(binSearch(srcArray, 78));
}
/**
* 二分查找普通實現。
*
* @param srcArray 有序數組
* @param key 查找元素
* @return 不存在返回-1
*/
private static int binSearch(int srcArray[], int key) {
int mid;
int start = 0;
int end = srcArray.length - 1;
while (start <= end) {
mid = (end - start) / 2 + start;
if (key < srcArray[mid]) {
end = mid - 1;
} else if (key > srcArray[mid]) {
start = mid + 1;
} else {
return mid;
}
}
return -1;
}
/**
* 二分查找遞歸實現。
*
* @param srcArray 有序數組
* @param start 數組低地址下標
* @param end 數組高地址下標
* @param key 查找元素
* @return 查找元素不存在返回-1
*/
private static int binSearch(int srcArray[], int start, int end, int key) {
int mid = (end - start) / 2 + start;
if (srcArray[mid] == key) {
return mid;
}
if (start >= end) {
return -1;
} else if (key > srcArray[mid]) {
return binSearch(srcArray, mid + 1, end, key);
} else if (key < srcArray[mid]) {
return binSearch(srcArray, start, mid - 1, key);
}
return -1;
}
4.二分查找的應用場景
二分查 找依賴的是順序表結構,也就是數組,需要能夠按照下標隨機訪問元素。
二分查找針對的是有序數據,如果數據無序,需要先進行排序。而如果有頻繁的插入、刪除操作,則每次查找前都需要再次排序,這時候,二分查找將不再適用。
數據量太小可以直接遍歷查找,沒有必要用二分查找。但如果數據之間的比較操作非常耗時,比如數據爲長度超過 300 的字符串,則不管數據量大小,都推薦使用二分查找。
數據量太大不適合用二分查找。二分查找底層依賴數組這種數據結構,而數組爲了支持隨機訪問的特性,要求內存空間連續,對內存的要求比較苛刻。比如,我們有大小的數據,如果希望使用數組來存儲,那就需要的連續內存空間來存儲。
參考資料:
1.程傑 .大話數據結構. 北京:清華大學出版社, 2011