C++:数组-二分法-Leetcode-34题在排序数组中查找元素的第一个和最后一个位置

C++:数组-二分法-Leetcode-34题在排序数组中查找元素的第一个和最后一个位置

二分法的熟练应用,记录笔记,记住易错点



题目

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。


知识点

二分法的写法规则,二分法的应用

思路分析

1 .首先根据题目分析几种可能出现的情况,以nums[5,7,7,8,8,10]为例

  • 当nums长度为0,返回{-1,- 1}
  • 当target = 0时,返回{-1,-1}
  • 当target = 12时,返回{-1,-1}
  • 当target = 6时,返回{-1,-1}
  • 当target = 8时,返回对应的结果,{3,4}

2.题目要求时间复杂度为 O(log n) 的算法,并且为升序数组,故求左边界和求右边界时都要使用二分法
3.总体思路为当nums[middle]小于target时,left指针不断的往右移,当nums[middle]大于target时,right指针不断的往左移,直至出现left与right指针出现交叉,或者left、right移出数组时,退出循环,返回对应的指针,求左边界返回左指针,求右边界时返回右指针。
4.具体分析

  • nums[5,7,7,8,8,10]
  • target = 0时,求左边界:右指针不断往左靠,直至right=-1,退出循环,返回left = 0;求右边界:同样也是右指针往左靠,直至right = -1,退出循环,返回right = -1;
  • target = 12时,求左边界:左指针不断往右靠,直至left = 6,退出循环,返回left = 6;求右边界:同样也是左指针不断往右靠,直至left = 6,退出循环,返回right = 5;
  • target = 6时,当nums[middle]>target,右指针左移,right = middle-1;当nums[middle]<target,左指针右移,left = middle+1;
    求左边界时:当nums[middle]==target时,右指针左移,因为求的左边界,right = middle-1,最终left=1,right=0,左右指针出现交叉,退出循环,返回left = 1;
    求右边界时:当nums[middle]==target时,左指针右移,因为求的有边界,left = middle+1,最终left=1,right=0,左右指针出现交叉,退出循环,返回right = 0;
    最终结果的事left = 1,right=0,出现了交叉,因为左右指针都会不断的移动去比较比target大或小的元素,直至满足情况。故需要再加一个判断left > right来去除此种情况,最终返回{-1,-1},因为target = 6不存在。
  • target = 8时,求左边界和求右边界的判断逻辑与target = 6同理,也是直至最后出现左右指针出现交叉情况时,退出while(left<=right)的循环,然后求左边界返回left,求右边界返回right。

代码

/*
leetcode-34题二分法


思路分析:
1.先判定几种未找到值的情况
2.分别用二分法来求左边界和右边界
3.求左边界时,右边指针不断往左移,最终返回右指针
3.求右边界时,左边指针不断往右移,最终返回左指针
*/

#include "iostream"
#include "vector"
using namespace std;

class Solution
{
    
    
private:
    //获取左边界
    int getLeftBorder(vector<int> &nums, int target)
    {
    
    
        int left = 0;
        int right = nums.size() - 1;
        int middle;
        while (left <= right)
        {
    
    
            middle = left + (right - left) / 2;
            if (nums[middle] > target)
            {
    
    
                right = middle - 1;
            }
            else if (nums[middle] < target)
            {
    
    
                left = middle + 1;
            }
            else //nums[middle]==target
            {
    
    
                right = middle - 1;
            }
        }
        //循环结束后
        return left;
    }

    //获取右边界
    int getRightBorder(vector<int> &nums, int target)
    {
    
    
        int left = 0;
        int right = nums.size() - 1;
        int middle;
        while (left <= right)
        {
    
    
            middle = left + (right - left) / 2;
            if (nums[middle] > target)
            {
    
    
                right = middle - 1;
            }
            else if (nums[middle] < target)
            {
    
    

                left = middle + 1;
            }
            else
            {
    
    
                left = middle + 1;
            }
        }
        return right;
    }

public:
    vector<int> searchRange(vector<int> &nums, int target)
    {
    
    
        int len = nums.size();
        if (len == 0)
        {
    
    
            return {
    
    -1, -1};
        }
        int left = getLeftBorder(nums, target);
        int right = getRightBorder(nums, target);
        if (left > nums.size() - 1 || right < 0 || left > right) //left指针向右移动到最尾,right指针向左移动到最头,那就是没有找到
        {
    
    
            return {
    
    -1, -1};
        }
        return {
    
    left, right};
    }
};

int main(int argc, const char **argv)
{
    
    
    vector<int> nums;

    nums.push_back(5);
    nums.push_back(7);
    nums.push_back(7);
    //  nums.push_back(7);
    nums.push_back(8);
    nums.push_back(8);
    nums.push_back(10);

    Solution s1;
    vector<int> res;
    res = s1.searchRange(nums, 6);
    for (vector<int>::iterator it = res.begin(); it != res.end(); it++)
    {
    
    
        cout << *it << endl;
    }

    return 0;
}

总结

考察对二分法的熟练程度,本题可以求左右边界一块写,但初学者为了更好的理解,还是分开二分法求左右边界好,还是要多练,多调试理解原理。

猜你喜欢

转载自blog.csdn.net/Bellwen/article/details/128053643
今日推荐