【规律总结】
1.l<r与l<=r的区别:
中间记录结果用l<=r,最后返回结果用l<r此时l=r
2.什么时候l和r取mid,什么时候都不取mid:
2.1 取mid意思为:移动时l = mid,r=mid+1(或者l=mid+1,r=mid),不取意思为移动时l=mid+1,r=mid-1
2.2 当mid已经确定不可能为所求值或者已经被记录过时不取(如题目【Leetcode-33】、【Leetcode-34】、【Leetcode-35】、【Leetcode-69】);
否则取(如题目【Leetcode-剑指offer11】、【Leetcode-162】、【Leetcode-287】)
3.一般用l<r时取mid,l<=r不取mid
【Leetcode-剑指offer11】
一、题目:旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
注意:包含重复数字
二、代码:
def minArray(self, numbers: List[int]) -> int: """ 比最右边大,则往右走,比最右边小则往左走,相等则右-1。主要关注有重复值的情况 """ if numbers[-1] > numbers[0]: return numbers[0] n = len(numbers) l, r = 0, n-1 while l < r: mid = (l+r) // 2 if numbers[mid] > numbers[r]: l = mid + 1 elif numbers[mid] < numbers[r]: r = mid else: r -= 1 return numbers[l]
【Leetcode-33】
一、题目:搜索旋转排序数组
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的索引,否则返回 -1 。
二、代码:
class Solution: def search(self, nums: List[int], target: int) -> int: index = -1 if len(nums) == 0: return index l, r = 0, len(nums)-1 while l <= r: middle = (l+r)//2 if nums[middle] == target: return middle elif nums[middle] >= nums[0]: # 左升序 if nums[l] <= target < nums[middle]: r = middle - 1 else: l = middle + 1 else: # 右升序 if nums[middle] < target <= nums[r]: l = middle + 1 else: r = middle - 1 return index
【Leetcode-34】
一、题目:在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
二、代码:
class Solution: def searchRange(self, nums: List[int], target: int) -> List[int]: res = [-1, -1] if len(nums) == 0: return res def find(nums, target, low): n = len(nums) l, r = 0, n - 1 pos = n while l <= r: m = (l+r)//2 if nums[m] > target or (low is True and nums[m] >= target): r = m - 1 pos = m # 第一个t或者>t的第一个 else: l = m + 1 return pos left_index = find(nums, target, True) right_index = find(nums, target, False) - 1 n = len(nums) if (left_index <= right_index and right_index <= n-1 and nums[right_index] == target): return [left_index, right_index] return res
【Leetcode-35】
一、题目:搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
二、代码:
def searchInsert(self, nums: List[int], target: int) -> int: """ 二分法找第一个>=target的位置 """ l, r = 0, len(nums) - 1 res = -1 while l <= r: mid = (l+r)// 2 if nums[mid] >= target: res = mid r = mid - 1 else: l = mid + 1 if res == -1: # 没找到>=target的,说明target应该插入在最后 return len(nums) else: return res
【Leetcode-69】
一、题目:x的平方根
实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
二、代码:
def mySqrt(self, x: int) -> int: """ 二分法,找第一个平方<=x的 """ l, r = 0, x res = 0 while l <= r: mid = (l+r) // 2 s = mid * mid if s <= x: res = mid l = mid + 1 else: r = mid - 1 return res
【Leetcode-162】
一、题目:寻找峰值
峰值元素是指其值大于左右相邻值的元素。
给你一个输入数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
二、代码:
def findPeakElement(self, nums: List[int]) -> int: """ 二分法,一定可以挤出一个峰值,峰值即最大值 """ n = len(nums) l, r = 0, n-1 while l < r: mid = (l+r) // 2 if nums[mid] < nums[mid+1]: l = mid + 1 else: r = mid return l
【Leetcode-287】
一、题目:寻找重复数
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。
二、代码:
def findDuplicate(self, nums: List[int]) -> int: """ 二分法:随意猜一个数mid,统计<=该数的数量cnt,如果cnt>mid,说明数在[left, mid] """ n = len(nums) l, r = 1, n-1 while l < r: mid = (l+r)//2 cnt = 0 for item in nums: if item <= mid: cnt += 1 if cnt > mid: r = mid else: l = mid + 1 return l
【Leetcode-378】
一、题目:有序矩阵中第K小的元素
给你一个 n x n 矩阵 matrix ,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是 排序后 的第 k 小元素,而不是第 k 个 不同 的元素。
二、代码:
def kthSmallest(self, matrix: List[List[int]], k: int) -> int: """ 二分法的扩展,左上角为最小值,右下角为最大值,找个mid,统计<=mid的值,判断与k的关系,确定往哪走 1.若<=mid的数目<k,显然需要从mid+1开始找
2.该方法只适应于数组元素都是整数!!! """ def get_cnt(mid_val): cnt = 0 i, j = 0, n-1 # 右上角开始二分查找 while i <= m-1 and j >= 0: if matrix[i][j] <= mid_val: # 这个点的这一行左边都小于 cnt += j+1 # 0~j有j+1个元素 i += 1 # 往下一行找 else: j -= 1 # 往左找 return cnt m, n = len(matrix), len(matrix[0]) if k > m*n: return l = matrix[0][0] # 非下标而是元素,因为二位矩阵的下标不能代表元素大小 r = matrix[m-1][n-1] while l < r: mid_val = (l + r) // 2 cnt = get_cnt(mid_val) # 计算小于mid_val的有多少个 if cnt < k: # 这个值太小了,加上等于的都不够,需要往大了找 l = mid_val + 1 else: # 即使等于k也无法确定是哪个,还需要在小堆中在找 r = mid_val return l