二分查找法主要用來解決查找的問題
1、二分查找法Binary Search
(注)對于有序數列才能使用二分查找法。
Image.png
二分查找的一個基本思想:找到數組的中間位置的元素v,將數組分成>v和<v兩部分,然后將v和要查找的數據進行一個比較,如果大于v那么就在>v的部分再次進行二分查找,否則就在<v的部分進行二分查找,直到找到對應的元素。
雖然二分查找法的基本思想是非常簡單的,但是第一個無BUG的二分查找法的實現卻經歷了幾十年才出現。下面我們來看這個二分查找法的基本代碼實現。
public static int binartSearch(int arr[], int target) {
// 在arr[l...r]之中查找target
int l = 0;
int r = arr.length - 1;
while (l <= r) {
int mid = (l+r) / 2;
if (arr[mid] == target)
return mid;
// 在arr[mid+1...r]之中繼續查找target
if (arr[mid] < target)
l = mid + 1;
else
// 在arr[l...mid-1]之中繼續查找target
r = mid - 1;
}
return -1;
}
很簡單就實現了出來,但是現在是有BUG的。請看上面加粗的那一行代碼,l和r都是一個int型,相加有可能會出現超過int取值范圍,所以第一個BUG的修改為:
int mid = l + (r - l) / 2;
就是用最小的值加上左右兩邊的一個范圍,將加法換成了減法。這樣我們就寫出了一個無BUG的二分查找法。
2、floor和ceil函數
floor和ceil是二分查找法的函數,我們在上面的基礎二分查找法中實現的二分查找雖然也可以找到相應的target元素,但是當這個元素在數組中重復出現好幾次的時候,如果我們要找到這個元素第一出現的位置就可以使用floor函數,最后一次出現的位置就可以使用ceil函數。
Image.png
另外,當我們查找的元素在數組中不存在的話,上面的基礎實現就會直接返回一個-1,但是使用floor和ceil函數返回的結果就不一樣了。比如我們要查找42這個元素,floor函數會返回最后一個41,ceil則會返回第一個43。
Image.png
因為這兩個函數比較復雜,感興趣的可以自行百度。
3、使用遞歸實現二分查找
// 使用遞歸的方式實現二分查找法
public static int binarySearch2(int arr[], int target){
return __binarySearch2(arr,0,arr.length-1,target);
}
public static int __binarySearch2(int arr[],int l,int r, int target) {
if (l > r) {
return -1;
}
int mid = (l+r)/2;
if (arr[mid] == target)
return mid;
else if (arr[mid]> target)
return __binarySearch2(arr, 0,mid-1,target);
else
return __binarySearch2(arr, mid+1, r, target);
}
使用遞歸的效率會稍微慢一些,但是邏輯上更簡單清晰。