基本的二分查找、尋找第一個和最后一個數的二分查找

二分查找

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

二分查找場景:有序數組尋找一個數、尋找左側邊界(有序數組第一個等目標數的下標)、尋找右側邊界(有序數組最后一個等于目標數的下標)

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是為了把所有情況寫清楚,這樣可以清楚的展現所有細節。本文都使用else if,旨在說清楚,可自行簡化。

其中…標記的部分,就是可能出現細節問題的地方,當你見到一個二分查找的代碼時,首先要注意這幾個地方。

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

這個場景是最簡單的,即搜索一個數,如果存在,返回其索引,否則返回-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的循環條件中是<=,而不是<?
    答:因為初始化right的賦值是numsSize-1,即最后一個元素的索引,而不是numsSize,這二者可能出現在不同功能的二分查找中,區別是:前者相當于兩端都閉區間[left,right],后者相當于左閉右開區間[left,right),因為索引大小為numsSize是越界的。
    我們這個算法中使用的是[left,right]兩端都閉的區間,這個區間就是每次進行搜索的區間,那while循環什么時候應該終止?搜索區間為空的時候應該終止,意味著你沒的找了。

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

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

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

3 尋找左側邊界的二分搜索

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

直接看代碼:

/* 二分查找左側邊界 */
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。
請添加圖片描述
nums[mid]>=target時,說明有大于等于target的數了,我們需要更新ans來記錄大于等于targte的數,right需要更新,然后繼續往在[left,mid-1]區間找大于等于target的數,如果nums[mid]<target,則需要在[mid+1,right]區間找大于等于target的數,left下標所指向的值始終是小于等于target(如果全部數據全部大于target,left將不會變化),所以,當結束時,ans所指向的值是[left,right]區間最后一個大于等于target的值,left下標所指向的值又始終是小于等于target,所以ans所指向的內容是第一個大于等于target的值。

4 尋找右側邊界的二分查找

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

/* 二分查找右側邊界 */
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;
}

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

5 合并

我們添加一個參數,表示找第一個等于target的數,還是找最后一個等于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) / 2;if (nums[mid] > target || (lower && nums[mid] >= target)) {right = mid - 1;ans = mid;} else {left = mid + 1;}}return ans;
}

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

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

示例代碼:


/* 
在一個升序數組中,找第一個等于target的數組,即找第一個大于等于target的數,返回其下標,最后判斷
是否等于targettarget。
找最后一個等于target的數組,即找第一個大于target的數,返回其下標減1,最后判斷該下標對應的數是否等于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;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/379666.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/379666.shtml
英文地址,請注明出處:http://en.pswp.cn/news/379666.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PostgreSQL 中的遞歸查詢 與oracle 的比較

PostgreSQL 中的遞歸查詢&#xff0c;2種方法&#xff1a; 1、用with decursive WITH RECURSIVE d AS (SELECT d1.id,d1.parent_id,d1.caption FROM course_types d1 where d1.dr 0 and d1.idtypeId union ALL SELECT d2.id,d2.parent_id,d2.caption FROM course_types d2, d …

教你如何玩轉GitHub

使用GitHub ①目的&#xff1a;借助GitHub托管項目代碼 基本概念&#xff1a; ①倉庫(Repository)&#xff1a; 用來存放項目代碼&#xff0c;每個項目對應一個倉庫&#xff0c;多個開源項目對應多個倉庫 ②收藏(Star)&#xff1a; 收藏項目&#xff0c;方便下次查看 ③…

Java SecurityManager checkDelete()方法與示例

SecurityManager類的checkDelete()方法 (SecurityManager Class checkDelete() method) checkDelete() method is available in java.lang package. checkDelete()方法在java.lang包中可用。 checkDelete() method calls checkPermission with FilePermission(filename,"d…

jQuery中的treeview插件

jQuery做樹狀結構真的很簡單,下面做一個最簡單的示例: 在html文件中引用: <link rel"stylesheet" href"../jquery.treeview.css" /> <link rel"stylesheet" href"../red-treeview.css" /> <link rel"styles…

Linux內核設計與實現---中斷和中斷處理程序

中斷和中斷處理程序1 中斷異常2 中斷處理程序上半部與下半部的對比3 注冊中斷處理程序釋放中斷處理程序4 編寫中斷處理程序重入和中斷處理程序共享的中斷處理程序中斷處理程序實例5 中斷上下文6 中斷處理機制的實現7 中斷控制禁止和激活中斷禁止指定中斷線中斷系統的狀態8 總結…

asp.net中的窗體身份驗證(最簡單篇)

在創建網站中&#xff0c;常常會使用到身份驗證。asp.net中內置了幾種身份驗證的方式&#xff0c;如Windows、Froms、Passport等。這幾種身份驗證的方式各有不同。一般來說&#xff0c;網站的身份驗證方式都會經過以下幾個步驟&#xff1a; 1、輸入用戶名和密碼&#xff0c;單擊…

bat文件調用dos命令 (dos淘金)

ECHO命令是大家都熟悉的DOS批處理命令的一條子命令&#xff0c;但它的一些功能和用法也許你并不是全都知道&#xff0c;不信你瞧&#xff1a; 1&#xff0e; 作為控制批處理命令在執行時是否顯示命令行自身的開關 格式&#xff1a;ECHO [ON|OFF] 如果想關閉“ECHO OFF”命令…

response細節點

一、 1&#xff09;、response獲得的流不需要手動關閉&#xff0c;Tomcat容器會幫你自動關閉 2&#xff09;、getWriter和getOutputStream不能同時調用 //error package com.itheima.content;import java.io.IOException; import javax.servlet.ServletException; import java…

Java RandomAccessFile writeBytes()方法與示例

RandomAccessFile類writeBytes()方法 (RandomAccessFile Class writeBytes() method) writeBytes() method is available in java.io package. writeBytes()方法在java.io包中可用。 writeBytes() method is used to write the sequence of bytes (i.e. string) to the file. E…

linux內核設計與實現---下半部和推后執行的工作

下半部和推后執行的工作1 下半部為什么要用下半部下半部的環境內核定時器2 軟中斷軟中斷的實現軟中斷處理程序執行軟中斷使用軟中斷3 tasklettasklet的實現使用taskletksoftirqd4 工作隊列工作隊列的實現工作、工作隊列和工作者線程之間的關系使用工作隊列5 下半部機制的選擇6 …

Jquery對復選框的操作

<from> 你的愛好是?<br/> <input type"checkbox" name"items" value"籃球" />籃球 <input type"checkbox" name"items" value"乒乓球" />乒乓球 <input type"checkbox" na…

HttpServletRequest(request的一些API)

一、request的運行流程 首先&#xff0c;自己寫一個web工程&#xff0c;也就是建一個工程&#xff1b;當把該web工程發布到Tomcat服務器當中&#xff0c;可以讓外界訪問&#xff0c;這就成了一個web應用。 在客戶端輸入一個網站&#xff0c;是web應用資源的地址URL&#xff0c…

DCI:James O. Coplien和Trygve Reenskau提出的新架構方法

http://www.infoq.com/cn/news/2009/05/dci-coplien-reenskau 轉載于:https://www.cnblogs.com/yelinpalace/archive/2009/06/13/1502573.html

Java ObjectStreamField getOffset()方法與示例

ObjectStreamField類的getOffset()方法 (ObjectStreamField Class getOffset() method) getOffset() method is available in java.io package. getOffset()方法在java.io包中可用。 getOffset() method is used to get the offset of this ObjectStreamField field. getOffse…

Mac VSCode配置C語言環境(可以調試)

Mac VSCode配置C語言環境c_cpp_properties.jsontasks.jsonlaunch.json新建一個文件夾&#xff0c;用vscode&#xff0c;然后再新建一個test.c文件。 #include <stdio.h>int main(void) {int a1,b1;int cab;printf("%d\n",c);return 0; }這篇文章說怎么配置c_c…

XmlPullParserException

今天在android的開發中約到一個問題 使用Ksoap2 訪問 WebService 拋出 XmlPullParserException 異常。 在網上淘了一下這個問題 http://www.eoeandroid.com/thread-70527-1-1.html 不能解決我的問題&#xff0c;求解轉載于:https://www.cnblogs.com/pengqinping/archive/2012/0…

vShpere Client在win 7 RC下和2008下 無法正常連接esx主機之解決辦法

vShpere Client在win 7 RC下和2008下 無法正常連接esx主機之解決辦法 在win7下和2008下打開client后連接esx主機會出現2個錯誤提示, 第一個是 第二個是 然后就連接失敗了,開始以為是CC的esx主機安裝有問題,后來找了找,借助了強大google工具,終于找到解決辦法.解決辦法如下: 1.從…

tooctalstring_Java Integer類toOctalString()方法的示例

tooctalstring整數類toOctalString()方法 (Integer class toOctalString() method) toOctalString() method is available in java.lang package. toOctalString()方法在java.lang包中可用。 toOctalString() method is used to represent an octal string of the given parame…

localhost與127.0.0.1之間的關系更改

其實localhost的默認IP地址為127.0.0.1&#xff0c;因為這是一種映射關系。 更改步驟如下&#xff1a; C:\Windows\System32\drivers\etc 下的hosts 打開hosts可以看到 更改即可

基于Hash表的排序--C語言

我們知道&#xff0c;C語言里面是沒有hash表的&#xff0c;但是我們可以用一個結構體表示&#xff0c;對結構體排序&#xff0c;我們可以用qsort排序。 下面我們用一個LeedCode上面的一道題目講解。 347. 前 K 個高頻元素 這個題目是讓我們求解前k個高頻元素&#xff0c;求解思…