Leetcode[二分查找] 34. 在排序数组中查找元素的第一个和最后一个位置

Leetcode[二分查找] 34. 在排序数组中查找元素的第一个和最后一个位置

  • 审题
  • 代码实现
  • 反思

审题

给定一个按照升序排列的整数数组 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 <= 10^5
-10^9 <= nums[i] <= 10^9
nums 是一个非递减数组
-10^9 <= target <= 10^9

看到这道题,可以看出时间复杂度为 O ( n ) O(n) O(n)的方法是很好搞定的,然而,如果想要优化到 O ( l o g ( n ) ) O(log(n)) O(log(n))则需要利用二分查找的方法,用三个指针, l , r , m i d l,r,mid l,r,mid形成分块查找,然而,这道题特殊的地方在于他可能有很多个待查找的值,因此,我也不知道最开始想出来的办法算不算 O ( l o g ( n ) ) O(log(n)) O(log(n))的方法,具体我们直接看代码吧。

代码实现

方案一(上文提到的的方法):

首先,我们做一个fail数组,用于没有找到目标值的情况,也就是loc=-1,其次,我们只要找到了一个待查找值就退出循环,这个时间复杂度是 O ( l o g ( n ) ) O(log(n)) O(log(n))的。然而,我们后面还要有一步是查找整个串的头尾所在位置,假设有k个值为target的元素,那么时间复杂度就是 O ( k ) O(k) O(k)因此整个的时间复杂度为 O ( l o g ( n ) + k ) O(log(n)+k) O(log(n)+k)

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int l=0;
        int r=nums.size()-1;
        int loc=-1;
        int mid;
        vector<int> fail;
        vector<int> success;
        fail.push_back(-1);
        fail.push_back(-1);
        while ( l<=r ) {
            mid = l + (r-l)/2;
            if ( nums[mid] < target ) {
                l = mid+1;
            } else if ( nums[mid] > target ) {
                r = mid-1;
            } else {
                loc = mid;
                break;
            }
        }
        if ( loc == -1 ) return fail;
        int templ=loc;
        int tempr=loc;
        while ( templ >= 0 && nums[templ] == target ) templ--;
        templ++;
        while ( tempr < nums.size() && nums[tempr] == target ) tempr++;
        tempr--;
        success.push_back(templ);
        success.push_back(tempr);
        return success;
    }
};

方案2:
看了官方题解后感觉如梦初醒,原来可以直接找左右边界,学到了…不过今天时间有点紧了,之前看到官方题解写的虽然简练,但是丧失了一些代码的易读性,因此我自己写的易读版如下。

class Solution {
public:
    int find_left(vector<int> nums, int target) {
        int l=0;
        int r=nums.size()-1;
        int res=-1;
        int mid;
        while ( l<=r ) {
            mid = l + (r-l)/2;
            if ( nums[mid] >= target ) {
                r = mid-1;
                res = mid;
            } else {
                l = mid+1;
            }
        }
        return res;
    }
    int find_right(vector<int> nums, int target) {
        int l=0;
        int r=nums.size()-1;
        int res=-1;
        int mid;
        while ( l<=r ) {
            mid = l + (r-l)/2;
            if ( nums[mid] > target ) {
                r = mid-1;
            } else {
                l = mid+1;
                res = mid;
            }
        }
        if ( res!=-1 && nums[res]!=target ) return -1;
        return res;
    }
    vector<int> searchRange(vector<int>& nums, int target) {
        int l = find_left(nums, target);
        int r = find_right(nums, target);
        if ( l>r ) return vector<int>{-1,-1}; 
        return vector<int>{l,r};
        
    }
};

反思

二分并不难,难的是细节,继续努力!

你可能感兴趣的:(Leetcode,二分查找专题,算法,leetcode,二分查找,c++)