二分查找Go版本实现

本文会汇总常见的二分查找题型并给出相关Leetcode题目的题解

二分查找框架:

基础二分算法:

二分查找是特定对于有序的数组中寻找特定元素或者特定元素位置的算法,我一直使用的是[left,right] 闭区间的算法方式,所以我们循环退出的条件是while (l<=r) ,也就是二者错开才退出循环,当l=r的时候仍然可能搜索到数据。

二分查找

Go 复制代码
func search(nums []int, target int) int {
    l,r:=0,len(nums)-1
    //定义区间为l-r的闭区间,每一个元素都可能被搜索到
    for l<=r{
        //防止超过最大范围取中点
        mid:=(r-l)/2+l
        if nums[mid]==target{
            return mid
        }else if nums[mid]>target{
            //当前值大于target,缩小范围,也就是动右指针
            r=mid-1
        }else{
            l=mid+1
        }
    }
    return -1
}

下面针对mid的定义是为了防止溢出设置的,而针对指针的移动,因为mid在不匹配的情况下就证明了从l或者r的区间到mid是没有目标数据的,那么直接移动,使得l=mid+1或者r=mid-1。

搜索左侧边界的二分算法:

只搜索对应元素还是太基础了,我们经常会遇见搜索某一个数字的最左侧或者最右侧边界的问题,那么这类问题和上方的不同点在于数组中会含有多个target目标值。

Go 复制代码
// 将给定的数字 nums 搜索 target 的左侧边界
func left_bound(nums []int, target int) int {
    left := 0
    right := len(nums) - 1
    // 搜索区间为 [left, right]
    for left <= right {
        mid := left + (right - left) / 2
        if nums[mid] < target {
            // 搜索区间变为 [mid+1, right]
            left = mid + 1
        } else if nums[mid] > target {
            // 搜索区间变为 [left, mid-1]
            right = mid - 1
        } else if nums[mid] == target {
            // 收缩右侧边界
            right = mid - 1
        }
    }
    // 判断 target 是否存在于 nums 中
    if left < 0 || left >= len(nums) {
        return -1
    }
    // 如果越界,target 肯定不存在,返回 -1
    if nums[left] == target {
        // 判断一下 nums[left] 是不是 target
        return left
    }
    return -1
}

在这里,我们针对mid=target的时候需要不同的处理,因为我们在搜索边界值,那么当二者相等的时候,我们不可以直接返回,需要不断收缩右边界,看看l到mid-1还有没有目标值了。在整个循环的过程中,假设数组为[1,2,3,3,4,5,6],最后一次循环的时候,右侧指针还要向左移动到2指向的位置,而left一直指向的是最左侧3的位置,所以我们需要返回left。

搜索右侧边界的二分算法:

同理,搜索右侧边界的代码如下:

Go 复制代码
func rightBound(nums []int, target int) int {
    left, right := 0, len(nums) - 1
    for left <= right {
        mid := left + (right - left) / 2
        if nums[mid] < target {
            left = mid + 1
        } else if nums[mid] > target {
            right = mid - 1
        } else if nums[mid] == target {
            // 这里改成收缩左侧边界即可
            left = mid + 1
        }
    }
    // 最后改成返回 left - 1
    if left - 1 < 0 || left - 1 >= len(nums) {
        return -1
    }
    if nums[right] == target {
        return right
    }
    return -1
}

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

本题就是对于上面搜索左右边界的使用,只需要把两个代码拼起来即可解题。

Go 复制代码
func searchRange(nums []int, target int) []int {
    res:=[]int{-1,-1}
    left,right:=0,len(nums)-1
    for left<=right{
        mid:=left+(right-left)/2
        if target<=nums[mid]{
            right=mid-1
        }else{
            left=mid+1
        }  
    } 
    if left<len(nums)&&nums[left]==target{
        res[0]=left
    }
    left, right = 0, len(nums)-1
    for left<=right{
        mid:=left+(right-left)/2
        if target>=nums[mid]{
            left=mid+1
        }else{
            right=mid-1
        }  
    }
    if  right>=0&&nums[right]==target{
        res[1]=right
    }
    return res
}

二分查找习题运用:

统计目标成绩出现次数

对于本题,数组非严格递增,如果有多个符合条件的目标成绩需要找到左右端点,下标相减+1就是总共的成绩数。

Go 复制代码
func searchLeft(scores []int,target int)int{
    l,r:=0,len(scores)-1
    for l<=r{
        mid:=(r-l)/2+l
        if scores[mid]==target{
            r=mid-1
        }else if scores[mid]<target{
            l=mid+1
        }else{
            r=mid-1
        }
    }
    if l<0||l>=len(scores){
        return -1
    }
    if scores[l]==target{
        return l
    }
    return -1
}
func searchRight(scores []int,target int)int{
    l,r:=0,len(scores)-1
    for l<=r{
        mid:=(r-l)/2+l
        if scores[mid]==target{
            l=mid+1
        }else if scores[mid]<target{
            l=mid+1
        }else{
            r=mid-1
        }
    }
    if r<0||r>=len(scores){
        return -1
    }
    if scores[r]==target{
        return r
    }
    return -1
}
func countTarget(scores []int, target int) int {
    res:=0
    left:=searchLeft(scores,target)
    right:=searchRight(scores,target)
    if left==-1&&right==-1{
        return 0
    }
    res=right-left+1
    return res

}

搜索旋转排序数组

观察题目给定数据,我们发现在旋转点前和旋转点之后的两个数组都是有序的,那么只需要对这两个区间分别进行二分查找,再重新旋转返回对应下标即可。

Go 复制代码
func binsearch(nums []int,target int)int{
    l,r:=0,len(nums)-1
    for l<=r{
        mid:=(r-l)/2+l
        if nums[mid]==target{
            return mid
        }else if nums[mid]<target{
            l=mid+1
        }else{
            r=mid-1
        }
    }
    return -1
}
func search(nums []int, target int) int {
    index:=0
    for i:=0;i<len(nums)-1;i++{
        if nums[i]>nums[i+1]{
            //index找到的是下标K
            index=i+1
        }
    }
    leftNum:=nums[:index]
    rightNum:=nums[index:]
    ll:=binsearch(leftNum,target)
    rl:=binsearch(rightNum,target)
    if ll!=-1{
        return ll
    }
    if rl!=-1{
        return (rl+index)%len(nums)
    }
    return -1
}

搜索二维矩阵

题目给的条件是二维数组每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。所以直接遍历存在nums当中二分查找。

Go 复制代码
func searchMatrix(matrix [][]int, target int) bool {
    m,n:=len(matrix),len(matrix[0])
    nums:=[]int{}
    for i:=0;i<m;i++{
        for j:=0;j<n;j++{
            nums=append(nums,matrix[i][j])
        }
    }
    l,r:=0,m*n-1
    for l<=r{
        mid:=(r-l)/2+l
        if nums[mid]==target{
            return true
        }else if nums[mid]<target{
            l=mid+1
        }else{
            r=mid-1
        }
    }
    return false
}
相关推荐
小雨下雨的雨2 小时前
Flutter鸿蒙共赢——墨染算法:柏林噪声与鸿蒙生态中的数字水墨意境
算法·flutter·华为·交互·harmonyos·鸿蒙
NAGNIP8 小时前
万字长文!回归模型最全讲解!
算法·面试
知乎的哥廷根数学学派8 小时前
面向可信机械故障诊断的自适应置信度惩罚深度校准算法(Pytorch)
人工智能·pytorch·python·深度学习·算法·机器学习·矩阵
txinyu的博客8 小时前
解析业务层的key冲突问题
开发语言·c++·分布式
666HZ6669 小时前
数据结构2.0 线性表
c语言·数据结构·算法
SmartRadio9 小时前
ESP32添加修改蓝牙名称和获取蓝牙连接状态的AT命令-完整UART BLE服务功能后的完整`main.c`代码
c语言·开发语言·c++·esp32·ble
且去填词10 小时前
Go 语言的“反叛”——为什么少即是多?
开发语言·后端·面试·go
余瑜鱼鱼鱼10 小时前
Java数据结构:从入门到精通(十二)
数据结构
实心儿儿10 小时前
Linux —— 基础开发工具5
linux·运维·算法