查找算法——二分查找
- 簡單介紹:對有序數組,通過遞歸,不斷進行折中,直到其中間值等於所需要查找的數字
- 思路分析:
- 確定該數組的中間下標,mid = (left + right) / 2
- 然後將需要查找的數與中間值mid進行比對
- 比中間值大,那就再以中間值mid爲left,right爲right進行同樣的折半查找
- 比中間值小,那就再以中間值mid爲right,left爲left進行同樣的折半查找 - 如果arr[mid] == findVal,說明找到了
- 判定遞歸截至的條件
- 找到對應的值,返回後進行推出
- 沒有找到對應的數值,會滿足left>right,然後進行退出
- 代碼實現:
我的代碼:
package binarysearch;
public class Binary {
public static void main(String[] args) {
int[] arr = {1,3,45,56,789,888,999};
System.out.println(binarySearch(arr,0,arr.length - 1,4));
}
static int valIndex = - 1;
public static int binarySearch(int[] arr,int left,int right,int searchVal){
//設置中間值的索引
int mid = (left + right) / 2;
if(left < right){
if(arr[mid] == searchVal){
//如果找到對應的值,那就退出
valIndex = mid;
}else if(arr[mid] > searchVal){
//如果你要找的值小於中間值,那就說明在左半邊
binarySearch(arr,left,mid - 1,searchVal);
}else if (arr[mid] < searchVal){
//如果你要找的值大於中間值,那就說明再右半邊
binarySearch(arr,mid + 1,right,searchVal);
}else{
valIndex = - 1;
}
return valIndex;
}
}
}
問題分析:
- 每一次都折中是否能夠查遍每一個元素?
- 通過實驗發現,不能夠查找隨後一個元素。這取決去整除的特性,自動捨棄餘數。如果是相鄰的兩個數,他會下意識的取左邊那個數,而右邊的數取不到。如{1,3},取中間值,會默認是索引0,就是對應的1.永遠取不到三
- 如果在運算中沒有對中值索引進行更改,爲什麼找不到元素時會陷入死循環?
*
- 如{1,3},要尋找的是2。因爲不專門對mid進行改變,mid在僅僅剩兩個值的時候始終不變,都是0,right右邊界始終不變,都是1,所以陷如【0,1】的死循環
- 問題:不修改前,能夠對除了最後一個元素的所有數組元素進行遍歷,但是對於不在數組內的元素,會現如相鄰區間的死循環。
- 修改:既然每一次比較都是和中間值進行比較,那既然不等與中間值,那麼在下一次比較中就可以不把中間值算在內。比中間值小,那麼在下一次比較中,right = mid - 1,比中間值大,下一次比較 left = mid + 1。既可以便利最後一個元素,又可以避免陷入死循環,最後一定是left == right。
- 問題:在查找不存在的數時,會陷入死循環。最後肯定是圍繞着某一個值來回反覆旋轉。因爲無論是加一,還是減一,最終都是left和mid以及mid等於同一個數。
- 修改:添加判斷是否執行的條件,一但left和right並於0處,就會出現right = -1,left = 0,然後陷入死循環。所以前提就必須是,left <right纔可以
教程代碼:
public static int binarySearch(int[] arr,int left,int right,int searchVal){
//設置中間值的索引
int mid = (left + right) / 2;
if (right >= left){
if(arr[mid] == searchVal){
//如果找到對應的值,那就退出
return mid;
}else if(arr[mid] > searchVal){
//如果你要找的值小於中間值,那就說明在左半邊
return binarySearch(arr,left,mid - 1,searchVal);
}else if (arr[mid] < searchVal){
//如果你要找的值大於中間值,那就說明再右半邊
return binarySearch(arr,mid + 1,right,searchVal);
}
}
return -1;
}
分析總結:
- 沒有必要設置static index專門承裝索引值,直接設置返回的是遞歸函數返回的值就可以了,層層遞歸,返回的一定是迭代到最終的那個數。返回最終值
- 思考不夠有邏輯,兩數相比較,除了大於,等於,小於,還剩什麼?第四種使你變得嗎?大哥!!!
改良版——有序數組中多個相同的值
- 思路分析:找到mid的索引之後並不要立即放回,而是以mid爲中心分別向兩邊進行掃描,將所有於mid相同的值的索引加入到集合ArrayList中,然後將集合ArrayList返回
- 代碼實現:
public static List<Integer> binarySearch2(int[] arr,int left,int right,int searchVal){
//區別一下重寫和重載:重載:兩同一不同,同類同名,不同的形參.重寫:原方法的覆蓋,是子類對父類的繼承.
//這裏都不是,形參列表不同,只有返回值類型不同,不是重載,沒有繼承,不是重寫
int mid = (left + right) / 2;
if (left <= right){
if (searchVal < arr[mid]){
return binarySearch2(arr,left,mid - 1,searchVal);
}else if(searchVal > arr[mid]){
return binarySearch2(arr,mid + 1,right,searchVal);
}else{
List<Integer> list = new ArrayList<>();
int temp = mid;
while (temp >= 0 && arr[temp] == arr[mid]){
list.add(temp);
temp --;
}
temp = mid + 1;
while (temp <= arr.length - 1 && arr[temp] == arr[mid]){
list.add(temp);
temp ++;
}
return list;
}
}
return new ArrayList<>();
}
分析總結:
- 多重索引關鍵在於遍歷,因爲是有序數組,所以相同的一定在一起。對其左右進行遍歷就行了。
- 出現兩個數組複製值的情況,賦值加上索引移位兩步走