LeetCode 34、在排序数组中查找元素的第一个和最后一个位置
LeetCode 34、在排序数组中查找元素的第一个和最后一个位置
视频地址:https://uha.xet.tech/s/4uRhHR
一、题目描述
给定一个按照升序排列的整数数组 nums
,和一个目标值 target
。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target
,返回 [-1, -1]
。
进阶:
- 你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
提示:
- 0 <= nums.length <= 105
- -10(9) <= nums[i] <= 10(9)
- nums 是一个非递减数组
- -10(9) <= target <= 10(9)
二、题目解析
三、参考代码**(吴师兄左闭右闭版本代码)**
Java
class Solution {
public int[] searchRange(int[] nums, int target) {
// 寻找目标值在数组中的开始位置
int firstIdx = findBeginPostion(nums,target);
// 寻找目标值在数组中的结束位置
int lastIdx = findEndPostion(nums,target);
// 返回寻找的结果
return new int[]{firstIdx,lastIdx};
}
// 寻找目标值在数组中的开始位置
private int findBeginPostion(int[] nums , int target){
// left 指向当前区间的最左边位置,所以初始化为 0
int left = 0;
// right 指向当前区间的最右边位置,所以初始化为 nums.length - 1
int right = nums.length - 1;
// 循环进行二分查找,直到左端点位置超过了右端点
// 或者在循环过程中找到了起始位置
while(left <= right){
// 计算当前区间的中间位置,向下取整
int mid = (left + right) / 2;
// 如果中间位置的元素值等于目标值 target
if(nums[mid] == target){
// 并且中间位置 mid 的左边没有元素,即中间位置 mid 为当前区间的起始位置
// 或者中间位置 mid 的前一个元素小于目标值 target
// 说明 mid 指向了 target 的起始位置
if(mid == 0 || nums[mid - 1] < target){
// mid 指向了 target 的起始位置,返回这个结果
return mid;
}
// 否则,说明 mid 的左边依然有元素值等于 target
// 那么 mid 就不是 target 的起始位置,需要在 mid 的左边进行查找
// 所以缩小范围为 left 到 mid - 1
// 当前区间的左侧为 left,右侧 right = mid - 1
right = mid - 1;
// 如果中间位置的元素值大于目标值 target
// 说明需要在 mid 的左边进行查找
}else if( nums[mid] > target){
// 所以缩小范围为 left 到 mid - 1
// 当前区间的左侧为 left,右侧 right = mid - 1
right = mid - 1;
// 如果中间位置的元素值小于目标值 target
// 说明需要在 mid 的右边进行查找
}else{
// 所以缩小范围为 mid + 1 到 right
// 当前区间的左侧为 left = mid + 1,右侧 right
left = mid + 1;
}
}
// 如果循环结束后还没有返回,说明找不到目标值 target,返回 -1
return - 1;
}
// 寻找目标值在数组中的结束位置
private int findEndPostion(int[] nums , int target){
// left 指向当前区间的最左边位置,所以初始化为 0
int left = 0;
// right 指向当前区间的最右边位置,所以初始化为 nums.length - 1
int right = nums.length - 1;
// 循环进行二分查找,直到左端点位置超过了右端点
// 或者在循环过程中找到了结束位置
while(left <= right){
// 计算当前区间的中间位置,向下取整
int mid = (left + right) / 2;
// 如果中间位置的元素值等于目标值 target
if(nums[mid] == target){
// 并且中间位置 mid 的右边没有元素,即中间位置 mid 为当前区间的结束位置
// 或者中间位置 mid 的后一个元素大于目标值 target
// 说明 mid 指向了 target 的结束位置
if(mid == nums.length - 1 || nums[mid + 1] > target){
// mid 指向了 target 的结束位置,返回这个结果
return mid;
}
// 否则,说明 mid 的右边依然有元素值等于 target
// 那么 mid 就不是 target 的结束位置,需要在 mid 的右边进行查找
// 所以缩小范围为 mid + 1 到 right
// 当前区间的左侧为 left = mid + 1 ,右侧为 right
left = mid + 1;
// 如果中间位置的元素值大于目标值 target
// 说明需要在 mid 的左边进行查找
}else if( nums[mid] > target){
// 所以缩小范围为 left 到 mid - 1
// 当前区间的左侧为 left,右侧 right = mid - 1
right = mid - 1;
// 如果中间位置的元素值小于目标值 target
// 说明需要在 mid 的右边进行查找
}else{
// 所以缩小范围为 mid + 1 到 right
// 当前区间的左侧为 left = mid + 1,右侧 right
left = mid + 1;
}
}
// 如果循环结束后还没有返回,说明找不到目标值 target,返回 -1
return - 1;
}
}
Python
class Solution:
def searchRange(self, nums, target):
# 寻找目标值在数组中的开始位置
first_idx = self.find_begin_position(nums, target)
# 寻找目标值在数组中的结束位置
last_idx = self.find_end_position(nums, target)
# 返回寻找的结果
return [first_idx, last_idx]
# 寻找目标值在数组中的开始位置
def find_begin_position(self, nums, target):
# left 指向当前区间的最左边位置,所以初始化为 0
left = 0
# right 指向当前区间的最右边位置,所以初始化为 len(nums) - 1
right = len(nums) - 1
# 循环进行二分查找,直到左端点位置超过了右端点
while left <= right:
# 计算当前区间的中间位置,向下取整
mid = (left + right) // 2
# 如果中间位置的元素值等于目标值 target
if nums[mid] == target:
# 如果 mid 为当前区间的起始位置或 mid 的前一个元素小于 target
# 说明 mid 指向了 target 的起始位置
if mid == 0 or nums[mid - 1] < target:
return mid
# 否则在左边区域继续查找
right = mid - 1
elif nums[mid] > target:
# 在左边区域继续查找
right = mid - 1
else:
# 在右边区域继续查找
left = mid + 1
# 没找到目标值,返回 -1
return -1
# 寻找目标值在数组中的结束位置
def find_end_position(self, nums, target):
# left 指向当前区间的最左边位置,所以初始化为 0
left = 0
# right 指向当前区间的最右边位置,所以初始化为 len(nums) - 1
right = len(nums) - 1
# 循环进行二分查找
while left <= right:
# 计算当前区间的中间位置,向下取整
mid = (left + right) // 2
# 如果中间位置的元素值等于目标值 target
if nums[mid] == target:
# 如果 mid 为当前区间的结束位置或 mid 的后一个元素大于 target
# 说明 mid 指向了 target 的结束位置
if mid == len(nums) - 1 or nums[mid + 1] > target:
return mid
# 否则在右边区域继续查找
left = mid + 1
elif nums[mid] > target:
# 在左边区域继续查找
right = mid - 1
else:
# 在右边区域继续查找
left = mid + 1
# 没找到目标值,返回 -1
return -1
C++
#include <vector>
using namespace std;
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
// 寻找目标值在数组中的开始位置
int firstIdx = findBeginPosition(nums, target);
// 寻找目标值在数组中的结束位置
int lastIdx = findEndPosition(nums, target);
// 返回寻找的结果
return {firstIdx, lastIdx};
}
private:
// 寻找目标值在数组中的开始位置
int findBeginPosition(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
// 循环进行二分查找
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
if (mid == 0 || nums[mid - 1] < target) {
return mid; // 找到目标值的起始位置
}
right = mid - 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
// 没找到目标值,返回 -1
return -1;
}
// 寻找目标值在数组中的结束位置
int findEndPosition(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
// 循环进行二分查找
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
if (mid == nums.size() - 1 || nums[mid + 1] > target) {
return mid; // 找到目标值的结束位置
}
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
// 没找到目标值,返回 -1
return -1;
}
};
四、参考代码(许老师左闭右开版本代码)
Java
import java.util.Arrays;
import java.util.List;
class Solution {
public int[] searchRange(int[] nums, int target) {
int n = nums.length;
// 排除特殊情况
if (n == 0) {
return new int[]{-1, -1};
}
// 第一次搜索【开始位置】,左闭右开
// 搜索【第一个大于等于target的数】
int left = 0, right = n;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}
}
// 若第一次搜索结束后,left对应的数字不是target
// 返回{-1, -1}
if (left == n || nums[left] != target) {
return new int[]{-1, -1};
}
int start = left;
// 第二次搜索【结束位置】,左闭右开
// 搜索【第一个大于target的数】
left = 0;
right = n;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] > target) {
right = mid;
} else {
left = mid + 1;
}
}
// 返回起始位置和结束位置
return new int[]{start, left - 1};
}
}
Python
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
n = len(nums)
# 排除特殊情况
if n == 0:
return [-1, -1]
# 第一次搜索【开始位置】,左闭右开
# 搜索【第一个大于等于target的数】
left, right = 0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] >= target:
right = mid
else:
left = mid + 1
# 若第一次搜索结束后,left对应的数字不是target
# 返回[-1, -1]
if left == n or nums[left] != target:
return [-1, -1]
i = left
# 第二次搜索【结束位置】,左闭右开
# 搜索【第一个大于target的数】
left, right = 0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] > target:
right = mid
else:
left = mid + 1
return [i, left-1]
C++
#include <vector>
using namespace std;
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int n = nums.size();
// 排除特殊情况
if (n == 0) {
return {-1, -1};
}
// 第一次搜索【开始位置】,左闭右开
// 搜索【第一个大于等于target的数】
int left = 0, right = n;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}
}
// 若第一次搜索结束后,left对应的数字不是target
// 返回{-1, -1}
if (left == n || nums[left] != target) {
return {-1, -1};
}
int start = left;
// 第二次搜索【结束位置】,左闭右开
// 搜索【第一个大于target的数】
left = 0, right = n;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] > target) {
right = mid;
} else {
left = mid + 1;
}
}
// 返回起始位置和结束位置
return {start, left - 1};
}
};