LeetCode34-在排序数组中查找元素的第一个和最后一个位置

一、题目

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
要求:
(1)如果数组中不存在目标值 target,返回 [-1, -1]。
(2)你必须设计并实现时间复杂度为 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
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array
著作权归领扣网络所有。

二、思路及代码

方法一:二分法1

1、思路:

(1)先判断nums数组的大小,如果其中没有元素,想都不用想就返回{-1,-1}。
(2)如果有元素,通过二分法找到目标,然后以这个下标为起点往左往右遍历,直到左边或者右边的值不再是target为止。

2、代码:
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.size() == 0){
            return {-1,-1};
        }else{
        	//二分法查找target
            int left = 0;
            int right = nums.size() - 1;
            while(left <= right){
                int middle = left + (right-left) / 2;
                if(nums[middle] > target){
                    right = middle - 1;
                }else if(nums[middle] < target){
                    left = middle + 1;
                }else{
                    //如果找到了,那么left和right以middle为起点分头行动
                    left = middle;
                    right = middle;
                    while(left >= 0 && nums[left] == nums[middle]){
                        left--;
                    }
                    while(right < nums.size() && nums[right] == nums[middle]){
                        right++;
                    }
                    return {left+1, right-1};
                }
            }
        }
        return {-1,-1};
    }
};

注意:
(1)为什么return{left+1,right-1}?
因为left–和right++总会多执行一遍,踩到了边界外面,举个例子,[2,2,2,2,3],target = 2,到最后left = -1,那么左边界值肯定是left+1 = 0。

方法二:二分法2

1、思路:

LeetCode34-在排序数组中查找元素的第一个和最后一个位置_第1张图片

潦草地画了一张图,大致意思就是这样。
(1)先寻找出左边界,然后寻找右边界;
(2)第一次循环遍历到最后,l肯定会和r重合,即可找到左边界
(3)同理,找右边界,注意细节即可。

2、代码
(1)左闭右开
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> v(2,-1);//建立一个长度为2,初始值均为1的数组
        int left = 0;
        int right = nums.size();
        //判断数组是否为空
        if(nums.size()==0){
            return v;
        }
        //先寻找左边界
        while(left < right){
            int middle = left + (right-left)/2;
            if(nums[middle] >= target){
                //如果nums[middle]==target,那么更新right的值使其向left靠拢
                right = middle;//左闭右开典型特征right=middle
            }else{
                left = middle + 1;//左闭右开使得这里+1
            }
        }
        //如果没有找到target,那么直接返回
        if(left>nums.size()-1 || nums[left]!=target){    //这里的判断条件有注解
            return v;
        }
        //找到了target,寻找右边界
        v[0] = left;
        right = nums.size();
        while(left < right){
            int middle = left + (right-left)/2;
            if(nums[middle] <= target){
                //如果nums[middle]==target,那么更新left的值使其向right靠拢
                left = middle + 1;
            }else{
                right = middle;
            }
        }
        v[1] = right - 1;
        return v;
    }
};

注意:为什么这里判断条件是left>nums.size()-1 || nums[left]!=target,因为这里面left很有可能超出边界,那么执行下面的代码就报错了。

(2)左闭右闭
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> v(2,-1);//建立一个长度为2,初始值均为1的数组
        int left = 0;
        int right = nums.size() - 1;//看下面注解
        //判断数组是否为空
        if(nums.size()==0){
            return v;
        }
        //先寻找左边界
        while(left <= right){
            int middle = left + (right-left)/2;
            if(nums[middle] >= target){
                //如果nums[middle]==target,那么更新right的值使其向left靠拢
                right = middle - 1;//左闭右闭使得right=middle-1
            }else{
                left = middle + 1;//左闭右闭使得这里+1
            }
        }
        //如果没有找到target,那么直接返回
        if(left>nums.size()-1 || nums[left]!=target){
            return v;
        }
        //找到了target,寻找右边界
        v[0] = left;
        right = nums.size() - 1;
        while(left <= right){
            int middle = left + (right-left)/2;
            if(nums[middle] <= target){
                //如果nums[middle]==target,那么更新left的值使其向right靠拢
                left = middle + 1;
            }else{
                right = middle - 1;
            }
        }
        v[1] = right;
        return v;
    }
};

杂谈:附上四个月以前写的代码,不知道自己怎么做到的。。。

class Solution {
public:
	vector<int> searchRange(vector<int>& nums, int target) {
		int left = 0;
		int right = nums.size() - 1;
		vector<int> v2,v3;
		int middle = 0;
		while (left <= right)
		{
			middle = (left + right) / 2;
			if (nums[middle] < target) {//在右区间
				left = middle + 1;
			}
			else if (nums[middle] > target) {//在左区间
				right = middle - 1;
			}
			else {//相等
				v2.push_back(middle);
				break;
			}
		}
		if (v2.empty())
		{
			vector<int> v3(2,-1);
			return v3;
		}

		else {
			left = middle;
			right = middle;
			while (left - 1 >= 0 && right + 1 <= nums.size() - 1) {
				if (nums[left - 1] != target && nums[right + 1] != target)
				{
					v2.push_back(middle);
					break;
				}
				if (nums[left - 1] == target)
				{
					v2.push_back(--left);
				}
				if (nums[right + 1] == target)
				{
					v2.push_back(++right);
				}
			}
			while (left == 0 && right + 1 <= nums.size() - 1 && nums[right + 1] == target)
			{
				v2.push_back(++right);
			}
			while (right == nums.size() - 1 && left - 1 >= 0 && nums[left - 1] == target)
			{
				v2.push_back(--left);
			}
		}
		//排序
		sort(v2.begin(), v2.end());
		if (v2[0] == v2[v2.size() - 1])
		{
			v3.push_back(v2[0]);
			v3.push_back(v2[0]);
		}
		else {
			v3.push_back(v2[0]);
			v3.push_back(v2[v2.size() - 1]);
		}
		return v3;
	}
};

你可能感兴趣的:(LeetCode刷题,算法,数据结构,leetcode)