前言:
1、二分查找法又叫折半查找法,從名字來看,能大概明白它的算法邏輯
2、二分查找法必須滿足被搜索的集合必須是有序的
3、待查找的元素是在集合中真實存在的
算法:
1、二分查找法每次搜索都是按照搜索區域中間位置的元素進行定位,首次搜索時,開始位置爲0,結束位置爲集合長度-1,中間位置=(開始位置+結束位置)/2
2、如果中間位置的元素正好與待查找的元素相同,則直接返回結果
3、如果中間位置的元素值大於待查找的元素值,則表示待查找元素在集合的左半區域,那麼搜索範圍將會縮小到原來的一半,右半邊的區域將會被捨棄掉,搜索的結束位置=中間位置-1,開始位置不變
4、如果中間位置的元素值小於帶查找的元素值,則表示待查找元素在集合的右半區域,那麼搜索範圍將會縮小到原來的一半,左半區域將會被捨棄掉,搜索的開始位置=中間位置+1,結束位置不變
例如:
1、有序數組arr={1,3,4,5,7,9,10,15,18,30,50,100}
2、有序數組arr總長度:12
3、數組首個元素位置:0
4、數組中間元素位置:5
5、數組末尾元素位置:11
第一次查找:
開始位置:0
結束位置:11
中間位置:5
中間位置的值:9
比較中間位置的值和待查找元素:9 > 5,則舍掉9以後的數據,繼續搜索9之前的數據
下一次搜索範圍:{1,3,4,5,7}
第二次查找:
開始位置:0
結束位置:第一次的中間位置-1 = 5-1 = 4
中間位置:(開始位置 + 結束位置)/2 = (0 + 4) / 2 = 2
中間位置的值:4
比較中間位置的值和待查找元素:4 < 5,則捨棄掉4之前的數據,繼續搜索4之後的數據
下一次搜索範圍:{5,7}
第三次查找:
開始位置:第二次的中間位置 + 1 = 2 + 1 = 3
結束位置:4
中間位置:(開始位置+結束位置)/2
中間位置的值:5
比較中間位置的值和待查找元素:5 = 5,則結束查詢
注意:
1、每次搜索之後的集合是沒有變的,只是搜索範圍在隨着開始位置和結束位置的變化而縮小
2、最高效的時候就是搜索一次的時候正好在中間的位置,這個時候只會搜索一次
3、最低效的時候就是將搜索範圍縮小到1的時候
4、分解原理:
第1次搜索時爲n/2⁰,搜索第2次爲n/2¹,搜索第3次爲n/2²,搜索第4次爲n/2³...以此類推,到第k次的時候即爲n/2^(k-1),當分解到最後時候,只會剩下1個元素,所以n/2^(k-1)>=1,極限的情況下,n/2^(k-1)=1,則n=log2(k-1),所以二分法的時間複雜度爲O(logn)
二分查找法代碼:
/**
* 1,數組必須是順序結構的數組,如果不是順序結構的,需要先進行排序
* 2,如果數組中存在相同數字的,只會返回其中一個的位置,不會返回兩個的位置
* 3,二分查找法是在縮短數組的查找範圍,所以開始位置的結束位置是在隨時變化的
* 4,開始位置變化的情況:當中間位置的值小於待查找數字的時候,開始位置 = 中間位置 + 1(表示待查找數字在數組的右半邊區域中)
* 5,結束位置變化的情況:當中間位置的值大於待查找數字的時候,結束位置 = 中間位置 - 1(表示待查找數字在數組的左半邊區域中)
* @param arr
* @param target
* @return
*/
public static int halve(int[]arr, int target) {
// 開始位置
int s_pos = 0;
// 結束位置(即數組長度)
int e_pos = arr.length - 1;
while(s_pos <= e_pos) {
// 中間位置 = (開始位置 + 結束位置 ) / 2
int m_pos = ( s_pos + e_pos ) >>> 1; // 無符號右移1位,即/2
// 如果被查詢的數字大於中間位置的值,則表示該數在數組的前半區域,則將結束位置設置爲中間位置-1
if(target < arr[m_pos]) {
e_pos = m_pos - 1;
}
// 如果被查詢的數字大於中間位置的值,則表示該數在數組的後半區域,則將開始位置設置爲中間位置+1
else if(target > arr[m_pos]) {
s_pos = m_pos + 1;
}
// 如果找到了數字則直接返回
else {
return m_pos;
}
}
return -1; //表示沒有找到查找的數字:1,可能數字根本不在數組中;2,數組不是順序結構的
}