一.相关链接
题目链接:34. 在排序数组中查找元素的第一个和最后一个位置
代码随想录思路解析:34. 在排序数组中查找元素的第一个和最后一个位置(代码随想录)
二.心得体会
这一道题目的核心思想就是利用二分查找来得到所需的左边界与右边界。
以搜索右边界为例,代码的思路主要分为两点。第一:当nums[middle] > target时,right下标的变化都必然会落到新搜索区间中最大可能取值的位置,即right最终会落到下标最大的target处。第二:当nums[middle] == target时,依旧右移left下标,使得left>right并跳出循环。这两点综合起来就能使得最终left下标位置即为右边界。搜索左边界同理。
最后在代码开始单独实现判断len为0与1的情况,并在输出时判断是否有边界的值等于-2(只有当nums[middle] == target时才会修改边界的值),若有则说明数组不存在target大小的数,返回{-1,-1}。
三.代码
1)暴力:直接用for循环找左右边界。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int left_border = -2;
int right_border = -2;
int len = nums.size();
if(len == 0)
return {-1,-1};
for(int i=0;i<len;i++)
{
if(nums[i]==target)
{
left_border = i;
break;
}
}
for(int i=len-1;i>=0;i--)
{
if(nums[i]==target)
{
right_border = i;
break;
}
}
if(right_border!=-2&&left_border!=-2) #有边界等于-2说明不存在target
return {left_border,right_border};
else return {-1,-1};
}
};
2)自我尝试所得AC代码:解题时的思想是找到一个target后分别向左右试探。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
//单独讨论空数组和长度为1、2的数组
if(nums.size()==0)
return {-1,-1};
if(nums.size()==1&&nums[0]==target)
return {0,0};
else if(nums.size()==1&&nums[0]!=target)
return {-1,-1};
if(nums.size()==2)
{
if(nums[0]==target&&nums[0]==nums[1])
return {0,1};
else if(nums[0]==target)
return {0,0};
else if(nums[1]==target)
return {1,1};
else return{-1,-1};
}
int left = 0;
int right = nums.size() - 1;
int middle = 0;
while(left<=right)
{
middle = left +(right-left)/2;
if(nums[middle]>target)
right = middle - 1;
else if(nums[middle]<target)
left = middle + 1;
else if(nums[middle]==target)
{
//想法很简单,在数组中定位到一个target后分别向左和右试探
//算法复杂度容易退化成O(n)
int flag_left = middle;
int flag_right = middle;
while(nums[flag_left] == target)
if(flag_left!=0)
flag_left -= 1;
else break;
if(nums[flag_left] != target)
flag_left++;
while(nums[flag_right] == target)
if(flag_right != nums.size()-1)
flag_right += 1;
else break;
if(nums[flag_right] != target)
flag_right--;
return {flag_left,flag_right};
}
}
return {-1,-1};
}
};
3)学习后实现代码:
这里分别实现了寻找左右边界的函数,并添加了一些条件判断。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int len = nums.size();
//单独讨论空数组与长度为1的数组
if(len == 0) return {-1,-1};
if(len == 1&&nums[0]==target) return {0,0};
if(len == 1&&nums[0]!=target) return {-1,-1};
int right_border = -2;
int left_border = -2;
//在调用函数前单独讨论一下边界等于target的情况
if(nums[len - 1] == target)
right_border = len;
else
right_border = getRightBorder(nums, target);
//在调用函数前单独讨论一下边界等于target的情况
if(nums[0] == target)
left_border = -1;
else
left_border = getLeftBorder(nums, target);
//只有左右边界都找到才不返回{-1,-1}
if(left_border!=-2&&right_border!=-2)
return {left_border+1, right_border-1};
else return {-1,-1};
}
private:
int getRightBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
int middle = -2;
int right_border = -2;
while(left<=right)
{
middle = left + ((right - left)/2);
if(nums[middle] > target)
right = middle - 1;
if(nums[middle] < target)
left = left + 1;
//当找到即使是找到target值,也需要让left持续移动到right右边以找到右边界。
if(nums[middle] == target)
{
left = left + 1;
right_border = left;
}
}
return right_border;
}
int getLeftBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
int middle = -2;
int left_border = -2;
while(left<=right)
{
middle = left + ((right - left)/2);
if(nums[middle] < target)
left = middle + 1;
if(nums[middle] > target)
right = middle - 1;
//当找到即使是找到target值,也需要让right持续移动到left左边以找到左边界。
if(nums[middle] == target)
{
right = middle - 1;
left_border = right;
}
}
return left_border;
}
};
由上面的代码不难发现,寻找左右边界的函数其实仅仅在nums[middle] == target时才有所不同,所以完全可以合二为一,提高复用性。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int len = nums.size();
//单独讨论空数组与长度为1的数组
if(len == 0) return {-1,-1};
if(len == 1&&nums[0]==target) return {0,0};
if(len == 1&&nums[0]!=target) return {-1,-1};
int right_border = -2;
int left_border = -2;
//在调用函数前单独讨论一下边界等于target的情况
if(nums[len - 1] == target)
right_border = len;
else
right_border = getBorder(nums, target,"right");
//在调用函数前单独讨论一下边界等于target的情况
if(nums[0] == target)
left_border = -1;
else
left_border = getBorder(nums, target,"left");
//只有左右边界都找到才不返回{-1,-1}
if(left_border!=-2&&right_border!=-2)
return {left_border+1, right_border-1};
else return {-1,-1};
}
private:
int getBorder(vector<int>& nums, int target, const string& bound) {
int left = 0;
int right = nums.size() - 1;
int middle = -2;
int border = -2;
while(left<=right)
{
middle = left + ((right - left)/2);
if(nums[middle] > target)
right = middle - 1;
if(nums[middle] < target)
left = left + 1;
//将两种情况合二为一
if(nums[middle] == target)
{
if(bound == "right") {
left = left + 1;
border = left;
}
else if (bound == "left") {
right = middle - 1;
border = right;
}
else{
cout<<"error";
//异常处理
}
}
}
return border;
}
};