LeetCode HOT100刷题笔记(十一):二分查找

【如果笔记对你有帮助,欢迎关注&点赞&收藏,收到正反馈会加快更新!谢谢支持!】

ps:笔记和代码按本人理解整理,重思路

题目63: 搜索插入位置 【二分查找 baseline】

35. 搜索插入位置 - 力扣(LeetCode)

  • 题意:找到有序数组中 ≥ target 的第一个位置
  • 拆解:
    「代码逻辑」
    以【前闭后闭】写法为例
    
    左指针:最左端
    右指针:最右端
    
    while 左指针 <= 右指针:
    	中指针:(左+右)//2
    	if 中值 < target:
    		左指针 ← 中指针 + 1 # 取右区间
    	else:  # 中值 >= target
    		右指针 ← 中指针 + 1 # 取右区间
        return 左指针
    		 
  • 代码:
    class Solution:
        def searchInsert(self, nums: List[int], target: int) -> int:
            left = 0
            right = len(nums)-1
            while left <= right:
                mid = (left+right) // 2
                if nums[mid] < target:
                    left = mid + 1
                else:
                    right = mid - 1
            return left

题目64: 搜索二维矩阵

74. 搜索二维矩阵 - 力扣(LeetCode)

  • 题意:找二维矩阵中是否存在target,二维矩阵逐行递增
  • 关键:二分查找变成 ≤ target 的第一个位置
  • 拆解:
    「代码逻辑」
    以【前闭后闭】写法为例
    
    左指针:最左端
    右指针:最右端
    
    binary_search(数组,target):
    	while 左指针 <= 右指针:
    		中指针:(左+右)//2
    		if 中值 <= target:  【变化1】
    			左指针 ← 中指针 + 1 # 取右区间
    		else:  # 中值 >= target
    			右指针 ← 中指针 + 1 # 取右区间
    		return 右指针 【变化2】
    		
    # step 1
    对第一列做 binary_search -> 找到目标行
    对目标行做 binary_search -> 看返回结果是否是target
  • 代码:

    class Solution:
        def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
            def binary_search(nums, target):
                left = 0
                right = len(nums)-1
                while left <= right:
                    mid = (left+right) // 2
                    if nums[mid] <= target:
                        left = mid + 1
                    else:
                        right = mid - 1
                return right
                    
            first_col = [row[0] for row in matrix]
            row_idx = binary_search(first_col, target)
            col_idx = binary_search(matrix[row_idx], target)
            return matrix[row_idx][col_idx] == target 
    

     

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

34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

  • 题意:
  • 关键:
    • 元素的第一个位置:≥ target 的第一个位置 【二分查找代码同 题目63.搜索插入位置】
    • 元素的最后一个位置:≥ (target+1) 的前一个位置
    • 如果第一个位置不存在,最后一个位置一定也不存在
  • 代码:
    class Solution:
        def lower_bound(self, nums, target):
            left = 0
            right = len(nums)-1
            while left <= right:
                mid = (left+right) // 2
                if nums[mid] < target:
                    left = mid + 1
                elif nums[mid] >= target:
                    right = mid - 1
            return left
                     
        def searchRange(self, nums: List[int], target: int) -> List[int]:
            start = self.lower_bound(nums, target)
            if start == len(nums) or nums[start] != target:
                return -1, -1
            end = self.lower_bound(nums, target+1) - 1
            return start, end

题目66: 寻找旋转排序数组中的最小值

153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)

  • 题意:
  • 关键:
    • 找到全局最小而不是局部最小,比如 3 4 5 1 2 中 3是其中一个低谷,1是最低谷
    • target设置为数组的最后一个值,因为要找到“蓝色区域”的第一个值,“蓝色区域” ≤ 数组的最后一个值
  • 代码:
    class Solution:
        def findMin(self, nums: List[int]) -> int:
            left = 0
            right = len(nums)-1
            while left <= right:
                mid = (left+right) // 2
                if nums[mid] > nums[-1]:  
                    left = mid + 1
                else:
                    right = mid - 1              
            return nums[left]
    

题目67: 搜索旋转排序数组

33. 搜索旋转排序数组 - 力扣(LeetCode)

  • 题意:
  • 关键:
    • step 1 找区间(是旋转部分还是未旋转部分) → 找最小值 【同上题】
    • step 2 在区间内再次二分查找找目标
  • 代码:
    class Solution:
        def lower_bound(self, nums, target):
            left = 0
            right = len(nums)-1
            while left <= right:
                mid = (left+right) // 2
                if nums[mid] < target:
                    left = mid + 1
                elif nums[mid] >= target:
                    right = mid - 1  
            return left
    
        def search(self, nums: List[int], target: int) -> int:
            left = 0
            right = len(nums)-1
            
            while left <= right:
                mid = (left+right) // 2
                if nums[mid] > nums[-1]:  
                    left = mid + 1
                else:
                    right = mid - 1 
            
            if (target <= nums[-1]):
                loc = self.lower_bound(nums[left:], target)
                loc = left + loc
            else:
                loc = self.lower_bound(nums[:left], target)
    
            if loc < len(nums) and nums[loc] == target:
                return loc
            else:
                return -1

题目68: 寻找两个正序数组的中位数

4. 寻找两个正序数组的中位数 - 力扣(LeetCode)

  • 题意:给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2,找出并返回这两个正序数组的 中位数 
  • 思路:在两个数组中找到一个划分,使
    • 左边部分 = 右边部分(或左边多一个元素)
    • 左边最大值 ≤ 右边最小值
  • 举例:nums1 = [1, 5, 7, 8, 9] nums2 = [2, 3, 4, 6, 10]
    • 当划分为 [1, 5, 7, 8, 9] 和 [2, 3, 4, 6, 10] 的时候可以找到中位数(红色为左边,蓝色为右边)
    • 总数为偶数时,左边部分 = 右边部分 = (n+m)/2【中位数 = (左边最大 + 右边最大)➗2】;总数为奇数时,左边部分 = 右边部分+ 1 = (n+m+1)/2【中位数 = 左边最大】
  • 拆解:
    「代码逻辑」
    以【前闭后闭】写法为例
    
    - 让nums1成为两个里面更短的数组(减少二分查找计算量)
    
    - 初始化:
    	- 左指针:0;右指针:n # 这里有点不一样,右指针不会真的
    	- 划分后的左边部分长度 = (n + m + 1) // 2
    	while 左指针 <= 右指针:
    		i:中指针(表示左边有多少个元素来自 nums1)
    		j:左边部分长度 - i (表示左边有多少个元素来自 nums2)  
    		# 当 i = 0,表示左边没有来自 nums1 的元素(全从 nums2 取);当 i = n,表示左边全来自 nums1(nums2 不贡献元素)
    		# 左边部分:nums1[0..i-1] + nums2[0..j-1]
    		# 右边部分:nums1[i..n-1] + nums2[j..m-1]
    		if i < n(长度没越界)且 nums1[i] < nums2[j-1]:【说明i取小了】
    			左指针 ← 中指针 + 1
    		elif i > 0(长度没越界)且 nums1[i] > nums2[j-1]:【说明i取大了】
    			右指针 ← 中指针 - 1
    		else:
    			if i 或 j 为 0: 
    				左边最大为另一个nums[j/i - 1]
    			else:
    				左边最大 = max(nums1[i-1], nums2[j-1])
    			
    			if num1+nums2总长为奇数:
    				中位数 = 左边最大 
    			else: # 总长为偶数
    				同理算右边最大
    				中位数 = (左边最大 + 右边最大) / 2.0
    				
  • 代码:
    class Solution:
        def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
            # 确保 nums1 是较短的数组,以减少二分范围
            if len(nums1) > len(nums2):
                nums1, nums2 = nums2, nums1
    
            n, m = len(nums1), len(nums2)
            left, right = 0, n
            half_len = (n + m + 1) // 2  # 划分点
    
            while left <= right:
                i = (left + right) // 2  # nums1 的划分位置
                j = half_len - i         # nums2 的划分位置
    
                # 处理边界情况
                if i < n and nums1[i] < nums2[j-1]:
                    left = i + 1  # i 太小,需增大
                elif i > 0 and nums1[i-1] > nums2[j]:
                    right = i - 1  # i 太大,需减小
                else:
                    # 找到正确划分
                    max_left = float('-inf')
                    if i == 0: # 确保不会越界
                        max_left = nums2[j-1]
                    elif j == 0:
                        max_left = nums1[i-1]
                    else:
                        max_left = max(nums1[i-1], nums2[j-1])
    
                    # 如果是奇数长度,直接返回左半部分最大值
                    if (n + m) % 2 == 1:
                        return max_left
    
                    min_right = float('inf')
                    if i == n:
                        min_right = nums2[j]
                    elif j == m:
                        min_right = nums1[i]
                    else:
                        min_right = min(nums1[i], nums2[j])
    
                    # 偶数长度,返回左右部分的平均值
                    return (max_left + min_right) / 2.0
    
            return 0.0  # 理论上不会执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值