2022-08-04 13:43阅读: 24评论: 0推荐: 0

力扣-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;
}
}

本文作者:YaosGHC

本文链接:https://www.cnblogs.com/yaocy/p/16546268.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   YaosGHC  阅读(24)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起