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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

基本的二分查找、寻找第一个和最后一个数的二分查找

發(fā)布時間:2023/12/1 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基本的二分查找、寻找第一个和最后一个数的二分查找 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

二分查找

  • 1 二分查找的框架
  • 2 尋找一個數(shù)(基本的二分搜索)
  • 3 尋找左側(cè)邊界的二分搜索
  • 4 尋找右側(cè)邊界的二分查找
  • 5 合并

二分查找場景:有序數(shù)組尋找一個數(shù)、尋找左側(cè)邊界(有序數(shù)組等一個等目標(biāo)數(shù)的下標(biāo))、尋找右側(cè)邊界(有序數(shù)組最后一個等于目標(biāo)數(shù)的下標(biāo))

1 二分查找的框架

int binarySearch(int *nums,int numsSize,int target) {int left=0,rigth = ...;while(...){int mid = left +(rigth-left)/2;if(nums[mid] == target){...}else if (nums[mid]<target){left = ...;}else if (nums[mid]>target){rigth = ...;}}return ... }

使用else if是為了把所有情況寫清楚,這樣可以清楚的展現(xiàn)所有細節(jié)。本文都使用else if,旨在說清楚,可自行簡化。

其中…標(biāo)記的部分,就是可能出現(xiàn)細節(jié)問題的地方,當(dāng)你見到一個二分查找的代碼時,首先要注意這幾個地方。

2 尋找一個數(shù)(基本的二分搜索)

這個場景是最簡單的,即搜索一個數(shù),如果存在,返回其索引,否則返回-1。

int binarySearch(int *nums,int numsSize,int target) {int left=0;int right = numsSize-1; //注意while(left<=rigth) //注意{int mid = left +(right-left)/2;if(nums[mid] == target)return mid;else if(nums[mid]<target){left = mid+1; //注意}else if(nums[mid]>target){right = mid+1; //注意}}return -1; }
  • 為什么while的循環(huán)條件中是<=,而不是<?
    答:因為初始化right的賦值是numsSize-1,即最后一個元素的索引,而不是numsSize,這二者可能出現(xiàn)在不同功能的二分查找中,區(qū)別是:前者相當(dāng)于兩端都閉區(qū)間[left,right],后者相當(dāng)于左閉右開區(qū)間[left,right),因為索引大小為numsSize是越界的。
    我們這個算法中使用的是[left,right]兩端都閉的區(qū)間,這個區(qū)間就是每次進行搜索的區(qū)間,那while循環(huán)什么時候應(yīng)該終止?搜索區(qū)間為空的時候應(yīng)該終止,意味著你沒的找了。

while(left<=right)終止條件是left==right+1,寫成區(qū)間的形式是[ right+1,right],這個時候搜索區(qū)間為空,直接返回-1.

while(left<right)的終止條件是left==right,寫成區(qū)間的形式是[right,right],者時候區(qū)間非空,還有一個數(shù)nums[right],如果此時while循環(huán)停止了,就漏掉一個數(shù),如果這個時候返回-1就可能出現(xiàn)錯誤。

  • 為什么left=mid+1,right=mid-1?
    本算法的搜索區(qū)間兩端都是閉的,即[left,right]。那么當(dāng)我們發(fā)現(xiàn)索引mid不是要找的target時,如果確定下一步的搜索區(qū)間呢?
    當(dāng)然是去搜索[left,mid+1]或者[mid+1,right],因為mid已經(jīng)搜索過了,應(yīng)該從搜索區(qū)間去除。
  • 此算法有什么缺陷?
    比如說你有有序數(shù)組nums=[1,2,2,2,3],次算法返回的索引是2,但是如果我想得到target的左側(cè)邊界,即索引1,或者想得到target的右側(cè)邊界,即索引3,這樣的話,次算法是無法處理的。

3 尋找左側(cè)邊界的二分搜索

查找左側(cè)邊界的數(shù),即在一個有序數(shù)組中,找到第一個等于target的下標(biāo)。比如數(shù)組int nums[]={5,7,7,8,8,8,10},target=8,第一個等于8的下標(biāo)是3,第一個大于等于8的數(shù)組下標(biāo)也是3。即找到第一個等于target的數(shù)等價于 找第一個大于等于targte的數(shù)的下標(biāo),然后我們判斷該下標(biāo)所對應(yīng)的數(shù)是否是target。

直接看代碼:

/* 二分查找左側(cè)邊界 */ int lower_bound(int *nums,int numsSize,int target) {int left=0,right=numsSize-1,ans=numsSize;while(left<=right){int mid = left+(right-left)/2;if(nums[mid]>=target){right = mid-1;ans = mid;}else {left = mid+1;}}return ans; } int main(void) {int nums[]={5,7,7,8,8,8,10};int ret;//int p = high_bound(nums,7,8);//printf("%d \n",p);ret = lower_bound(nums,7,8);printf("%d \n",ret);return 0; }

輸出是3。

當(dāng)nums[mid]>=target時,說明有大于等于target的數(shù)了,我們需要更新ans來記錄大于等于targte的數(shù),right需要更新,然后繼續(xù)往在[left,mid-1]區(qū)間找大于等于target的數(shù),如果沒有nums[mid]<target,則需要在[mid+1,right]區(qū)間找大于等于target的數(shù),left下標(biāo)所指向的值始終是小于等于target(如果全部數(shù)據(jù)全部大于target,left將不會變化),所以,當(dāng)結(jié)束時,ans所指向的值是[left,right]區(qū)間最后一個大于等于target的值,left下標(biāo)所指向的值又始終是小于等于target,所以ans所指向的內(nèi)容是第一個大于等于target的值。

4 尋找右側(cè)邊界的二分查找

尋找右側(cè)邊界的數(shù),就是找第一個大于target的數(shù),返回其下標(biāo)減1,int nums[]={5,7,7,8,8,8,10},最后一個等于8的下標(biāo)是5,第一個大于8的數(shù)是10,其下標(biāo)是6,減去1是5,找target最后位置等價于找第一個大于target的下標(biāo)減1,然后判斷該位置上的數(shù)是否等于target。
具體代碼為:

/* 二分查找右側(cè)邊界 */ int high_bound(int *nums,int numsSize,int target) {int left=0,right=numsSize-1,ans=numsSize;while(left<=right){int mid = left+(right-left)/2;if(nums[mid]>target){right = mid-1;ans = mid;}else {left = mid+1;}}return ans; } int main(void) {int nums[]={5,7,7,8,8,8,10};int ret;//int p = high_bound(nums,7,8);//printf("%d \n",p);ret = high_bound(nums,7,8)-1;printf("%d \n",ret);return 0; }

運行結(jié)果是5。
如果nums[mid]>target,有大于target的數(shù)了,ans就要去記錄,right更新,right=mid-1 ,如果nums[mid]<=target,則left需要更新,left=mid-1,left指示的內(nèi)容始終是小于等于target的(如果數(shù)據(jù)全部大于target,left不會變)。只要left<=right,就會一直縮小區(qū)間,當(dāng)運行結(jié)束后,ans所指示的內(nèi)容是最后一個大于target的數(shù)。

5 合并

我們添加一個參數(shù),表示找第一個等于target的數(shù),還是找最后一個等于target的數(shù)。

int binarySearch(int* nums, int numsSize, int target, bool lower) {int left = 0, right = numsSize - 1, ans = numsSize;while (left <= right) {int mid = (left + right) / 2;if (nums[mid] > target || (lower && nums[mid] >= target)) {right = mid - 1;ans = mid;} else {left = mid + 1;}}return ans; }

當(dāng)lower為真時,表示找第一個大于等于target的數(shù),當(dāng)lowe為假時,表示找第一個大于target的數(shù),返回之后,檢查和上面代碼一樣。

leedcode的第34題:在排序數(shù)組中查找元素的第一個和最后一個位置

示例代碼:

/* 在一個升序數(shù)組中,找第一個等于target的數(shù)組,即找第一個大于等于target的數(shù),返回其下標(biāo),最后判斷 是否等于targettarget。 找最后一個等于target的數(shù)組,即找第一個大于target的數(shù),返回其下標(biāo)減1,最后判斷該下標(biāo)對應(yīng)的數(shù)是否等于target。 */ int binarySearch(int *nums,int numsSize,int target,bool lower) {int left=0,right=numsSize-1,ans=numsSize;while(left<=right){int mid=left+(right-left)/2;if(nums[mid]>target || (lower && nums[mid]>=target)){right=mid-1;ans = mid;}else{left=mid+1;}}return ans; } /*** Note: The returned array must be malloced, assume caller calls free().*/ int* searchRange(int* nums, int numsSize, int target, int* returnSize){int leftIdx = binarySearch(nums,numsSize,target,true);int rightIdx = binarySearch(nums,numsSize,target,false)-1;int *ret = (int *)malloc(sizeof(int)*2);*returnSize=2;if(leftIdx<=rightIdx && nums[leftIdx]==target && nums[rightIdx]==target){ret[0]=leftIdx;ret[1]=rightIdx;return ret;}ret[0]=-1;ret[1]=-1;return ret; }

總結(jié)

以上是生活随笔為你收集整理的基本的二分查找、寻找第一个和最后一个数的二分查找的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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