每天一道LeetCode-----某个数在递增序列第一次和最后一次出现的位置
来源:互联网 发布:推广平台源码 编辑:程序博客网 时间:2024/05/19 11:48
Search for a Range
原题链接Search for a Range
给定一个递增序列和一个值,找到该值在序列中出现的范围,实际上就是找到该值第一次出现和最后一次出现的位置。如果没有,返回[-1,-1]
递增序列肯定是二分了,正常二分法查找算法如下,是通过判断中间位置的值与给定值的大小关系,从而将区间变为原来的一半,继续查找,不断的一半,一半,最后变成只有一个元素的区间,比较后返回。
int binary_find(vector<int>& nums, int target){ int left = 0; int right = nums.size(); while(left <= right) { int middle = (left + right) / 2; if(nums[middle] == target) return middle; else if(nums[middle] > target) right = middle - 1; else left = middle + 1; } return -1;}
普通的二分法查找到一个相等的值就结束了,但是这里需要确定这个值第一次出现和最后一次出现的位置。所以很明显不能让它结束这么快,也就是说即使nums[middle] == target,也不返回,因为目的是要找一个范围,即两个边界,而middle只是一个点,不一定是边界,有可能middle前面和后面也都是等于target的位置。
但是又因为二分法最后肯定会收敛到一个点,不能直接找范围,所以可以先找左边界,再找右边界
二分法需要保证,如果序列中存在目标元素target,那么最后收敛到的位置的值一定是target
对于左边界
考虑二分法的实现,每次找到middle后比较nums[middle]和target的大小关系
- 如果nums[middle] > target,说明target在[left, middle)区间,改变right = middle - 1;
- 如果nums[middle] < target,说明target在(middle, right]区间,改变left = middle + 1;
- 如果nums[middle] == target,说明第一次出现target的位置在[left, middle]内,改变right = middle。因为middle位置有可能就是第一次出现target的位置,所以不能让right = middle - 1;
对于右边界
考虑二分法的实现,每次找到middle后比较nums[middle]和target的大小关系
- 如果nums[middle] > target,说明target在[left, middle)区间,改变right = middle - 1;
- 如果nums[middle] < target,说明target在(middle, right]区间,改变left = middle + 1;
- 如果nums[middle] == target,说明最后一次出现target的位置在[middle, right]内,改变left = middle。因为middle位置有可能就是最后一次出现target的位置,所以不能让right = middle - 1;
但是!考虑一种情况,求右边界时,某次区间[left, right]长度只有2,也就是说right = left + 1,这就导致middle = left。如果nums[middle] == target,根据上面的式子,另left = middle,此时left根本没有变化,也就是说改变left后区间根本没有更新,会陷入无限循环
这种问题只出现在求右边界的情况,原因是在求左边界时,right不可能和middle相等,所以每次区间都会变小,不会出现上面的问题
怎么解决呢,可以当区间长度为2时手动判断nums[left]和nums[right]。因为目的是求最后一个target出现的位置,而right的位置是区间的最右边,所以如果nums[right] == target,那么根本就不需要再找了,right就是最后一次出现target的位置 ,而如果nums[right] != target,那么right -= 1将right左移。
代码如下
class Solution {public: vector<int> searchRange(vector<int>& nums, int target) { if(nums.empty()) return {-1, -1}; int front = equalLeftBound(nums, target); int back = equalRightBound(nums, target); if(front < nums.size() && back >= 0 && nums[front] == target && nums[back] == target) return {front, back}; else return {-1, -1}; }private: /* 寻找第一次出现target的位置 */ int equalLeftBound(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; while(left < right) { int middle = (left + right) / 2; /* * if(nums[middle] < target) * left = middle + 1; * else if(nums[middle] > target) * right = middle - 1; * else * right = middle; */ /* * 这里将nums[middle] > target和nums[middle] == target合在一起 * 对于left是否需要加一可以通过nums[middle]是否等于target判断 * 因为right永远不会和middle相等,所以区间会一直减小,不会出现无限循环 * 怎么写无所无,但是右边界不行 */ if(nums[middle] < target) left = middle + 1; else right = middle; /* * 如果nums[middle] < target导致left = middle + 1后 * nums[left]仍然小于target会导致left继续加一,这里可能出现left > right的情况 * 如果此时right = nums.size() - 1,那么left就越界了 * 返回的left也就越界了,需要在返回后判断 */ if(nums[left] == target) break; else ++left; } return left; } /* 寻找最后一次出现target的位置 */ int equalRightBound(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; while(left < right) { /* * 这里只能合在一起,因为left可能和middle相等,导致区间根本没有更新,导致无限循环 * 所以需要改变区间,从而将区间缩小 */ int middle = (left + right) / 2; if(nums[middle] > target) right = middle - 1; else left = middle; /* 如果右边界就是target,直接返回即可,否则需要将right减小,因为最后的结果是right */ /* * 同理左边界,如果nums[middle] > target导致right = middle - 1 * 而nums[right]仍然大于target,会导致right继续减一,可能出现right < left的情况 * 如果此时left = 0,那么right就越界了,需要在返回后判断是否越界 */ if(nums[right] == target) break; else --right; } return right; }};
- 每天一道LeetCode-----某个数在递增序列第一次和最后一次出现的位置
- Java 查找某个数字在数组中第一次和最后一次出现的位置
- 统计一个字符串中某个字符第一次出现的位置和最后一次出现的位置
- 二分 第一次出现的位置,最后一次出现的位置
- 二分元素第一次出现的位置和最后一次出现的位置
- 如何找到二分查找中目标元素第一次出现和最后一次出现的位置
- sql怎么取某个字符串最后一次出现的位置后面的字符串&&sql 取得某字符串第一次出现的位置前面的字符串
- (核心算法)查找某个数在有序数组中(递增,含重复)第一次出现的下标
- 每天一道LeetCode-----给定字符串s和字符数组words,在s中找到words出现的位置,words内部字符串顺序无要求
- 每天一道LeetCode-----找到一个字符串在另一个字符串出现的位置,字符串内部顺序无要求
- Java查找指定字符串第一次或最后一次出现的位置
- 每天一道LeetCode-----寻找地增序列中第一个大于等于目标元素的位置
- 每天一道LeetCode-----在字符方格中查找某个单词
- 每天一道LeetCode-----在有序的二维数组中查找某个元素
- mysql 将一个字符串按某个字符串出现的最后一次位置来拆分成两个字符串
- 输出递增序列中不重复的两个数和等于某个给定的值
- 每天一道LeetCode-----找出给定序列的所有子序列
- 查找指定字符串在字符串中第一次或最后一词出现的位置
- js闭包
- 字符串id生成示例
- STM32 PWM调制信号关闭后端口电平不确定处理
- Tetris_Java(I)千里之行始于足下
- Parallel&Distributed Algorithm-1
- 每天一道LeetCode-----某个数在递增序列第一次和最后一次出现的位置
- AFN提交类型和回应类型
- SpringBatch配置多线程step
- JavaWeb框架-【Hibernate+Struts2】-框架世界的联手-图书管理系统
- LLVM学习笔记(21)
- leetcode 6 ZigZag Conversion
- Android,沉浸式状态栏,状态栏以及Toolbar颜色分开设置
- 提示错误:A child Container failed during start
- C99中的for语句