LeetCode算法题4:二分查找及扩展应用
文章目錄
- 前言
- 一、二分查找
- 二、第一個錯誤的版本
- 三、搜索插入位置
- 總結
前言
??????Leetcode算法系列:https://leetcode-cn.com/study-plan/algorithms/?progress=njjhkd2
??????簡單介紹總結一下二分查找相關的算法題:
一、二分查找
??????題目鏈接:https://leetcode-cn.com/problems/binary-search/
??????題目描述:給定一個 n 個元素有序的(升序)整型數組 nums 和一個目標值 target ,寫一個函數搜索 nums 中的 target,如果目標值存在返回下標,否則返回 -1。
??????思路:直接采用通用的二分查找算法即可:
class Solution {public int search(int[] nums, int target) {int start=0,end=nums.length-1,mid;while(start<=end){mid=(start+end)/2; // 最好改為:mid = start+(end-start)/2;if(nums[mid]==target)return mid;else if(nums[mid]<target)start=mid+1;elseend=mid-1;}return -1;} }??????1,取 mid 值時,采用 mid = start+(end-start)/2; 可以避免當 start 和 end 很大時造成的整數(int)越界。
??????2,循環結束條件為 start<=end,所以在 while 循環退出時有 start>end,(在此為 start=end+1)。
??????3,二分查找的時間復雜度為 O(logn),因為每次循環都會將待處理數組的規模縮小一半。
二、第一個錯誤的版本
??????題目鏈接:https://leetcode-cn.com/problems/first-bad-version/
??????題目描述:你是產品經理,目前正在帶領一個團隊開發新的產品。不幸的是,你的產品的最新版本沒有通過質量檢測。由于每個版本都是基于之前的版本開發的,所以錯誤的版本之后的所有版本都是錯的。
假設你有 n 個版本 [1, 2, …, n],你想找出導致之后所有版本出錯的第一個錯誤的版本。
你可以通過調用 bool isBadVersion(version) 接口來判斷版本號 version 是否在單元測試中出錯。實現一個函數來查找第一個錯誤的版本。你應該盡量減少對調用 API 的次數。
??????思路:保持之間的二分查找框架不變,修改循環退出條件為當前版本正確且上一個版本錯誤,
public class Solution extends VersionControl {public int firstBadVersion(int n) {if(isBadVersion(1))return 1;int start=2,mid; //區間為[2,n].while(start<=n){ //還是按照二分查找的思路來做mid=start+(n-start)/2;if(!isBadVersion(mid)) //縮小區間到[mid+1,n]start=mid+1;else if(!isBadVersion(mid-1)) //如果mid為true,mid-1為false,返回mid即可return mid; else //如果mid-1不是false,縮小區間到[start,mid-1]n=mid-1;}return -1;} }??????缺點時要調用兩次 isBadVersion函數。
??????Version2:修改循環判定條件為start<end,在循環退出時的 start 或 end 為我們感興趣的下標值。并采用合理的縮小規模方式在循環內僅調用一次 isBadVersion 函數。如下:
public class Solution extends VersionControl {public int firstBadVersion(int n) {int start=1,mid;while(start<n){mid=start+(n-start)/2;if(!isBadVersion(mid))start=mid+1;elsen=mid;}return start;} }??????上述算法看起來很巧妙,甚至成功的有點僥幸,因為在每次計算 mid 值時,由于向下取整,所以有時候會出現 mid==start 出現,而不會存在 mid==n 出現。因此令 n=mid;這樣在每次循環時區間規模總會縮小,直至得到最終結果。
??????在縮小區間時,最好不要采用 start==mid,很有可能區間不會縮小,陷入死循環。比如當要求找到最后一個正確的版本時會發生這種情況。
三、搜索插入位置
??????題目鏈接:https://leetcode-cn.com/problems/search-insert-position/
??????題目描述:給定一個排序數組和一個目標值,在數組中找到目標值,并返回其索引。如果目標值不存在于數組中,返回它將會被按順序插入的位置。
請必須使用時間復雜度為 O(log n) 的算法。
??????思路:采用通用的二分查找框架,如果目標值存在于數組中,由 mid 給出其返回下標;如果不存在的話,由 start 或 end+1 給出返回下標。
class Solution {public int searchInsert(int[] nums, int target) {int start=0,end=nums.length-1,mid;while(start<=end){mid=start+(end-start)/2;if(nums[mid]==target)return mid;else if(nums[mid]<target)start=mid+1;elseend=mid-1;}return start; //關鍵點在于確定最后一個查找區間和target之間的關系。//return end+1;} }??????由 start 或 end+1 給出返回值下標的原因在于:在一個不含目標值的升序數組中采用通用的二分查找算法查找 target 的位置,在循環結束后 end 處的值剛好小于 target;start 處的值剛好大于 target。 比如對于數組 [1,3,4,6,7],查找 5 所在位置:循環結束后 start =3,end = 2。
??????Version2:修改循環判定條件為start<end,在循環退出時的 start 或 end 為我們感興趣的下標值。如下:
public int searchInsert(int[] nums, int target) {int end=nums.length-1,start=0,mid;// 特殊判斷if (nums[end] < target) return end+1;// 程序走到這里一定有 nums[end] >= target,保證插入位置在區間 [start..end]while(start<end){mid=start+(end-start)/2;if(nums[mid]<target)start=mid+1;// 下一輪搜索的區間是 [mid + 1..end]elseend=mid;// 下一輪搜索的區間是 [start..mid]}return end;}??????和 二、查找錯誤的版本類似:在循環中令 end=mid 來保證循環執行結束。
總結
??????通用的二分查找判定條件為 start<=end,意味著結果在循環中給出;當采用 start<end 時,結果在循環執行后由 start 或 end 給出。
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的LeetCode算法题4:二分查找及扩展应用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二叉查找树的实现
- 下一篇: LeetCode算法题5:双指针