二分查找(一)

704. 二分查找

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        # 基础二分查找
        low = 0
        high = len(nums) - 1
        while low <= high:
            mid = low + (high-low) // 2
            if nums[mid] > target:
                high = mid - 1
            elif nums[mid] < target:
                low = mid + 1
            else:
                return mid
        return -1

744. 寻找比目标字母大的最小字母

class Solution:
    def nextGreatestLetter(self, letters: List[str], target: str) -> str:
        if target >= letters[-1]:
            return letters[0]
        low = 0
        high = len(letters) - 1
        while low <= high:
            mid = low + (high - low) // 2
            if letters[mid] > target:
                # 判断是不是第一个
                if mid == 0 or letters[mid-1] <= target:
                    return letters[mid]
                high = mid - 1
            else:
                low = mid + 1
        return

33. 搜索旋转排序数组

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        # 二分查找,以 mid 分界,一定有一半是有序数组
        size = len(nums)
        l, r = 0, size - 1
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] == target:
                return mid
            # 判断左边是否是有序数组
            if nums[l] <= nums[mid]:
                if nums[l] <= target and target <= nums[mid]:
                    r = mid - 1
                else:
                    l = mid + 1
            # 右边有序
            else:
                if nums[mid] <= target and target <= nums[r]:
                    l = mid + 1
                else:
                    r = mid -1
        return -1

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

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        # 二分查找目标值第一个和最后一个
        n = len(nums)
        l, r = 0, n-1
        res = [-1, -1]
        # 查找起始位置
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] < target:
                l = mid + 1
            elif nums[mid] > target:
                r = mid -1
            else:
                # 注意需要判断 mid == 0
                if mid == 0 or nums[mid] > nums[mid - 1]:
                    res[0] = mid
                    break
                else:
                    r = mid - 1
        # 查找结束位置
        l, r = 0, n-1
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] < target:
                l = mid + 1
            elif nums[mid] > target:
                r = mid -1
            else:
                # 注意需要判断 mid == n-1
                if mid == n-1 or nums[mid+1] > nums[mid]:
                    res[1] = mid
                    break
                else:
                    l = mid + 1
        return res
                

35. 搜索插入位置

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        n = len(nums)
        l, r = 0, n-1
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] > target:
                r = mid - 1
            else:
                l = mid + 1
        return l
            

69. x 的平方根

class Solution:
    def mySqrt(self, x: int) -> int:
        # 二分查找
        l, r = 0, x
        while l <= r:
            mid = l + (r - l) // 2
            tmp = mid * mid
            if tmp == x:
                return mid
            elif tmp > x:
                r = mid - 1
            else:
                l = mid + 1
        return l - 1

74. 搜索二维矩阵

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        m = len(matrix)
        n = len(matrix[0])
        # # 两次二分查找
        # col = [i[0] for i in matrix]
        # l, r = 0, m-1
        # while l <= r:
        #     mid = l + (r - l) // 2
        #     if col[mid] == target:
        #         return True
        #     elif col[mid] > target:
        #         r = mid - 1
        #     else:
        #         l = mid + 1
        
        # row_index = l - 1

        # l, r = 0, n-1
        # while l <= r:
        #     mid = l + (r - l) // 2
        #     if matrix[row_index][mid] == target:
        #         return True
        #     elif matrix[row_index][mid] > target:
        #         r = mid -1
        #     else:
        #         l = mid + 1
        
        # return False

        # 一次二分查找
        l, r = 0, m * n -1
        while l <= r:
            mid = l + (r - l) // 2
            # mid 转换为二维下标
            tmp = matrix[mid // n][mid % n]
            if tmp == target:
                return True
            elif tmp > target:
                r = mid -1
            else:
                l = mid + 1
        return False

81. 搜索旋转排序数组 II

class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        # 二分查找
        # 对比「搜索旋转排序数组」题目,可能会出现 nums[l] = nums[mid] = nums[r] 的情况,只能 l++ & r-- 排除干扰项继续查找
        n = len(nums)
        l, r = 0, n-1
        while l <= r:
            mid = l + (r-l) // 2
            if nums[mid] == target:
                return True
            # 这里分开排除干扰项
            elif nums[l] == nums[mid]:
                l += 1
            elif nums[r] == nums[mid]:
                r -= 1
            # 左边有序
            elif nums[l] < nums[mid]:
                if nums[l] <= target and target <= nums[mid]:
                    r = mid - 1
                else:
                    l = mid + 1
            # 右边有序
            else:
                if nums[mid] <= target and target <= nums[r]:
                    l = mid + 1
                else:
                    r = mid - 1
        return False

153. 寻找旋转排序数组中的最小值

class Solution:
    def findMin(self, nums: List[int]) -> int:
        print(nums)
        n = len(nums)
        l, r = 0, n-1
        # while l <= r:
        #     mid = l + (r - l) // 2
            # if r - l == 1:
            #     return min(nums[l], nums[r])
            # # 整体有序
            # if nums[l] <= nums[mid] and nums[mid] <= nums[r]:
            #     return nums[l]
            # # 左边有序
            # if nums[l] < nums[mid]:
            #     l = mid
            # # 右边有序
            # else:
            #     r = mid
        while l < r:
            mid = l + (r - l) // 2
            if nums[mid] > nums[r]:
                l = mid + 1
            else:
                r = mid
        return nums[l]

154. 寻找旋转排序数组中的最小值 2

class Solution:
    def findMin(self, nums: List[int]) -> int:
        # 和 left 判断
        # 当left到right为非递减数组,此时跟left比较,按理说(旋转数组的情况——左子列非递减,右子列非递减,中间最小值)是「最小值在左边子列」的情况(right应该移到mid)
        # 但是因为跟left比的时候如果nums[mid]大于nums[left],直接判断「最小值在右子列」(旋转数组:最小值在右子列,在mid的右边),与实际情况(非递减数组:最小值在最左边,在mid的左边)不符
        # 所以每次遇到 nums[mid]>nums[left] 的情况,需要提前判断是否整体有序。
        # 而且最后剩余两个元素的情况下,如果left处数小于right处,mid为left,与left相等这时候按理应该left++(错失最小值),所以遇到nums[mid]==nums[left]的情况也要分别处理。
        
        # 如果跟右边界right比较,上述情况均不存在
        # 分别考虑:nums[mid]>nums[right],mid一定位于左子列;
        # nums[mid]<nums[right],mid一定位于右子列;
        # nums[mid]==nums[right],即使只剩两个数,因为mid是left,所以还是两个数比较大小。很简单纯粹的三种情况。
        n = len(nums)
        l, r = 0, n-1
        while l <= r:
            mid = l + (r-l) // 2
            # 整体有序(需要提前判断)
            if nums[l] < nums[r]:
                return nums[l]
            # 左边有序
            if nums[mid] > nums[l]:
                l = mid + 1
            # 右边有序 不能排除 mid
            elif nums[mid] < nums[l]:
                r = mid
            # 如果相等,可忽略 left,有 mid 代替
            else:
                l += 1
        return nums[l-1]





class Solution:
    def findMin(self, nums: List[int]) -> int:
        # 和 right 判断
        n = len(nums)
        l, r = 0, n-1
        while l < r:
            mid = l + (r - l) // 2
            if nums[mid] > nums[r]:
                l = mid + 1
            elif nums[mid] < nums[r]:
                r = mid
            # 如果相等,可忽略 nums[r],有 mid 替代
            else:
                r -= 1
        return nums[l]

162. 寻找峰值

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        n = len(nums)
        l, r = 0, n-1
        # while l < r:
        #     mid = l + (r - l) // 2
        #     if mid == n-1 or nums[mid] >= nums[mid+1]:
        #         r = mid
        #     else:
        #         l = mid + 1
        while l <= r:
            mid = l + (r - l) // 2
            if mid == n-1 or nums[mid] >= nums[mid+1]:
                r = mid - 1
            else:
                l = mid + 1
        return l

167. 两数之和 II - 输入有序数组

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        # 二分查找 时间复杂度 O(nlogn)
        n = len(numbers)
        # index1, index2 = -1, -1

        # def find(i, ans):
        #     l, r = i+1, n-1
        #     while l <= r:
        #         mid = l + (r - l) // 2
        #         if numbers[mid] == ans:
        #             return mid
        #         elif numbers[mid] > ans:
        #             r = mid - 1
        #         else:
        #             l = mid + 1
        #     return -1

        # for index1 in range(n):
        #     ans = target - numbers[index1]
        #     index2 = find(index1, ans)
        #     if index2 > 0:
        #         return [index1+1, index2+1]
        
        # 双指针
        i, j = 0, n-1
        while i < j:
            if numbers[i] + numbers[j] == target:
                return [i+1, j+1]
            elif numbers[i] + numbers[j] > target:
                j -= 1
            else:
                i+= 1

209. 长度最小的子数组

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # # 滑动窗口 O(n)
        # # 判断滑动窗口sum 是否大于 target,如果大于 target,说明满足条件,更新 res,同时缩小窗口继续判断
        # # 如果小于 target,右侧扩展窗口继续判断
        # n = len(nums)
        # i, j = 0, 0
        # total = 0
        # res = n+1
        # while j < n:
        #     total += nums[j]
        #     # 窗口满足条件,更新 res,缩窄左侧继续判断
        #     while total >= target:
        #         res = min(res, j-i+1)
        #         total -= nums[i]
        #         i += 1
        #     # 不满足条件
        #     j += 1
        # return res if res != n+1 else 0

        # 二分查找
        # 利用前缀和递增,对每个下标 i,二分查找 找到最小的 j 满足 sum[j]-sum[i-1] >= target
        n = len(nums)
        sums = [0 for _ in range(n)]
        sums[0] = nums[0]
        for i in range(1, n):
            sums[i] = sums[i-1] + nums[i]

        # 对前缀和二分查找,找到最小的 j 满足 sum[j]-sum[i-1] >= target
        def find(i):
            l, r = i, n-1
            while l <= r:
                mid = l + (r-l) // 2
                if sums[mid] - (sums[i-1] if i > 0 else 0) >= target:
                    r = mid - 1
                else:
                    l = mid + 1
            return l
        
        res = n+1
        for i in range(n):
            j = find(i)
            print(i, j)
            if j < n:
                res = min(res, j-i+1)
        return res if res != n+1 else 0

240. 搜索二维矩阵 II

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        # # 二分查找
        # # 遍历每一行,对每一行单独进行二分查找 O(nlogn)
        # m = len(matrix)
        # n = len(matrix[0])

        # def exist(target, mat):
        #     l, r = 0, len(mat)-1
        #     while l <= r:
        #         mid = l + (r-l) // 2
        #         if mat[mid] == target:
        #             return True
        #         elif mat[mid] > target:
        #             r = mid -1
        #         else:
        #             l = mid + 1
        #     return False

        # for i in range(m):
        #     mat = matrix[i]
        #     if exist(target, mat):
        #         return True
        # return False

        # Z 字形查找,从矩阵右上角开始查找,如果大于 target,j--,否则 i++
        # O(m+n)
        m, n = len(matrix), len(matrix[0])
        i, j = 0, n-1
        while i < m and j >= 0:
            if matrix[i][j] == target:
                return True
            elif matrix[i][j] > target:
                j -= 1
            else:
                i += 1
        return False
        

275. H 指数 II

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值