zoukankan      html  css  js  c++  java
  • 二分查找(golang)

    感觉自己二分总是写不对,上下界的寻找出错,在此记录下
    先是基本的查找和特殊查找,然后是对二分进行融合的题目

    相关学习链接

    b乎关于二分的讨论
    循环带==号的解法,别搞混了
    还有这个

    其他题目汇总

    教程可以试读
    本文是基于for left<right,而非等于的方式

    普通二分

    func search(nums []int, target int) int {
        left,right:=0,len(nums)
        for left<right{ //相等时候退出循环
            mid:=left+(right-left)/2
            if nums[mid]<target{ //左闭右开,所以左边+1
                left = mid+1
            }else if nums[mid]>target{
                right = mid  //右边不变,右开,不包含在内
            }else{
                return mid
            }
        }
        return left  //目标值如果不存在left,则是大于目标值的第一位元素下标
    }
    

    704 二分查找

    题目链接

    • 简单二分,无重复找target, 标准写法
      这样写的好处,最后如果目标不存在,直接返回left,则left是大于目标值的下一位下标

    这样的写法需要考虑的问题是,1.返回的left元素不一定等于目标值,2.返回的left已经越界到达最右边

    func search(nums []int, target int) int {
        left,right:=0,len(nums)
        for left<right{ //相等时候退出循环
            mid:=left+(right-left)/2
            if nums[mid]<target{ //左闭右开,所以左边+1
                left = mid+1
            }else if nums[mid]>target{
                right = mid  //右边不变,右开,不包含在内
            }else{
                return mid
            }
        }
        return -1  //没找到返回-1,如果返回left,则是大于目标值的第一位元素下标
    }
    

    35 搜索插入位置

    题目链接

    • 同上面一样,但开始需要增加特殊判断,当目标值1大于数组值

    (此题目数组中无重复值,如果有,代码需要改动)
    为什么要特殊判断,举个栗子

       输入数组[1,3,5,6]  找7应该插入的位置
    按照之前代码,left会不断=mid+1,向右,right一直在len(nums),也就是3
    最终 left==right时候退出循环,left ==3,
    实际应该插入的位置下标为4,
    因此先增加判断,target>最后一位,直接 return  长度(就是应该插入的位置下标)
    
    func searchInsert(nums []int, target int) int {
        //特殊判断,最后一位和目标值
        if target>nums[len(nums)-1]{
            return len(nums)
        }
        //寻找大于目标值下标
        left,right:=0,len(nums)-1
        for left<right{
            mid:=left+(right-left)/2
            if nums[mid]<target{
                left = mid+1
            }else{
                right = mid
            }
        }
        return left
    }
    

    牛客 74 数字在升序数组中出现的次数

    题目链接

    • 二分法找上下界,找元素右边的时候,找到的实际为大于元素的位置
    package main
    
    /**
     * 
     * @param data int整型一维数组 
     * @param k int整型 
     * @return int整型
    */
    func GetNumberOfK( data []int ,  k int ) int {
        // write code here
        left,right:=0,len(data)
        //找左边
        for left<right{
            mid:=left+(right-left)/2
            if data[mid]<k{
                left = mid+1
            }else{
                right = mid
            }
        }
        l:=left
        left,right=0,len(data)
        //找右边大于元素的位置
         for left<right{
            mid:=left+(right-left)/2
            if data[mid]<=k{
                left = mid+1
            }else{
                right = mid
            }
        }
        r:=left
        return r-l //不用+1,刚好是长度,因为r是大于元素的位置
    }
    

    34 排序数组中找到元素第一个和最后一个位置

    题目链接

    • 两个二分,一个找左,一个找右

    找右边之前需要特殊判断,
    如果在左边就找不到元素,则

    func searchRange(nums []int, target int) []int {
        if len(nums)==0{
            return []int{-1,-1}
        }
        left:=searchLeft(nums,target)
        if left==len(nums)|| nums[left]!=target{ //没找到直接返回, 前面的left==len(nums)是因为,[2,2]找3,最后left是2,已经越界
            return []int{-1,-1}
        }
        right:=searchRight(nums,target)-1
        return []int{left,right}
    }
    
    //找重复元素左侧,没有找到的是大于元素的位置,因为left = mid+1
    func searchLeft(nums[]int,target int)int{ 
        if len(nums)==0{
            return -1
        }
        left,right:=0,len(nums)
        for left<right{
            mid:=left+(right-left)/2
            if nums[mid]<target{
                left = mid+1
            }else{          //包含等于的情况,nums[mid]等于目标值,但不一定是最左边的.
                right = mid
            }
        }
       return left    //返回的下标不一定是目标值例如 [0,2,2,3] ,target=1,则第一次mid = (0+3)/2=1,最后left = righ = 1,最后返回的left是2的下标.
    }
    
    //有重复元素右侧大于元素的下标
    func searchRight(nums[]int,target int)int{
        if len(nums)==0{
            return -1
        }
      left,right:=0,len(nums)
        for left<right{
            mid:=left+(right-left)/2
            if nums[mid]<=target{ //等于,则左侧向右最后减一
                left = mid+1
            }else{
                right = mid
            }
        }
        return left
    }
    

    搜索旋转数组最小值 1,2

    题目链接2 1在2题上面

    • 二分去重,通用解法 o(logn)
      需要用mid和右边判断,确定自己的位置
    //比较nums[mid]和nums[right]大小
    func findMin(nums []int) int {
        //二分去重
        left,right:=0,len(nums)-1
        for left<right{
            mid:=left+(right-left)/2
            if nums[mid]>nums[right]{
                 left = mid+1
            }else if nums[mid]<nums[right]{
                right = mid
            }else{ //相等right--,  无论1当时的mid在左边还是右边都是对的
                right--
            }
        }
        return nums[left]
    }
    

    33 搜索旋转排序数组1 (需要<=)

    题目链接

    • 特殊情况,二分需要left<=right
    func search(nums []int, target int) int {
        //特殊题目使用=
        left,right:=0,len(nums)-1
        for left<=right{
            mid := left+(right-left)/2 
            if nums[mid]==target{
                return mid
            }else if  nums[mid]>=nums[left]{ //中点大于左边,则在左边寻找
                if target>=nums[left]&&target<nums[mid]{ //在左边和中点之间,right-
                    right =  mid-1
                }else{
                    left = mid+1
                }
            }else{ //小于左边,则在右边寻找
                if target>nums[mid]&&target<=nums[right]{
                    left = mid+1
                }else{
                    right = mid-1
                }
            }
        }
        return -1
    }
    

    81 搜索旋转排序数组2(需要<=)

    • 特殊判断,防止nums[mid]==nums[left]影响判断
      题目链接
    func search(nums []int, target int) bool {
        //防止重复干扰计算 nums[left]==nums[mid]时候left++
    
        left,right:=0,len(nums)-1
        for left<=right{
            mid:=left+(right-left)/2
            if nums[mid]==target{
                return true
            }else if nums[mid]==nums[left]{
                left++
            }else if nums[mid]>nums[left]{ //左边寻找
                if target>=nums[left]&&target<nums[mid]{
                    right = mid-1
                }else{
                    left = mid+1
                }
            }else{
                if target>nums[mid]&&target<=nums[right]{//右边寻找
                    left = mid+1
                }else{
                    right = mid-1
                }
            }
        }
        return false
    }
    

    牛客91 最长递增子序列(具体序列)

    可以参考算法导论
    题目链接
    dp会超时O(n^2)
    二分求具体序列O(nlogn)

    
    package main
    import "math"
    func LIS( arr []int ) []int {
        //两个数组
        n:=len(arr)
        tail:=make([]int,n+1) //存储LIS长度为i的最小结尾数字,0不使用
        tail[0] = math.MinInt32
        dp:=make([]int,n)  //存储arr[i]结尾的LIS长度
        end:=0  //记录LIS结尾下标
        for i:=0;i<n;i++{
            num:=arr[i]
            if num>tail[end]{
                end++
                tail[end] = num
                dp[i] = end
            }else{ //二分找到前面的大于元素的位置进行替换
                left,right:=1,end
                for left<right{
                    mid:=left+(right-left)/2
                    if tail[mid]<num{
                        left = mid+1
                    }else{
                        right = mid
                    }
                }
                //更新数据
                tail[left] = num
                dp[i] = left  //i位置最长序列更新
            }
        }
        //字典序最小,如果dp[i]=dp[j],i<j,一定arr[j]<arr[i] 否则dp[j]应该增加
        res:=make([]int,end) //找到最长递增子序列
        size:=end
        for i:=n-1;i>=0;i--{
            if dp[i]==size{
                res[size-1] = arr[i]
                size--
            }
        }
       return res
    }
    
  • 相关阅读:
    html iframe 滚动条
    Angular-Ant Desigin 开篇
    端口访问不了的原因
    swift 加载 本地html 和 网络路径
    xcode9.4 报错 error:The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.
    viewDidLoad, viewWillDisappear, viewWillAppear等区别及各自的加载顺序
    JavaScript设计模式之一Interface接口
    ES6原生Class
    react portals
    react-native-pushy 热更新
  • 原文地址:https://www.cnblogs.com/9527s/p/15171910.html
Copyright © 2011-2022 走看看