二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。但是,折半查找要求線性表必須采用順序存儲結構,而且表中元素按關鍵字有序排列。
二分查找思路非常簡單,由粗暴的遍歷查找改為了將元素排序后不斷的進行折半查找,將搜索的時間復雜度由O(N)降到了O(log2N)。
二分查找的思路與二叉查找樹的查找過程完全相同,我們將一個經過排序的數組看做一棵平衡的二叉查找樹,那么數組的中點便是樹的根節點。折半后的中點是下一層子樹的根節點,以此類推。我們通過不斷的判斷目標值與各樹根節點的值的大小來決定下一步查找根節點的左子樹還是右子樹。
在實現時,我們可以維護兩個指針left和right,指針之間的范圍便是我們的查找范圍。查找過程如下:
首先判斷邊界條件,left位置的值與right位置的值是否包含目標值,若包含則查找結束;
通過left與right的位置找到當前范圍的中點mid,即mid的值為(left+right)/2;
如果mid的值便是target的值,查找結束;
如果left與right已經是相鄰的元素,那么證明數組中沒有目標值,查找結束;
如果目標值大於mid則在mid、right間繼續查找,即將mid的值賦予left;
反之在left與mid間繼續查找,即將mid的值賦予right;
left指針與right指針的移動如下圖所示(例子為在目標數組中查找20):
大於mid則在mid、right間繼續查找,即將上次查找的mid值賦予left:
同上:
同上:
此時left與right相鄰,仍未找到目標值,即數組中沒有包含目標值。
代碼實現如下,值得注意的是,(left+right)/2有可能會造成整數溢出問題,我們可改寫為left+(right-left)/2來防止該問題,下面看代碼
/**
* @Author Nyr
* @Date 2019/11/18 20:29
* @Description 二分查找,找到target返回target的索引,未找到返回-1
*/
class MidFind {
public static void main(String[] args){
int[] nums={-5,0,1,2,3,5,7,9,10,15,20,28,50,56};
System.out.println(searchInsert(nums,56,0,nums.length-1));
}
public static final int searchInsert(int[] nums, int target,int left,int right) {
if(nums==null){
throw new NullPointerException("目標數組為空!");
}
//邊界條件
if(target>nums[right]||target<nums[left]){return -1;}
if(right==left&&nums[left]!=target){return -1;}
if(nums[left]==target){return left;}
if(nums[right]==target){return right;}
while(true){
//計算當前中點
int mid=left+(right-left)/2;
//找到目標值
if(target==nums[mid]){
return mid;
}
//未找到目標值但終止循環
if(right==left+1){
return -1;
}
if(target>nums[mid]){
left=mid;
}
if(target<nums[mid]){
right=mid;
}
}
}
}