前言
题单LeetCode 热题 100的二分查找部分,给出解析。
35. Search Insert Position
题目描述
Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You must write an algorithm with O(log n)
runtime complexity.
Example 1:
1 | Input: nums = [1,3,5,6], target = 5 |
Example 2:
1 | Input: nums = [1,3,5,6], target = 2 |
Example 3:
1 | Input: nums = [1,3,5,6], target = 7 |
Constraints:
1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
nums
contains distinct values sorted in ascending order.-10^4 <= target <= 10^4
解题方法
排序数组找目标值,优先考虑二分查找。
初始化两个指针,一个指针指向头部,一个指针指向尾部,每一次判断两个指针中间的值,比目标值大则调整尾指针,比目标值小则调整头指针。
代码实现
1 | class Solution: |
复杂度分析
时间复杂度:$O(logn)$
空间复杂度:$O(1)$
74. Search a 2D Matrix
题目描述
You are given an m x n
integer matrix matrix
with the following two properties:
- Each row is sorted in non-decreasing order.
- The first integer of each row is greater than the last integer of the previous row.
Given an integer target
, return true
if target
is in matrix
or false
otherwise.
You must write a solution in O(log(m * n))
time complexity.
Example 1:
1 | Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 |
Example 2:
1 | Input: matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 |
Constraints:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-10^4 <= matrix[i][j], target <= 10^4
解题方法
这个问题可以通过二分查找来高效解决。由于矩阵每一行都是按非严格递增排列,并且每一行的第一个元素大于前一行的最后一个元素,可以把整个矩阵看成一个有序的数组。通过在这个一维有序数组中进行二分查找,就能有效地找到目标值。
思路:
- 将矩阵看成一个一维有序数组,通过二分查找来查找目标元素。
- 将矩阵的下标转换为一维数组的下标。假设矩阵的大小是
m x n
,那么位置(i, j)
对应一维数组中的索引为i * n + j
。 - 利用二分查找来判断目标值是否存在。
代码实现
1 | class Solution: |
复杂度分析
时间复杂度:$O(log(m×n))$
空间复杂度:$O(1)$
34. Find First and Last Position of Element in Sorted Array
题目描述
Given an array of integers nums
sorted in non-decreasing order, find the starting and ending position of a given target
value.
If target
is not found in the array, return [-1, -1]
.
You must write an algorithm with O(log n)
runtime complexity.
Example 1:
1 | Input: nums = [5,7,7,8,8,10], target = 8 |
Example 2:
1 | Input: nums = [5,7,7,8,8,10], target = 6 |
Example 3:
1 | Input: nums = [], target = 0 |
Constraints:
0 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9
nums
is a non-decreasing array.-10^9 <= target <= 10^9
解题方法
要在有序数组中找到指定值 target
的起始和结束位置,并保证算法的时间复杂度为 $O(log n)$,可以使用二分查找(Binary Search)的方法。
思路如下:
- 二分查找左边界:通过二分查找找到
target
第一次出现的位置。 - 二分查找右边界:通过二分查找找到
target
最后一次出现的位置。
代码实现
1 | def findLeft(nums, target): |
findLeft
函数:
目标:找到
target
在数组中第一次出现的位置,即左边界。过程:
- 初始化两个指针,
left
指向数组的起始位置,right
指向数组的末尾。 - 进入一个
while
循环,条件是left <= right
,表示当搜索区间还存在时继续执行。 - 计算
mid
,即中间位置的索引:mid = (left + right) // 2
。 - 判断
- 如果
nums[mid] < target
:说明target
在mid
右边,因此移动left
指针到mid + 1
,排除左半部分。 - 否则,说明
target
可能在mid
处或者左边,因此移动right
指针到mid - 1
,缩小搜索范围。
- 如果
- 循环结束时,
left
指向的是第一个大于等于target
的元素位置。
- 初始化两个指针,
findRight
函数:
目标:找到
target
在数组中最后一次出现的位置,即右边界。过程:
- 与
findLeft
类似,初始化left
和right
指针。 - 进入
while
循环,条件是left <= right
。 - 计算
mid
的位置:mid = (left + right) // 2
。 - 判断
- 如果
nums[mid] <= target
:说明target
可能在mid
处或者右边,因此移动left
指针到mid + 1
,排除左半部分。 - 否则,
target
在mid
左边,因此移动right
指针到mid - 1
。
- 如果
- 循环结束时,
right
指向的是最后一个小于等于target
的元素位置。
- 与
总结:
findLeft
函数的作用是找到target
的左边界,它通过不断缩小搜索区间,最终返回第一个>= target
的位置。findRight
函数的作用是找到target
的右边界,它通过类似的方法,最终返回最后一个<= target
的位置。
复杂度分析
时间复杂度:$O(logn)$
空间复杂度:$O(1)$
33. Search in Rotated Sorted Array
题目描述
There is an integer array nums
sorted in ascending order (with distinct values).
Prior to being passed to your function, nums
is possibly rotated at an unknown pivot index k
(1 <= k < nums.length
) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
( 0-indexed ). For example, [0,1,2,4,5,6,7]
might be rotated at pivot index 3
and become [4,5,6,7,0,1,2]
.
Given the array nums
after the possible rotation and an integer target
, return the index of target
if it is in nums
, or -1
if it is not in nums
.
You must write an algorithm with O(log n)
runtime complexity.
Example 1:
1 | Input: nums = [4,5,6,7,0,1,2], target = 0 |
Example 2:
1 | Input: nums = [4,5,6,7,0,1,2], target = 3 |
Example 3:
1 | Input: nums = [1], target = 0 |
Constraints:
1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
- All values of
nums
are unique . nums
is an ascending array that is possibly rotated.-10^4 <= target <= 10^4
解题方法
这个问题是一个典型的「旋转排序数组」查找问题。我们需要在一个旋转过的有序数组中使用二分查找方法查找目标值,时间复杂度要求是 $O(log n)$。为了实现这一点,我们可以利用旋转数组的特性,通过判断数组的左右部分是否有序来缩小查找范围。
思路:
- 数组被旋转后,数组的一部分是有序的,另一部分也是有序的。我们可以利用这个性质进行二分查找。
- 在每次二分查找时,判断中间元素和左右端点的关系:
- 如果左半部分有序,判断目标值是否在左半部分的范围内。
- 如果右半部分有序,判断目标值是否在右半部分的范围内。
通过这种方式,我们可以在 $O(log n)$ 时间内找到目标值。
算法步骤:
- 初始化两个指针,
left
和right
,分别指向数组的左右两端。 - 进入循环,当
left
小于等于right
时:- 计算中间位置
mid = (left + right) // 2
。 - 如果
nums[mid] == target
,返回mid
。 - 如果左半部分有序,即
nums[left] <= nums[mid]
:- 如果
target
在左半部分范围内,即nums[left] <= target < nums[mid]
,则将right = mid - 1
,否则将left = mid + 1
。
- 如果
- 如果右半部分有序,即
nums[mid] <= nums[right]
:- 如果
target
在右半部分范围内,即nums[mid] < target <= nums[right]
,则将left = mid + 1
,否则将right = mid - 1
。
- 如果
- 计算中间位置
- 如果循环结束后仍然没有找到目标,返回 -1。
代码实现
1 | class Solution: |
复杂度分析
时间复杂度:$O(logn)$
空间复杂度:$O(1)$
153. Find Minimum in Rotated Sorted Array
题目描述
Suppose an array of length n
sorted in ascending order is rotated between 1
and n
times. For example, the array nums = [0,1,2,4,5,6,7]
might become:
[4,5,6,7,0,1,2]
if it was rotated4
times.[0,1,2,4,5,6,7]
if it was rotated7
times.
Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]]
1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]]
.
Given the sorted rotated array nums
of unique elements, return the minimum element of this array.
You must write an algorithm that runs in O(log n) time
.
Example 1:
1 | Input: nums = [3,4,5,1,2] |
Example 2:
1 | Input: nums = [4,5,6,7,0,1,2] |
Example 3:
1 | Input: nums = [11,13,15,17] |
Constraints:
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
- All the integers of
nums
are unique . nums
is sorted and rotated between1
andn
times.
解题方法
这道题的目的是在一个经过旋转的升序数组中找到最小元素,且要求时间复杂度为 $O(log n)$。我们可以使用 二分查找来解决这个问题,因为旋转后的数组依然保持着部分有序的性质。
思路:
- 给定一个数组
nums
,它是一个升序数组经过旋转后的结果。数组的最小值就是旋转点所在的位置。 - 旋转后的数组将会分为两个部分:一部分是升序的,另一部分也是升序的。旋转后,数组的最小值总是在两个部分的交界处。
- 利用二分查找,可以根据当前中间元素与数组的左右边界元素的大小关系来判断最小值的所在区间。
算法步骤:
- 设定两个指针
left
和right
,分别指向数组的开始和结束。 - 计算中间元素
mid = (left + right) // 2
。 - 如果中间元素大于右边的元素
nums[mid] > nums[right]
,说明最小值在mid
右边,此时left = mid + 1
。 - 如果中间元素小于等于右边的元素
nums[mid] <= nums[right]
,说明最小值在mid
左边或就是mid
本身,此时right = mid
。 - 循环直到
left
等于right
,此时nums[left]
就是最小元素。
代码实现
1 | class Solution: |
复杂度分析
时间复杂度:$O(log n)$,因为每次二分查找将搜索区间缩小一半。
空间复杂度:$O(1)$,只使用了常数级别的额外空间。
4. Median of Two Sorted Arrays
题目描述
Given two sorted arrays nums1
and nums2
of size m
and n
respectively, return the median of the two sorted arrays.
The overall run time complexity should be O(log (m+n))
.
Example 1:
1 | Input: nums1 = [1,3], nums2 = [2] |
Example 2:
1 | Input: nums1 = [1,2], nums2 = [3,4] |
Constraints:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-10^6 <= nums1[i], nums2[i] <= 10^6
解题方法
这个问题要求我们在两个已排序的数组中找到中位数,并且要在 $O(log(m+n))$ 的时间复杂度内完成。我们可以利用二分查找的思想来解决。
解决思路:
假设我们有两个已排序的数组 nums1
和 nums2
,我们需要在它们的合并数组中找到中位数。直接合并两个数组并排序的时间复杂度是 $O((m+n) log(m+n))$,这显然不满足题目对时间复杂度的要求。因此,我们可以利用二分查找来优化这一过程。
我们可以通过在较小的数组上进行二分查找,来逐步缩小问题的规模。具体步骤如下:
保证数组 nums1 更小:如果 nums1 比 nums2 大,我们交换它们,以确保我们总是在较小的数组上进行二分查找。
二分查找:在 nums1 上进行二分查找,设定一个切分点
i
,然后根据i
的位置计算 nums2 中的切分点j
,使得左右两部分元素的数量相等或尽量接近。判断是否找到合适的切分点:
- 确保左半部分的最大值小于右半部分的最小值,即:
nums1[i-1] <= nums2[j]
和nums2[j-1] <= nums1[i]
。
- 如果不满足条件,调整二分查找的范围。
- 确保左半部分的最大值小于右半部分的最小值,即:
计算中位数:
- 如果两个数组的总长度是奇数,中位数是左半部分的最大值。
- 如果是偶数,中位数是左右两部分最大值和最小值的平均。
代码实现
1 | class Solution: |
复杂度分析
时间复杂度:$O(log(min(m, n)))$,由于我们在较小的数组上进行二分查找,最坏情况下的时间复杂度是 $O(log(min(m, n)))$。
空间复杂度:$O(1)$,只使用了常数级别的额外空间。