日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[算法总结] 二分查找

發(fā)布時(shí)間:2023/12/20 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [算法总结] 二分查找 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文首發(fā)于我的個(gè)人博客:尾尾部落

二分查找法作為一種常見的查找方法,將原本是線性時(shí)間提升到了對(duì)數(shù)時(shí)間范圍,大大縮短了搜索時(shí)間,但它有一個(gè)前提,就是必須在有序數(shù)據(jù)中進(jìn)行查找。

二分查找很好寫,卻很難寫對(duì),據(jù)統(tǒng)計(jì)只有10%的程序員可以寫出沒有bug的的二分查找代碼。出錯(cuò)原因主要集中在判定條件和邊界值的選擇上,很容易就會(huì)導(dǎo)致越界或者死循環(huán)的情況。

下面對(duì)二分查找及其變形進(jìn)行總結(jié):

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; }

其中,有幾個(gè)要注意的點(diǎn):

  • 循環(huán)的判定條件是:low <= high
  • 為了防止數(shù)值溢出,mid = low + (high - low)/2
  • 當(dāng) A[mid]不等于target時(shí),high = mid - 1或low = mid + 1
  • leetcode參考:Search Insert Position

    2. 查找目標(biāo)值區(qū)域的左邊界/查找與目標(biāo)值相等的第一個(gè)位置/查找第一個(gè)不小于目標(biāo)值數(shù)的位置

    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){int low = 0, high = n, mid;while(low <= high){mid = low + (high - low) / 2;if(target <= A[mid]){high = mid - 1;}else{low = mid + 1;}}if(low < A.length && A[low] == target)return low;elsereturn -1; }

    3. 查找目標(biāo)值區(qū)域的右邊界/查找與目標(biāo)值相等的最后一個(gè)位置/查找最后一個(gè)不大于目標(biāo)值數(shù)的位置

    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){int low = 0, high = n, mid;while(low <= high){mid = low + (high - low) / 2;if(target >= A[mid]){low = mid + 1;}else{high = mid - 1;}}if(high >= 0 && A[high] == target)return high;elsereturn -1; }

    此題以可變形為查找第一個(gè)大于目標(biāo)值的數(shù)/查找比目標(biāo)值大但是最接近目標(biāo)值的數(shù),我們已經(jīng)找到了最后一個(gè)不大于目標(biāo)值的數(shù),那么再往后進(jìn)一位,返回high + 1,就是第一個(gè)大于目標(biāo)值的數(shù)。

    劍指offer:數(shù)字在排序數(shù)組中出現(xiàn)的次數(shù)

    4. 查找最后一個(gè)小于目標(biāo)值的數(shù)/查找比目標(biāo)值小但是最接近目標(biāo)值的數(shù)

    此題以可由第 2 題變形而來,我們已經(jīng)找到了目標(biāo)值區(qū)域的下(左)邊界,那么再往左退一位,即low - 1,就是最后一個(gè)小于目標(biāo)值的數(shù)。其實(shí)low - 1也是退出循環(huán)后high的值,因?yàn)榇藭r(shí) high剛好等于low - 1,它小于low,所以 while 循環(huán)結(jié)束。我們只要判斷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){mid = low + (high - low) / 2;if(target <= A[mid]){high = mid - 1;}else{low = mid + 1;} } return high < 0 ? -1 : high;

    5. 查找第一個(gè)大于目標(biāo)值的數(shù)/查找比目標(biāo)值大但是最接近目標(biāo)值的數(shù)

    此題以可由第 3 題變形而來,我們已經(jīng)找到了目標(biāo)值區(qū)域的上(右)邊界,那么再往右進(jìn)一位,即high + 1,就是第一個(gè)大于目標(biāo)值的數(shù)。其實(shí)high + 1也是退出循環(huán)后low的值,因?yàn)榇藭r(shí) low剛好等于high + 1,它大于high,所以 while 循環(huán)結(jié)束。我們只要判斷l(xiāng)ow是否超出邊界即可。

    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){mid = low + (high - low) / 2;if(target >= A[mid]){low = mid + 1;}else{high = mid - 1;} } return low > n ? -1 : low;

    6. 旋轉(zhuǎn)數(shù)組返回最小元素

    6.1 查找旋轉(zhuǎn)數(shù)組的最小元素(假設(shè)不存在重復(fù)數(shù)字)

    LeetCode: Find Minimum in Rotated Sorted Array
    Input: [3,4,5,1,2]
    Output: 1

    public int findMin(int[] nums) {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] > nums[right])left = mid + 1;else{right = mid;}}return nums[left]; }

    注意這里和之前的二分查找的幾點(diǎn)區(qū)別:

  • 循環(huán)判定條件為left < right,沒有等于號(hào)
  • 循環(huán)中,通過比較nums[left]與num[mid]的值來判斷mid所在的位置:
    • 如果nums[mid] > nums[right],說明前半部分是有序的,最小值在后半部分,令left = mid + 1;
    • 如果nums[mid] <= num[right],說明最小值在前半部分,令right = mid。

    最后,left會(huì)指向最小值元素所在的位置。

    6.2 查找旋轉(zhuǎn)數(shù)組的最小元素(存在重復(fù)項(xiàng))

    LeetCode: Find Minimum in Rotated Sorted Array II
    劍指offer:旋轉(zhuǎn)數(shù)組的最小數(shù)字
    Input: [2,2,2,0,1]
    Output: 0

    public int findMin(int[] nums) {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] > nums[right])left = mid + 1;else if(nums[mid] < nums[right])right = mid;elseright--;}return nums[left]; }

    和之前不存在重復(fù)項(xiàng)的差別是:當(dāng)nums[mid] == nums[right]時(shí),我們不能確定最小值在 mid的左邊還是右邊,所以我們就讓右邊界減一。

    7. 在旋轉(zhuǎn)排序數(shù)組中搜索

    7.1 不考慮重復(fù)項(xiàng)

    LeetCode: Search in Rotated Sorted Array

    法一:

    • 先利用方法 6.1 查找數(shù)組中的最小元素,即確定分界點(diǎn)的位置
    • 把旋轉(zhuǎn)的數(shù)組當(dāng)成偏移,用(offset + mid) % len來求真實(shí)的 mid 的位置。
    • 然后用二分查找來定位目標(biāo)值
    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] > nums[right])left = mid + 1;elseright = mid;}int offset = left;left = 0;right = len - 1;while(left <= right){mid = left + (right - left) / 2;int realmid = (mid + offset) % len;if(nums[realmid] == target)return realmid;else if(nums[realmid] < target)left = mid + 1;elseright = mid - 1;}return -1; }

    法二:其實(shí)沒有必要找到旋轉(zhuǎn)數(shù)組的分界點(diǎn),對(duì)于搜索左側(cè)還是右側(cè)我們是可以根據(jù)mid跟high的元素大小來判定出來的,直接根據(jù)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 存在重復(fù)項(xiàng)

    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. 二維數(shù)組中的查找

    劍指offer:二維數(shù)組中的查找

    二維數(shù)組是有序的,從右上角來看,向左數(shù)字遞減,向下數(shù)字遞增。因此可以利用二分查找的思想,從右上角出發(fā):

    • 當(dāng)要查找數(shù)字比右上角數(shù)字大時(shí),下移;
    • 當(dāng)要查找數(shù)字比右上角數(shù)字小時(shí),左移;

    總結(jié)

    以上是生活随笔為你收集整理的[算法总结] 二分查找的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。