@创建于:20211122
文章目录
1、二分法介绍
「二分查找算法(Binary Search Algorithm)」,也叫做 「折半查找算法」、「对数查找算法」。是一种在有序数组中查找某一特定元素的搜索算法。
基本算法思想:先确定待查找元素所在的区间范围,在逐步缩小范围,直到找到元素或找不到该元素为止。
二分法查找的思路如下:
(1)首先,从数组的中间元素开始搜索,如果该元素正好是目标元素,则搜索过程结束,否则执行下一步。
(2)如果目标元素大于/小于中间元素,则在数组大于/小于中间元素的那一半区域查找,然后重复步骤(1)的操作。
(3)如果某一步数组为空,则表示找不到目标元素。
二分法查找的时间复杂度O(logn)。
2、704. 二分查找
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right-left) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
3、35. 搜索插入位置
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
4、374. 猜数字大小
class Solution:
def guessNumber(self, n: int) -> int:
left = 1
right = n
# 注意是<号
while left < right:
mid = left + (right - left) // 2
if guess(mid) <= 0:
# 答案在区间 [left, mid] 中
right = mid
else:
# 答案在区间 [mid+1, right] 中
left = mid + 1
# 此时有 left == right,区间缩为一个点,即为答案
return left
5、69. Sqrt(x)
class Solution:
def mySqrt(self, x: int) -> int:
left = 0
right = x
res = -1
while left <= right:
mid = left + (right - left) // 2
# 在[mid+1, right]中
if mid*mid<=x:
res = mid
left = mid + 1
# 在[left, mid-1]
else:
right = mid - 1
return res
6、167. 两数之和 II - 输入有序数组
使用双指针
- 初始时两个指针分别指向第一个元素位置和最后一个元素的位置。
- 每次计算两个指针指向的两个元素之和,并和目标值比较。
- 如果两个元素之和等于目标值,则发现了唯一解。
- 如果两个元素之和小于目标值,则将左侧指针右移一位。
- 如果两个元素之和大于目标值,则将右侧指针左移一位。
- 移动指针之后,重复上述操作,直到找到答案。
https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/solution/liang-shu-zhi-he-ii-shu-ru-you-xu-shu-zu-by-leet-2/
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
left = 0
right = len(numbers) - 1
while left < right:
total = numbers[left] + numbers[right]
if total == target:
return [left+1, right+1]
elif total < target:
left += 1
else:
right -= 1
return [-1, -1]
7、1011. 在 D 天内送达包裹的能力
- 这艘船最小的运载能力必须是所有货物中最重的那一件max(weights), 否则无法完成运输的任务。
- 这艘船最大的运载能力就是一次性把所有的货都搬走, 就是sum(weights). 所以答案就在区间[max(weights), sum(weights)]之间。
class Solution:
def shipWithinDays(self, weights: List[int], days: int) -> int:
# 最小值得是任何一个货物都可以运走, 不可以分割货物
left = max(weights)
# 最大值是一趟就全部运走, 所以是所有货物之和
right = sum(weights)
while left < right:
mid = (left + right) // 2
# 计算船的最低运载能力为mid时,需要的天数
need = 1
cur = 0
for weight in weights:
if cur + weight > mid:
need += 1
cur = 0
cur += weight
# 如果天数超了, 说明运载能力有待提升, start改大一点, 继续二分搜索
if need > days:
left = mid + 1
# 否则运载能力改小一点继续搜索
else:
right = mid
return left
8、278. 第一个错误的版本
8.1 外层循环体 不取等号 情况解释:
循环结束条件是 left==right,因为本题应该默认一定有错误的版本,返回哪一个都可以,即直接返回 left或者right。但是在二分查找某个数值时,不写等号还要判断 left位置的元素是否等于目标数。
class Solution:
def firstBadVersion(self, n):
"""
:type n: int
:rtype: int
"""
left = 1
right = n
while left < right:
mid = left + (right-left)//2
wrong = isBadVersion(mid)
if not wrong:
left = mid + 1
else:
right = mid
return left
8.2 外层循环体 取等号 情况解释:
利用二分查找求左边界(收缩右边界)
class Solution:
def firstBadVersion(self, n):
"""
:type n: int
:rtype: int
"""
left = 1
right = n
while left <= right:
mid = left + (right-left)//2
wrong = isBadVersion(mid)
if not wrong:
left = mid + 1
else:
right = mid - 1
return left
9、33. 搜索旋转排序数组
二分法的变种,需要预先判断,哪个部分是有序部分。
class Solution:
def search(self, nums: List[int], target: int) -> int:
if not nums:
return -1
left, right = 0, len(nums)-1
while left <= right:
mid = left + (right-left)//2
if nums[mid] == target:
return mid
if nums[0] <= nums[mid]:
if nums[0] <= target and target < nums[mid]:
right = mid - 1
else:
left = mid + 1
else:
if target > nums[mid] and target <= nums[-1]:
left = mid + 1
else:
right = mid - 1
return -1
10、153. 寻找旋转排序数组中的最小值
参考链接
第一种情况是 nums[pivot] < nums[high]。如下图所示,这说明 nums[pivot]是最小值右侧的元素,因此我们可以忽略二分查找区间的右半部分。
第二种情况是 nums[pivot]>nums[high]。如下图所示,这说明 nums[pivot]是最小值左侧的元素,因此我们可以忽略二分查找区间的左半部分。
class Solution:
def findMin(self, nums: List[int]) -> int:
left, right = 0, len(nums)-1
while left < right:
mid = left + (right - left) // 2
if nums[mid] < nums[right]:
right = mid
else:
left = mid + 1
return nums[left]