二分

更加严谨的二分

  • 使用场景
    • 满足类似的条件,需要找的某个数>=target
    • 或者<=target
  • 一般的二分
    • 只会判断是否满足==的条件
    • 对于边界的点会舍弃
    • 造成有可能找不到正确的答案

二分模板

  • 判断>=target 的情况
// 序列为0-n-1
// 为什么设置n,n为不合法的情况,即没有比target还要大的
let left=0
let right=n
while(left<right){
let mid=(left+right)>>1
if(nums[mid]>=target){
right=mid
}else{
left=mid+1
}
}
return right
  • 判断<=target的情况
// 这里-1也是为了进行边界处理
left=-1
right=nums.length-1
while(left<right){
let mid=(left+right+1)>>1
if(nums[mid]<=target){
left=mid
}else{
right=mid-1
}
}
return right

34. 在排序数组中查找元素的第一个和最后一个位置

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109
解题思路
1. 其实是查找>=target 的第一个数
2. 和<=target 的最后一个数
完整代码
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
var searchRange = function(nums, target) {
// 问题本质在于求>=target 的第一个数
// 和<=target 的最后一个数
let left=0
let right=nums.length
let res=[]
while(left<right){
let mid=(left+right)>>1
if(nums[mid]>=target){
right=mid
}else{
left=mid+1
}
}
if(nums[right]!==target) right=-1
res.push(right)
left=-1
right=nums.length-1
while(left<right){
let mid=(left+right+1)>>1
if(nums[mid]<=target){
left=mid
}else{
right=mid-1
}
}
if(nums[right]!==target) right=-1
res.push(right)
return res
};

153. 寻找旋转排序数组中的最小值(二分查找的变种)

已知一个长度为 n 的数组,预先按照升序排列,经由 1n旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:

  • 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
  • 若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]

给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。

示例 2:

输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。

示例 3:

输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。

提示:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • nums 中的所有整数 互不相同
  • nums 原来是一个升序排序的数组,并进行了 1n 次旋转
解题思路
1. 需要将题目抽象一下
2. 二分不仅仅只是在一个有序数组中查找某个值
3. 是一种思想,满足条件的放右边,不满足的放左边(舍弃)
4. 本题旋转后的数组,部分有序
5. 本质是找满足<=最后一个元素 的第一个元素(下标靠前的)
完整代码
/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function(nums) {
// 旋转后的数组部分有序
3,4,5|1,2
// 观察可以得出二分的条件
// 即<=2 的第一个元素
4,5,6,7|0,1,2
// <=2 的第一个元素
let left=0
let right=nums.length-1
while(left<right){
let mid=Math.floor((left+right)/2)
if(nums[mid]<=nums[nums.length-1]){
// 当前的mid满足小于2
// 是不是之前的也满足
// 我们向左缩小范围
right=mid
}else{
left=mid+1
}
}
return nums[right]
};
posted @   凌歆  阅读(149)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示