说明
算法: 34. Find First and Last Position of Element in Sorted Array
地址:https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/
Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.
Your algorithm’s runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].
Example 1:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
Example 2:
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
Example 3:
Input: nums = [], target = 0
Output: [-1,-1]
Constraints:
- 0 <= nums.length <= 105
- -109 <= nums[i] <= 109
- nums is a non-decreasing array.
- -109 <= target <= 109
二分搜索来找到目标数值在排序数组中的起始和结束位置
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
# 找到目标数值在排序数组中的起始位置
lo_bound = self.findBound(nums, target, True)
# 如果目标数值不存在,返回[-1, -1]
if lo_bound == -1:
return [-1, -1]
# 找到目标数值在排序数组中的结束位置
up_bound = self.findBound(nums, target, False)
return [lo_bound, up_bound]
def findBound(self, nums, target, isFirst: bool) -> int:
n = len(nums)
lo = 0
hi = n - 1
# 二分搜索过程
while lo <= hi:
mid = lo + (hi - lo) // 2
# 当找到目标数值
if nums[mid] == target:
# 如果寻找的是起始位置
if isFirst:
# 如果mid已经是最左边或它的左边的数不等于目标数
if lo == mid or nums[mid - 1] != nums[mid]:
return mid
# 否则,将搜索范围移到左半部分
else:
hi = mid - 1
# 如果寻找的是结束位置
else:
# 如果mid已经是最右边或它的右边的数不等于目标数
if hi == mid or nums[mid+1] != nums[mid]:
return mid
# 否则,将搜索范围移到右半部分
else:
lo = mid + 1
# 如果mid处的数大于目标数,将搜索范围移到左半部分
elif nums[mid] > target:
hi = mid - 1
# 如果mid处的数小于目标数,将搜索范围移到右半部分
else:
lo = mid + 1
# 如果找不到目标数,返回-1
return -1
复杂度分析:
- 时间复杂度:O(log n),其中 n 是 nums 的长度。因为这里有两次二分搜索(一次寻找起始位置,一次寻找结束位置),每次二分搜索的时间复杂度都是 O(log n),因此总体的时间复杂度为 O(2 * log n),即 O(log n)。
- 空间复杂度:O(1),只使用了常数额外空间。
解法一
创建两个独立的方法:找到第一个,和找到最后一个。
public int[] searchRangeWithEasyTwoMethod(int[] nums, int target) {
int[] result = new int[]{-1, -1};
// edge check
if (nums == null || nums.length == 0) {
return result;
}
result[0] = findFisrt(nums, target);
result[1] = findLast(nums, target);
return result;
}
private int findFisrt(int[] nums, int target) {
int result = -1;
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + ((right - left) >> 1);
if (nums[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
if (nums[mid] == target) {
result = mid;
}
}
return result;
}
private int findLast(int[] nums, int target) {
int result = -1;
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + ((right - left) >> 1);
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
if (nums[mid] == target) {
result = mid;
}
}
return result;
}
解法二
- 找到第一个大于等于target的位置,也就是左边的起始值。
陷阱1:low <= target < high. 如果target比所有值都大,则返回值会是array.length。
陷阱2:如果没有这个值,那么返回来的low,nums[low] != target - 找到第一个大于等于target+1的位置,也就是右边的起始值,记住要结果-1.
public int[] searchRangeWithFirstGreatEqual(int[] nums, int target) {
int[] result = new int[]{-1, -1};
// edge check
if (nums == null || nums.length == 0) {
return result;
}
int find = findFirstGreatEqual(nums, target);
if (find >= nums.length || nums[find] != target) {
return result;
}
result[0] = find;
result[1] = findFirstGreatEqual(nums, target + 1) - 1;
return result;
}
//find the first number that is greater than or equal to target.
//could return A.length if target is greater than A[A.length-1].
private int findFirstGreatEqual(int[] nums, int target) {
int low = 0;
int high = nums.length;
while (low < high) {
int mid = low + ((high - low) >> 1);
//low <= mid < high
if (nums[mid] < target) {
low = mid + 1;
} else {
//should not be mid-1 when A[mid]==target.
//could be mid even if A[mid]>target because mid<high.
high = mid;
}
}
return low;
}
代码下载
https://github.com/zgpeace/awesome-java-leetcode/blob/master/code/LeetCode/src/binarysearch/FindFirstAndLastPositionOfElementInSortedArray.java