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

给定一个“非递减顺序排列”,就是说包含了重复元素的递增序列

class Solution {
public:

	int binarySearch(vector<int>& nums, int target, bool lower) {
		int left = 0;
		int right = nums.size() - 1;

		// 这里ans为什么要初始化为数组大小?
                // 绝大数情况其实ans初始化多少无所谓,有所谓的情况是[1],1
		// 当下面的循环不执行的情况,即数组中为空,返回0
		int ans = nums.size();

		while (left <= right) {
			int mid = (left + right) / 2;
			// 这里第二个条件中lower是一个标记位,只有当它为true第二个条件才成立,覆盖第一个条件
			if (nums[mid] > target || (lower && nums[mid] >= target)) {
				right = mid - 1;
				ans = mid;
			}
			else {
				left = mid + 1;
			}
		}

		return ans;
	}

	vector<int> searchRange(vector<int>& nums, int target) {
		// 第一个大于等于的下标
		int leftIndex = binarySearch(nums, target, true);
		cout << "第一个大于等于的下标:" << leftIndex << endl;
		// 第一个大于的下标,就是最后一个等于的下标
		int rightIndex = binarySearch(nums, target, false)-1;
		cout << "最后一个等于的下标:" << leftIndex << endl;

		// 为什么要加这么长一段限定?
		if (leftIndex <= rightIndex && rightIndex < nums.size() && nums[leftIndex] == target && nums[rightIndex] == target) {
			return vector<int>{leftIndex, rightIndex};
		}
		return vector<int>{-1, -1 };
	}
};

int main() {
	vector<int> data = { 3,4,5,6,7,8,9 };
	Solution sc;
	vector<int> ans = sc.searchRange(data, 8);
	for (int i : ans) {
		cout << i << " ";
	}
}

实测不加那么长一段限定也能过

还有两个细节点是:

  1. 为什么ans初始化为数组长度?如果不用结果倒推是怎么想到的,是不是只有[1],1一种情况
  2. 相对于初始的二分查找写法,循环条件里多了一个等于,没有这个等于就无法达成下面的推断,但是,具体又是为什么?
    因为我之前二分查找写错了,本来就该有个等于

考虑把等于合并到大于小于的情况

  1. 合并到大于
  • 在大于等于给返回值赋值——返回第一个等于
  • 在小于给返回值赋值——返回第一个小于
  1. 合并到小于
  • 在小于等于给返回值赋值————返回最后一个等于
  • 在大于给返回值赋值——返回第一个大于

还有值得注意的一点是,他的循环条件里多了个 等于

	// 写一个非递归的二分查找
	int binarySearch(vector<int>& nums,int target) {
		// 参数检查

		int lowIndex = 0;
		int highIndex = nums.size() - 1;

		int ans = -1;

		while (lowIndex <= highIndex) {

			int middle = (highIndex - lowIndex) / 2 + lowIndex;

			// 这里条件大于/大于等于的区别
			// 大于的话返回了第一个大于指定元素的元素下标
			if (nums[middle] > target) {
				highIndex = middle - 1;
				ans = middle;
			}
			// 如果等于,也仍然把左边界改了,找到的会是什么呢?返回的是第一个等于指定元素的元素下标
			else {
				lowIndex = middle + 1;
			}
		}
		return ans;
	}

Java中心拓展

其实我最开始的思路也是这样,先用二分随便找一个符合的值,再向前后延展边界

贴一下评论中能过的一段代码,后面再研究

  public int[] searchRange(int[] nums, int target) {
    int low = 0, high = nums.length - 1;
    while (low <= high) {
      int mid = (low + high) >>> 1;
      if (nums[mid] == target) {
        int start = mid-1;
        while (start >= 0 && nums[start] == target) {
          start--;
        }
        int end = mid+1;
        while (end <= nums.length - 1 && nums[end] == target) {
          end++;
        }
        return new int[] { start + 1, end - 1 };

      } else if (nums[mid] > target) {
        high = mid - 1;
      } else {
        low = mid + 1;
      }
    }
    return new int[] { -1, -1 };
  }

但是经提醒说,万一整个数组都是target,算法时间效率就会退化到O(n)

这儿还有一版

// 思路:利用二分查找,查找到target之后,可能它的左边和右边还可能有target,所以还要继续查找
// 时间复杂度:O(logN) 空间复杂度:O(1)
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = {-1, -1};
        if(nums.length==0) return res;

        int l = 0, r = nums.length - 1;
        // 查找到target之后,给res[0]赋值,继续向左二分查找
        while(r>=l){
            int mid = (l + r) / 2;
            if(nums[mid]==target){
                res[0] = mid;
                r = mid - 1;
            }
            else if(nums[mid]>target) r = mid - 1;
            else if(nums[mid]<target) l = mid + 1;
        }
        // 别忘了重置
        l = 0;
        r = nums.length - 1;
        // 查找到target之后,给res[1]赋值,继续向右左二分查找
        while(r>=l){
            int mid = (l + r) / 2;
            if(nums[mid]==target){
                res[1] = mid;
                l = mid + 1;
            }
            else if(nums[mid]>target) r = mid - 1;
            else if(nums[mid]<target) l = mid + 1;
        }
        return res;
    }
}
posted @ 2022-08-04 13:43  YaosGHC  阅读(31)  评论(0)    收藏  举报