二分查找

2020/9/29

69 x 的平方根
class Solution:
    def mySqrt(self, x: int) -> int:
        if(x<2):
            return x
        l=0
        h=x
        while(l<=h):
            mid=int((l+h)/2)
            #print(mid)
            if(mid*mid==x or mid==l):
                return mid
            if mid*mid<x:
                l=mid
            else:
                h=mid

2020/10/2

744. 寻找比目标字母大的最小字母
  • 记得low+1 不然可能一直循环
  • 找不到就返回第一个元素
class Solution:
    def nextGreatestLetter(self, letters: List[str], target: str) -> str:
        l=0
        h=len(letters)-1
        while(l<h):
            mid=int((l+h)/2)
            if(letters[mid]>target):
                h=mid
            else:
                l=mid+1 #不+1可能一直循环
                #如果最后一个元素都小于= target 就返回首元素
        return letters[l] if letters[l]>target else letters[0]
540. 有序数组中的单一元素

注意一直确定mid为偶数

class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
        l=0
        h=len(nums)-1
        if(h==0):
            return nums[0]
        while(l<h):
            mid=int((l+h)/2)
            mid=mid-1 if mid%2==1 else mid
            if(nums[mid]==nums[mid+1]):
                l=mid+2
            else:
                h=mid
        return nums[l]
278. 第一个错误的版本
# The isBadVersion API is already defined for you.
# @param version, an integer
# @return a bool
# def isBadVersion(version):

class Solution:
    def firstBadVersion(self, n):
        """
        :type n: int
        :rtype: int
        """
        l=1
        h=n
        while(l<h):
            mid=int((l+h)/2)
            if(isBadVersion(mid)):
                h=mid
            else:
                l=mid+1
        return l
153. 寻找旋转排序数组中的最小值

大多时候都要 l=mid+1 不然容易死循环

class Solution:
    def findMin(self, nums: List[int]) -> int:
        l=0
        h=len(nums)-1
        while(l<h):
            mid=int((l+h)/2)
            if(nums[mid]>nums[h]):
                l=mid+1
            else:
                h=mid
        return nums[l]
34. 在排序数组中查找元素的第一个和最后一个位置

注意空数组

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if(not nums):
            return [-1,-1]
        l=0
        h=len(nums)-1   
        start=-1
        end=-1
        while(l<h):
            mid=int((l+h)/2)
            if(nums[mid]<target):
                l=mid+1
            else:
                h=mid
        index=l
        if(nums[index]==target):
            start,end=index,index
            while(start-1>=0 and nums[start-1]==nums[start]):
                start-=1
            while(end+1<len(nums) and nums[end+1]==nums[end]):
                end+=1
        return [start,end]

官方解法 两次二分查找 找到第一个大于等于target的index

class Solution:
    #多少个数小于target 或者说第一个大于等于target的index
    def extreme_insertion_index(self, nums, target):
        lo = 0
        hi = len(nums)#注意 因为返回值有可能是数组长度

        while lo < hi:
            mid = (lo + hi) // 2
            if nums[mid] >= target : #注意加等于
                hi = mid
            else:
                lo = mid+1#注意+1

        return lo


    def searchRange(self, nums, target):
        left_idx = self.extreme_insertion_index(nums, target)
        #对应 target大于所有数 和 第一个大于等于target的数 不等于target
        if left_idx == len(nums) or nums[left_idx] != target:
            return [-1, -1]

        return [left_idx, self.extreme_insertion_index(nums, target+1)-1]

解析二分查找 边界情况
https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/solution/er-fen-cha-zhao-suan-fa-xi-jie-xiang-jie-by-labula/

2021/6/1

边界+1注意 一定要left+1 right-1避免死循环

注意边界条件,比如要找升序数组中第一个小于target的
注意l=mid还是l=mid+1

  • l=mid+1,最后的nums[h]是小于等于target的,因为nums[mid]虽然<target,但是l=mid+1,nums[mid+1]有可能=target
  • l=mid,最后的nums[h]肯定小于target,或者极端情况下h=-1;h就是小于target的第一个位置;因为l=mid强制了Nums[l]是小于target的,但是这种情况下肯能会导致 l=h-1的情况下 死循环 无法终止
    # 找第一个小于target的
    def findLeft(self,nums,target):
        l=0
        h=len(nums)-1
        # 从high往low找
        while(l<h):
            mid=(l+h)//2
            if(nums[mid]>=target):
                h=mid-1
            else:
                l=mid+1 # l=mid+1的情况下 nums[l]可能=target 所以h也可能等于target
        # h此时是第一个小于target的 但是也有可能=target
        return h
33. 搜索旋转排序数组

两部分有序数组,关键就是判断target在前半段还是后半段以及mid在前半段还是后半段

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if(len(nums)==0):
            return -1
        # nums[-1]>=target 说明在后半段
        # nums[-1]<target 说明在前半段
        right_flag=nums[-1]>=target # true target在后半段

        left=0
        right=len(nums)-1
        while(left<=right):
            mid=left+(right-left)//2
            if(nums[mid]==target):
                return mid
            
            # mid在前半段
            if(nums[mid]>nums[-1]):
                if(right_flag):
                    left=mid+1
                # target在前半段
                else:
                    if(nums[mid]>target):
                        right=mid-1
                    else:
                        left=mid+1
            # mid在后半段
            else:
                # target在前半段
                if(not right_flag):
                    right=mid-1
                else:
                    if(nums[mid]>target):
                        right=mid-1
                    else:
                        left=mid+1
        return -1   
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        if(len(nums)==0):
            return -1
        # nums[-1]>=target 说明在后半段
        # nums[-1]<target 说明在前半段
        right_flag=nums[-1]>=target # true target在后半段

        left=0
        right=len(nums)-1
        while(left<=right):
            mid=left+(right-left)//2
            if(nums[mid]==target):
                return mid
            # 1 mid target在同一段 正常搜索
            if((right_flag and nums[mid]<=nums[-1]) or (not right_flag and nums[mid]>nums[-1])):
                if(nums[mid]>target):
                    right=mid-1
                else:
                    left=mid+1
            # 2 mid target不在同一段 分情况
            else:
                # 2-1 target 后半段
                if(right_flag):
                    left=mid+1
                # 2-2 target 前半段
                else:
                    right=mid-1

        return -1   
74. 搜索二维矩阵
  • 1 先二分查找所在的行
  • 2 再二分查找具体位置

也可以当作一个升序数组,直接对数组的Index映射成martix的坐标

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        if(not matrix or not matrix[0]):
            return False
        # 1 先二分查找所在的行
        # 2 再二分查找具体位置
        row=len(matrix)
        col=len(matrix[0])
        left=0
        right=row-1
        if(matrix[0][0]>target or matrix[row-1][col-1]<target):
            return False
        while(left<=right):
            mid=left+(right-left)//2
            if(matrix[mid][0]<=target and matrix[mid][col-1]>=target):
                # # 考虑这一列都等于target时 left+1
                # if(matrix[mid][0]==matrix[mid][col-1]):
                # left+=1
                left=mid
                break
            elif(matrix[mid][0]>target):
                right=mid-1
            else:
                left=mid+1
        # target在left这一row
        cur_row=left
        left=0
        right=col-1
        while(left<=right):
            mid=left+(right-left)//2
            if(matrix[cur_row][mid]==target):
                return True
            elif(matrix[cur_row][mid]>target):
                right=mid-1
            else:
                left=mid+1

        return False
81. 搜索旋转排序数组 II
  • 关键是包含重复元素 ,重复元素可能即在左,又在右,当nums[left]=nums[mid]=nums[right]时无法区分到底下一步去前半部分还是后半部分(left+1 right-1 继续判断)
  • 以及target也要随时判断在左还是右 (因为left right是可变的不是fix)

不过个人感觉不如将首元素和尾元素相等一直剔除 再用老方法fix left right,更加快

class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        if(not nums):
            return False
        # 判断target和mid在不在同一段
        left=0
        right=len(nums)-1
        while(left<=right):
            mid=left+(right-left)//2
            if(nums[mid]==target):
                return True
            # 此时无法确定mid在左边还是右边
            if(nums[mid]==nums[left]==nums[right]):
                left+=1
                right-=1
                continue
            # mid在左边
            if(nums[mid]>=nums[left]):     
                # 1 target 在左边 
                    # 1-1 target<mid right=mid-1 
                    # 1-2 target>mid left=mid+1
                # 2 target 在右边 left=mid+1
                if(nums[mid]>target and target>=nums[left]):
                    right=mid-1
                else:
                    left=mid+1
            # mid在右边
            else:
                # 1 target 在右边
                    # 1-1 target<mid right=mid-1
                    # 1-2 target>mid left=mid+1
                # 2 target 在左边 right=mid-1
                if(nums[mid]<target and target<=nums[right]):
                    left=mid+1
                else:
                    right=mid-1
        
        return False
       

2021/6/2

50. Pow(x, n)

递归空间复杂度 o(logn),还可以写成二进制思想的计算,空间复杂度降为o(1),但是不太能看懂思路

class Solution:
    def myPow(self, x: float, n: int) -> float:
        # 负号
        # 奇数偶数
        ret=1.0
        if(n>=0):
            return self.pow(x,n)
        else:
            return 1/self.pow(x,-n)
            
    
    def pow(self,x,n):
        if(n==1):
            return x
        if(n==0):
            return 1
        # n为偶数
        tmp=self.pow(x,n//2)
        if(n%2==0):
            return tmp*tmp
        else:
            return tmp*tmp*x
162. 寻找峰值

这个题只要想明白 -inf和-inf之间一定有峰值,那么只要mid>mid+1,左边一定有峰值;mid<mid+1,右边一定有峰值
另外 迭代比递归省空间

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        left=0
        right=len(nums)-1
        # 所以只要出现 mid>mid+1 左边必定有峰值
        while(left<=right):
            if(left==right):
                return left
            mid=left+(right-left)//2
            # 只要出现 mid>mid+1 左边必定有峰值
            if(nums[mid]>nums[mid+1]):
                right=mid
            else:
                left=mid+1

2021/6/3

4. 寻找两个正序数组的中位数

先写一个o(m+n)的思路

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        len1,len2=len(nums1),len(nums2)
        mid=(len1+len2)//2
        p1=0
        p2=0
        num1,num2=0,0
        # 奇数走到第mid个数
        # 偶数走到mid mid-1
        for i in range(0,mid+1):
            num1=num2
            if(p1==len1):
                num2=nums2[p2]
                p2+=1
            elif(p2==len2):
                num2=nums1[p1]
                p1+=1
            else:
                if(nums1[p1]<nums2[p2]):
                    num2=nums1[p1]
                    p1+=1
                else:
                    num2=nums2[p2]
                    p2+=1
        # print(num1)
        # print(num2)
        if((len1+len2)%2==0):
            return (num1+num2)/2
        else:
            return num2
154. 寻找旋转排序数组中的最小值 II
class Solution:
    def findMin(self, nums: List[int]) -> int:
        if(nums[-1]>nums[0]):
            return nums[0]
        left=0
        right=len(nums)-1
        while(left<=right):
            mid=(left+right)//2
            if(nums[left]==nums[mid]==nums[right]):
                left+=1
                right-=1
                continue      
            if(nums[right]>=nums[mid]):
                right=mid
            else:
                left=mid+1
  
        return nums[mid]
209. 长度最小的子数组

双指针

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        if(nums[0]>=target):
            return 1
        if(len(nums)==1):
            return 0
        # 双指针
        ret=0
        p1=0
        p2=1
        cur_sum=nums[0]+nums[1]
        # 直到p2滑动到末尾
        while(p2<len(nums)):
            # 超出最大值时 p1右走
            if(cur_sum>=target):
                ret=min(ret,p2-p1+1) if ret>0 else p2-p1+1
                cur_sum-=nums[p1]
                p1+=1
            else:
                p2+=1
                if(p2==len(nums)):
                    break
                cur_sum+=nums[p2]

        return ret

官方给的双指针更加简洁

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        if(not nums):
            return 0
        ret=0
        p1=0
        p2=0
        cur_sum=0
        # 直到p2滑动到末尾
        while(p2<len(nums)):
            cur_sum+=nums[p2]
            # 1 cur_sum大于等于target
            while(cur_sum>=target): # 不用考虑p1<=p2 因为相等时 cursum=0
                ret=min(ret,p2-p1+1) if ret>0 else p2-p1+1
                if(ret==1):
                    return 1
                cur_sum-=nums[p1]
                p1+=1
            # 2 直到cur_sum<target
            p2+=1

        return ret
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值