【如果笔记对你有帮助,欢迎关注&点赞&收藏,收到正反馈会加快更新!谢谢支持!】
ps:笔记和代码按本人理解整理,重思路
题目63: 搜索插入位置 【二分查找 baseline】
- 题意:找到有序数组中 ≥ 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: 搜索二维矩阵
- 题意:找二维矩阵中是否存在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: 搜索旋转排序数组
- 题意:
- 关键:
- 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 # 理论上不会执行