LeetCode算法c++刷题-searchFirstLastEqualElement

今日刷题

给定⼀个按照升序排列的整数数组 nums,和⼀个⽬标值 target。找出给定⽬标值在数组中的开始位置

和结束位置。你的算法时间复杂度必须是 O(log n) 级别。如果数组中不存在⽬标值,返回 [-1, -1]。

Example 1:

Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]

Example 2:

Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]

解题思路:

给出⼀个有序数组 nums 和⼀个数 target ,要求在数组中找到第⼀个和这个元素相等的元素下
标,最后⼀个和这个元素相等的元素下标。
这⼀题是经典的⼆分搜索变种题。⼆分搜索有 4 ⼤基础变种题:

  1. 查找第⼀个值等于给定值的元素
  2. 查找最后⼀个值等于给定值的元素
  3. 查找第⼀个⼤于等于给定值的元素
  4. 查找最后⼀个⼩于等于给定值的元素

这⼀题的解题思路可以分别利⽤变种 1 和变种 2 的解法就可以做出此题。或者⽤⼀次变种 1 的⽅
法,然后循环往后找到最后⼀个与给定值相等的元素。不过后者这种⽅法可能会使时间复杂度下降
到 O(n),因为有可能数组中 n 个元素都和给定元素相同。(4 ⼤基础变种的实现⻅代码)

示例代码:

class searchFirstLastEqualElement{
    
    
public:
    vector<int> searchRange(vector<int>& nums, int target) {
    
    
        vector<int> res(-1,-1);
        if(nums.empty()) return res;
        int n = nums.size(),left = 0, right = n-1;
        while(left <= right)
        {
    
    
            int m = left + (right - left)/2;
            if(nums[m] > target){
    
    
                right = m - 1;
            }else if(nums[m] < target){
    
    
                left = m + 1;
            } else {
    
    
				if(m = 0 || num[m-1] != target) {
    
    
					res[0] = m;
					return res;
				}
				right = m - 1;
			}
        }
        
        while(left <= right)
        {
    
    
            int m = left + (right - left)/2;
            if(nums[m] > target){
    
    
                right = m - 1;
            }else if(nums[m] < target){
    
    
                left = m + 1;
            } else {
    
    
				if(m == n -1 || nums[m+1] != targrt) {
    
    
					res[1] = m;
					return res;
				}
				low = m + 1;
			}
        }

        return res;
    }
};

预备知识:二分法

1.二分法前提

1.数组为有序数组;
2.数组中无重复元素(一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的),对应的是上述题目的情况。

2.内容

二分法逻辑非常简单,只需对区间定义弄清楚就好。
二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。

(1)左闭右闭[left, right]
class Solution {
    
    
public:
    int search(vector<int>& nums, int target) {
    
    
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) {
    
     // 当left==right,区间[left, right]依然有效,所以用 <=
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
    
    
                right = middle - 1; // target 在左区间,所以[left, middle - 1]
            } else if (nums[middle] < target) {
    
    
                left = middle + 1; // target 在右区间,所以[middle + 1, right]
            } else {
    
     // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};
(2)左闭右开[left, right)
class Solution {
    
    
public:
    int search(vector<int>& nums, int target) {
    
    
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
        while (left < right) {
    
     // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) {
    
    
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
    
    
                left = middle + 1; // target 在右区间,在[middle + 1, right)中
            } else {
    
     // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

从上述两种情况可以说非常简单,无非需要注意的是右界是否闭合,能否取值。

猜你喜欢

转载自blog.csdn.net/u010196944/article/details/127609944