【Leetcode HOT100】在排序数组中查找元素的第一个和最后一个位置 c++

题目描述:
给你一个按照非递减顺序排列的整数数组 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]

方法1 c++代码 二分法+暴力寻找:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int n=nums.size();
        if(n==0){
            vector<int> v;
            v.push_back(-1);
            v.push_back(-1);
            return v;
        }
        if(n==1){
            vector<int> v;
            if(target == nums[0]){
                v.push_back(0);
                v.push_back(0);
            }
            else{
                v.push_back(-1);
                v.push_back(-1);
            }
            return v;
        }
        int left = 0, right = n-1, flag=0;
        vector<int> v;
        //二分法找target
        while(left<=right){
            int mid = (left+right)/2;
            //找到了mid处为target,向mid的左右两边找直到找到和target不一样的数,记录位置
            if(nums[mid] == target){
                flag = 1; //flag标志二分法是否找到了target
                if(mid == 0)v.push_back(mid);
                else{
                	//向左边找第一个和target不一样的数字的位置
                    for(int i=1;i<=mid;i++){
                        if(nums[mid-i]!=nums[mid]){
                            v.push_back(mid-i+1);
                            break;
                        }
                        else if(mid-i==0){
                            v.push_back(mid-i);
                        }
                    }
                }
                if(mid == n-1)v.push_back(mid);
                else{
                	//向右边找第一个和target不一样的数字的位置
                    for(int i=1;i<=(n-1-mid);i++){
                        if(nums[mid+i]!=nums[mid]){
                            v.push_back(mid+i-1);
                            break;
                        }
                        else if(mid+i==n-1){
                           v.push_back(mid+i);
                        }
                    }
                }
                break;
            }
            if(nums[mid]>target){
                right = right - 1;
            }
            if(nums[mid]<target){
                left = left + 1;
            }
        }
        if(flag == 0){
            v.push_back(-1);
            v.push_back(-1);
        }
        return v;
    }
};

正常使用二分法,先找到target(在mid处找到target),然后从mid处向左右两边查找,直到找到与target不同的位置停止,记录左右位置。

该方法速度较慢。
在这里插入图片描述

方法2 c++代码 使用两次二分法:

class Solution {
public:
    //返回大于等于target的靠最右边的数位置
    int solve1(vector<int> nums,int target,int n){
        int left = 0, right = n-1, flag = 0, index = -1;
        while(left<=right){
            int mid = (left+right)/2;
            if(nums[mid] >= target){ 
                right = right - 1;
                index =  mid;
            }
            else if(nums[mid] < target){
                left = mid + 1;
            }
        }
        return index;
    }
    //返回大于target的靠最左边的数位置
    int solve2(vector<int> nums,int target,int n){
        int left = 0, right = n-1, flag = 0, index = -1;
        while(left<=right){
            int mid = (left+right)/2;
            if(nums[mid] > target){ 
                right = right - 1;
                index = mid;
            }
            else if(nums[mid] <= target){
                left = mid + 1;
            }
        }
        return index;
    }
    //先找大于等于target的最靠右的数, 再找小于target的最靠左的数,两个中间即为等于target的区间
    vector<int> searchRange(vector<int>& nums, int target) {
        int n = nums.size();
        vector<int> v;
        if(n==0){
            v.push_back(-1);
            v.push_back(-1);
            return v;
        }
        int n1 = solve1(nums,target,n);
        int n2 = solve2(nums,target,n);
        //n1=-1即大于等于target的数找不到,不可能有等于target的数,返回【-1,-1】;
        //n1=n2即大于等于target的数和大于target的数是同一个,说明只满足大于target,而没有等于target的数,返回【-1,-1】
        if(n1 == -1 || n1==n2){
            v.push_back(-1);
            v.push_back(-1);
        }
        else if(n2==-1){ //n1!=-1,若n2=-1,说明没有比target大的数字,即从n1向后所有数字都等于target
            v.push_back(n1);
            v.push_back(n-1);
        }
        else if(n2!=-1){ //n1!=-1,若n2!=-1,说明n2处数字开始大于target,即从n1到(n2-1)的所有数字等于target
            v.push_back(n1);
            v.push_back(n2-1);
        }
        return v;
    }
};

在这里插入图片描述

总结:

特殊二分法:

(1)当给出的序列不满足**二分法的使用条件“有序”**时,创造有序的条件以使用二分法

(2)传统二分法用等于作为边界条件,当要求的结果不能只用一次二分法求出,就改变二分法的边界条件使用多次二分法得到多个结果(此题中使用两次二分法,一个用大于等于作为边界条件,一个用大于作为边界条件,得到两个结果以表示一个区间)。

(3)使用常数次二分法,时间复杂度仍为O(logn)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值