力扣-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 << " "; } }
实测不加那么长一段限定也能过
还有两个细节点是:
- 为什么ans初始化为数组长度?如果不用结果倒推是怎么想到的,是不是只有
[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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步