[算法总结] 二分查找
本文首發于我的個人博客:尾尾部落
二分查找法作為一種常見的查找方法,將原本是線性時間提升到了對數時間范圍,大大縮短了搜索時間,但它有一個前提,就是必須在有序數據中進行查找。
二分查找很好寫,卻很難寫對,據統計只有10%的程序員可以寫出沒有bug的的二分查找代碼。出錯原因主要集中在判定條件和邊界值的選擇上,很容易就會導致越界或者死循環的情況。
下面對二分查找及其變形進行總結:
1. 最基本的二分查找
public int binarySearch(int[] A, int target, int n){int low = 0, high = n, mid;while(low <= high){mid = low + (high - low) / 2;if(A[mid] == target){return mid;}else if(A[mid] > target){high = mid - 1;}else{low = mid + 1;}}return -1; }其中,有幾個要注意的點:
leetcode參考:Search Insert Position
2. 查找目標值區域的左邊界/查找與目標值相等的第一個位置/查找第一個不小于目標值數的位置
A = [1,3,3,5, 7 ,7,7,7,8,14,14]
target = 7
return 4
3. 查找目標值區域的右邊界/查找與目標值相等的最后一個位置/查找最后一個不大于目標值數的位置
A = [1,3,3,5,7,7,7, 7 ,8,14,14]
target = 7
return 7
此題以可變形為查找第一個大于目標值的數/查找比目標值大但是最接近目標值的數,我們已經找到了最后一個不大于目標值的數,那么再往后進一位,返回high + 1,就是第一個大于目標值的數。
劍指offer:數字在排序數組中出現的次數
4. 查找最后一個小于目標值的數/查找比目標值小但是最接近目標值的數
此題以可由第 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
5. 查找第一個大于目標值的數/查找比目標值大但是最接近目標值的數
此題以可由第 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
6. 旋轉數組返回最小元素
6.1 查找旋轉數組的最小元素(假設不存在重復數字)
LeetCode: Find Minimum in Rotated Sorted Array
Input: [3,4,5,1,2]
Output: 1
注意這里和之前的二分查找的幾點區別:
- 如果nums[mid] > nums[right],說明前半部分是有序的,最小值在后半部分,令left = mid + 1;
- 如果nums[mid] <= num[right],說明最小值在前半部分,令right = mid。
最后,left會指向最小值元素所在的位置。
6.2 查找旋轉數組的最小元素(存在重復項)
LeetCode: Find Minimum in Rotated Sorted Array II
劍指offer:旋轉數組的最小數字
Input: [2,2,2,0,1]
Output: 0
和之前不存在重復項的差別是:當nums[mid] == nums[right]時,我們不能確定最小值在 mid的左邊還是右邊,所以我們就讓右邊界減一。
7. 在旋轉排序數組中搜索
7.1 不考慮重復項
LeetCode: Search in Rotated Sorted Array
法一:
- 先利用方法 6.1 查找數組中的最小元素,即確定分界點的位置
- 把旋轉的數組當成偏移,用(offset + mid) % len來求真實的 mid 的位置。
- 然后用二分查找來定位目標值
法二:其實沒有必要找到旋轉數組的分界點,對于搜索左側還是右側我們是可以根據mid跟high的元素大小來判定出來的,直接根據target的值做二分搜索就可以了。
public int search(int[] nums, int target) {int len = nums.length;if(len == 0)return -1;int left = 0, right = len - 1, mid;while(left <= right){mid = left + (right - left) / 2;if(nums[mid] == target)return mid;else if(nums[left] <= nums[mid]){if(target < nums[mid] && target >= nums[left])right = mid - 1;elseleft = mid + 1;}else if(nums[mid] <= nums[right]){if(target > nums[mid] && target <= nums[right])left = mid + 1;elseright = mid - 1;}}return -1; }7.2 存在重復項
LeetCode: Search in Rotated Sorted Array II
public boolean search(int[] nums, int target) {int len = nums.length;if(len == 0)return false;int left = 0, right = len - 1, mid;while(left <= right){mid = left + (right - left) / 2;if(nums[mid] == target)return true;else if(nums[mid] > nums[right]){if(target < nums[mid] && target >= nums[left])right = mid;elseleft = mid + 1;}else if(nums[mid] < nums[right]){if(target > nums[mid] && target <= nums[right])left = mid + 1;elseright = mid;}else{right --;} }return false; }8. 二維數組中的查找
劍指offer:二維數組中的查找
二維數組是有序的,從右上角來看,向左數字遞減,向下數字遞增。因此可以利用二分查找的思想,從右上角出發:
- 當要查找數字比右上角數字大時,下移;
- 當要查找數字比右上角數字小時,左移;
總結
以上是生活随笔為你收集整理的[算法总结] 二分查找的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 跟着太白老师学python 09day
- 下一篇: 感觉要火!妹子实地采访网易猪厂程序员七夕