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;elseleft = 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};elsereturn {-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;elseright = 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;elseleft = 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-----某个数在递增序列第一次和最后一次出现的位置相关推荐

  1. Leetcode 674.最长递增序列

    最长递增序列 给定一个未经排序的整数数组,找到最长且连续的的递增序列. 示例 1: 输入: [1,3,5,4,7] 输出: 3 解释: 最长连续递增序列是 [1,3,5], 长度为3. 尽管 [1,3 ...

  2. leetcode - 674. 最长连续递增序列

    给定一个未经排序的整数数组,找到最长且连续的的递增序列. 示例 1: 输入: [1,3,5,4,7] 输出: 3 解释: 最长连续递增序列是 [1,3,5], 长度为3. 尽管 [1,3,5,7] 也 ...

  3. 二叉搜索树的中序遍历为 递增序列_Go 刷 Leetcode 系列:恢复二叉搜索树

    二叉搜索树中的两个节点被错误地交换. 请在不改变其结构的情况下,恢复这棵树. 示例 1: 输入: [1,3,null,null,2] 1 / 3 \ 2输出: [3,1,null,null,2] 3 ...

  4. LeetCode 674. 最长连续递增序列 (滑动窗口 计数法)

    LeetCode 674. 最长连续递增序列 滑动窗口 右边界不断往右移动 左边界收缩条件:当右边界的值小于等于其左边的值时(递减) 左边界收缩到右边界当前位置 class Solution {pub ...

  5. LeetCode 673. 最长递增子序列的个数

    LeetCode 673. 最长递增子序列的个数 文章目录 LeetCode 673. 最长递增子序列的个数 题目描述 一.解题关键词 二.解题报告 1.思路分析 2.时间复杂度 3.代码示例 2.知 ...

  6. leetcode 674. 最长连续递增序列

    给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度. 连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都 ...

  7. C#LeetCode刷题之#674-最长连续递增序列( Longest Continuous Increasing Subsequence)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3734 访问. 给定一个未经排序的整数数组,找到最长且连续的的递增 ...

  8. 一天一道LeetCode(61-90)

    一天一道LeetCode(61-90) 文章目录 一天一道LeetCode(61-90) 61.旋转链表 62.不同路径 63.不同路径 II 64.最小路径和 65.有效数字(未解决) 66.加一 ...

  9. 每日一道leetcode(python)48. 旋转图像

    每日一道leetcode(python)48. 旋转图像 2021-07-27 给定一个 n × n 的二维矩阵 matrix 表示一个图像.请你将图像顺时针旋转 90 度. 你必须在 原地 旋转图像 ...

最新文章

  1. log4j配置不生效
  2. 【Android 逆向】逆向修改游戏应用 ( 分析应用结构 | 定位动态库位置 | 定位动态库中的修改点 | 修改动态库 | 重打包 )
  3. 13.2System类中的常用方法
  4. 使用nginx动静分离后,druid被拦截的解决方法
  5. 安装ubuntu 13.04
  6. 【Javascript】 DOM节点
  7. Java编程经验汇总
  8. 数据结构中的各种排序---总结篇
  9. Android 中基本图像绘制
  10. 这样安装python库才是最正确的哦_这样安装 Python 库才是最正确的哦~
  11. linux限制组访问权限,linux用户和组管理以及文件权限访问控制ACL策略
  12. linux进程通信中有名管道的特点,linux进程通信之(四):有名管道的读与写
  13. Lua:打印lua表
  14. CentOS 6.5 最小化安装zabbix
  15. 使用HttpClient连接池进行https单双向验证
  16. python3安装pillow后报错没有pillow模块以及没有PIL模块问题解决
  17. Centos 安装Java JDK8
  18. iPad 和 iPhone怎么长截图? iPad截图方法汇总
  19. python实现钉钉群自动警报
  20. 解决:如何卸载WPS的vba宏功能

热门文章

  1. Java黑皮书课后题第1章:1.1(显示三条消息)编写程序,显示Welcome to Java、Welcome to Computer Science和Programming is fun
  2. OpenGL视点跟踪物体运动
  3. 西北工业大学复试上机
  4. P4332 [SHOI2014]三叉神经树(LCT)
  5. Software Engineering 265
  6. Codeforces 766E Mahmoud and a xor trip(树形DP)
  7. 【LeetCode】191. Number of 1 Bits
  8. C#(WinForm) + MySQL的中文编码问题(MySQL中文编码的终极解决方案)
  9. 牛客网(剑指offer) 第十五题 反转链表
  10. kali安装tools