二分查詢法作為一種常見的查詢方法,將原本是線性時間提公升到了對數時間範圍,大大縮短了搜尋時間,但它有乙個前提,就是必須在有序資料中進行查詢。
二分查詢很好寫,卻很難寫對,據統計只有10%的程式設計師可以寫出沒有bug的的二分查詢**。出錯原因主要集中在判定條件和邊界值的選擇上,很容易就會導致越界或者死迴圈的情況。
下面對二分查詢及其變形進行總結:
public int binarysearch(int a, int target, int n)else if(a[mid] > target)else
}return -1;
}
其中,有幾個要注意的點:
迴圈的判定條件是:low <= high
為了防止數值溢位,mid = low + (high - low)/2
當a[mid]
不等於target
時,high = mid - 1
或low = mid + 1
leetcode參考:search insert position
a = [1,3,3,5,7,7,7,7,8,14,14]target = 7
return 4
public int binarysearchlowerbound(int a, int target, int n)else
}if(low < a.length && a[low] == target)
return low;
else
return -1;
}
a = [1,3,3,5,7,7,7,7,8,14,14]target = 7
return 7
public int binarysearchupperbound(int a, int target, int n)else
}if(high >= 0 && a[high] == target)
return high;
else
return -1;
}
此題以可變形為查詢第乙個大於目標值的數/查詢比目標值大但是最接近目標值的數
,我們已經找到了最後乙個不大於目標值的數,那麼再往後進一位,返回high + 1
,就是第乙個大於目標值的數。
劍指offer:數字在排序陣列**現的次數此題以可由第 2 題變形而來,我們已經找到了目標值區域的下(左)邊界,那麼再往左退一位,即
low - 1
,就是最後乙個小於目標值的數。其實low - 1
也是退出迴圈後high
的值,因為此時high
剛好等於low - 1
,它小於low
,所以 while 迴圈結束。我們只要判斷high
是否超出邊界即可。
a = [1,3,3,5,7,7,7,7,8,14,14]target = 7
return 3
int low = 0, high = n, mid;
while(low <= high)else
}return high < 0 ? -1 : high;
此題以可由第 3 題變形而來,我們已經找到了目標值區域的上(右)邊界,那麼再往右進一位,即high + 1
,就是第乙個大於目標值的數。其實high + 1
也是退出迴圈後low
的值,因為此時low
剛好等於high + 1
,它大於high
,所以 while 迴圈結束。我們只要判斷low
是否超出邊界即可。
a = [1,3,3,5,7,7,7,7,8,14,14]target = 7
return 8
int low = 0, high = n, mid;
while(low <= high)else
}return low > n ? -1 : low;
6.1 查詢旋轉陣列的最小元素(假設不存在重複數字)
leetcode: find minimum in rotated sorted arrayinput: [3,4,5,1,2]
output: 1
public int findmin(int nums)
}return nums[left];
}
注意這裡和之前的二分查詢的幾點區別:
迴圈判定條件為left < right
,沒有等於號
迴圈中,通過比較nums[left]與num[mid]的值來判斷mid所在的位置:
最後,left會指向最小值元素所在的位置。
6.2 查詢旋轉陣列的最小元素(存在重複項)
leetcode: find minimum in rotated sorted array ii劍指offer:旋轉陣列的最小數字
input: [2,2,2,0,1]
output: 0
public int findmin(int nums)
return nums[left];
}
和之前不存在重複項的差別是:當nums[mid] == nums[right]
時,我們不能確定最小值在mid
的左邊還是右邊,所以我們就讓右邊界減一。
7.1 不考慮重複項
leetcode: search in rotated sorted array法一:
public int search(int nums, int target)
int offset = left;
left = 0;
right = len - 1;
while(left <= right)
return -1;
}
法二:其實沒有必要找到旋轉陣列的分界點,對於搜尋左側還是右側我們是可以根據mid跟high的元素大小來判定出來的,直接根據target的值做二分搜尋就可以了。
public int search(int nums, int target) else if(nums[mid] <= nums[right])
}return -1;
}
7.2 存在重複項
leetcode: search in rotated sorted array ii
public boolean search(int nums, int target) else if(nums[mid] < nums[right])else
}return false;
}
劍指offer:二維陣列中的查詢二維陣列是有序的,從右上角來看,向左數字遞減,向下數字遞增。因此可以利用二分查詢的思想,從右上角出發:
迭代二分查詢二分查詢
在寫這篇文章之前,已經寫過了幾篇關於改迭代二分查詢主題的文章,想要了解的朋友可以去翻一下之前的文章 bentley在他的著作 writing correct programs 中寫道,90 的計算機專家不能在2小時內寫出完整確正的二分搜尋演算法。難怪有人說,二分查詢道理單簡,甚至小學生都能明確。不過...
1128 二分 二分查詢
時間限制 10000ms 單點時限 1000ms 記憶體限制 256mb 描述nettle最近在玩 艦 因此nettle收集了很多很多的船 這裡我們假設nettle氪了很多金,開了無數個船位 去除掉重複的船之後,還剩下n 1 n 1,000,000 種不同的船。每一艘船有乙個稀有值,任意兩艘船的稀有...
二分查詢及變種二分查詢
二分查詢也稱折半查詢 binary search 它的查詢效率很好。二分查詢有乙個要求是必須採用順序儲存結構,而且表種的元素是有序的。只有滿足這個條件我們才能使用二分查詢。查詢條件 查詢區域的左邊界,小於等於查詢區域的右邊界 查詢過程 1.迴圈條件 查詢條件 2.計算序列中間下標位置 3.如果待查詢...