【LeetCode】No.34 Search for a Range
来源:互联网 发布:淘宝京润珍珠是真的吗 编辑:程序博客网 时间:2024/06/15 23:30
【原题】
Given an array of integers sorted in ascending order, find the starting and ending position of a given target value. Your algorithm's runtime complexity must be in the order of O(log n). If the target is not found in the array, return [-1, -1]. For example, Given [5, 7, 7, 8, 8, 10] and target value 8, return [3, 4].
【翻译】
数字在排序数组中出现的次数。
给定一个排好序的数组和一个数字,输出该数字出现的起始位置和结束位置,以及出现的次数(即:结束位置-起始位置+1)。
【解题思路】
排好序的数组→二分查找。
改进的二分查找:
二分查找算法总是先拿数组中间的数字和k作比较。如果中间的数字比k大,那么k只有可能出现在数组的前半段,下一轮我们只在数组的前半段查找就可以了。如果中间的数字比k小,那么k只有可能出现在数组的后半段,下一轮我们只在数组的后半段查找就可以了。如果中间的数字和k相等呢?我们先判断这个数字是不是第一个k。如果位于中间数字的前面一个数字不是k,此时中间的数字刚好就是第一个k。如果中间数字的前面一个数字也是k,也就是说第一个k肯定在数组的前半段,下一轮我们仍然需要在数组的前半段查找。
二分法在数组中查找一个合乎要求的数字时间复杂度是O(logn),因此总的时间复杂度也只有O(logn)。
==============================================================================================
伪代码1:找到开头的k
1、一旦发现start>end,就说明数组中根本没有K,所以返回-1表示出错;
2、计算中点位置,将中点的值和k进行比较:
2.1. 若k小于中点值,则在前半段找
2.2. 若k大于中点值,则在后半段找
2.3. 若k等于中点值:
a)若中点已经是数组第一个元素,则直接返回中点位置;
b)若中点值的前一个元素和中点值不同,则直接返回中点位置;
c)若中点值前一个元素和中点值相同,则继续在前半段找;
3、计算出新的start、end后继续递归。
伪代码2:主函数
1、健壮性判断:若数组为空、数组的第一个元素大于k、最后一个元素小于k,则根本不存在k,直接返回0.
2、分别计算k开头位置、结尾位置
3、只要其中一个为-1则返回-1;否则返回(结尾-开头+1)
===============================================================================================
(1)递归:每次的前半段、后半段有重复的操作,所以用递归,但是每次操作的数组范围不一样,所以应该再加两个参数传入start和end。
class Solution {public: //找起始位置 int GetFirstTarget(vector<int>& nums, int target, int start, int end){ if(start > end){ return -1; } int mid = (start+end)/2; //当nums[mid]=target,又分三种情况 if(nums[mid] == target){ if((mid>0 && nums[mid-1] != target) || mid == 0){//当mid不是第一个且左邻居不同(防止越界) 或者 mid就是数组第一个元素 return mid; } else{//否则在前半段继续找第一次出现的位置 end = mid-1; } } //当nums[mid]>target,在前半段找 else if(nums[mid] > target){ end = mid-1; } //当nums[mid]<target,在后半段找 else{ start = mid+1; } return GetFirstTarget(nums, target, start, end);//缩小要寻找的范围进行递归 } //找结束位置 int GetLastTarget(vector<int>& nums, int target, int start, int end){ if(start > end){ return -1; } int mid = (start+end)/2; //当nums[mid]=target,又分三种情况 if(nums[mid] == target){ if((mid<(nums.size()-1) && nums[mid+1] != target) || mid == nums.size()-1){//当mid不是最后一个且右邻居不同(防止越界) 或者 mid就是数组最后一个元素 return mid; } else{//否则在后半段继续找最后一次出现的位置 start = mid+1; } } //当nums[mid]>target,在前半段找 else if(nums[mid] > target){ end = mid-1; } //当nums[mid]<target,在后半段找 else{ start = mid+1; } return GetLastTarget(nums, target, start, end);//缩小要寻找的范围进行递归 } vector<int> searchRange(vector<int>& nums, int target) { vector<int> result; if(nums.size() <= 0){ result.push_back(-1); result.push_back(-1); } else{ int firstNum = GetFirstTarget(nums, target, 0, nums.size()-1); int lastNum = GetLastTarget(nums, target, 0, nums.size()-1); result.push_back(firstNum); result.push_back(lastNum); } return result; }};
(2)非递归:超时!
//找起始位置 int GetFirstTarget(vector<int>& nums, int target, int start, int end){ if(start > end){ return -1; } int mid = (start+end)/2; while(start <= end){ //当nums[mid]=target,又分三种情况 if(nums[mid] == target){ if((mid>0 && nums[mid-1] != target) || mid == 0){//当mid不是第一个且左邻居不同(防止越界) 或者 mid就是数组第一个元素 return mid; } else{//否则在前半段继续找第一次出现的位置 end = mid-1; } } //当nums[mid]>target,在前半段找 else if(nums[mid] > target){ end = mid-1; } //当nums[mid]<target,在后半段找 else{ start = mid+1; } }//while }
【O(n)的解法】
1、遍历一次,直接找,设置一个标记用来保证不移动start只移动end。可是时间为O(n),不符合。
class Solution {public: vector<int> searchRange(vector<int>& nums, int target) { int start=-1, end = -1; for(int i=0; i<nums.size(); i++){ if(nums[i]==target && start == -1){ start = i; end = i; } else if(start != -1 && nums[i] == target){ end += 1; } } vector<int> v; v.push_back(start); v.push_back(end); return v; }};2、若直接运用二分查找:给出的例子中,可以先用二分查找算法找到一个3。由于3可能出现多次,因此我们找到的3的左右两边可能都有3,于是在找到的3的左右两边顺序扫描,分别找出第一个3和最后一个3。因为要查找的数字在长度为n的数组中有可能出现O(n)次,所以顺序扫描的时间复杂度是O(n)。因此这种算法的效率和直接从头到尾顺序扫描整个数组统计3出现的次数的方法是一样的。
class Solution {public: vector<int> searchRange(vector<int>& nums, int target) { vector<int> result; int low = 0, high = nums.size()-1; int mid; while(low <= high){ mid = (low+high)/2; if(nums[mid] == target) break; else if(nums[mid] > target){ high = mid-1; } else { low = mid+1; } }//找不到跳出循环此时low>high,返回{-1,-1};找到了也跳出循环此时要判断nums[mid]周围情况 //往mid两边顺序扫描,直到找到起始位置和解释位置,最坏为O(n) if(low <= high){ low = mid-1;//把low放在mid的左邻居,看nums[mid-1]等不等于target while(low >=0 && nums[low] == target){//条件1:如果mid=0是数组的第一个元素;条件2:mid左边也是target low--; } high = mid+1; while(high < nums.size() && nums[high] == target){//条件1:如果mid=nums.size()-1是数组的最后一个元素;条件2:mid右边也是target high++; } result.push_back(low + 1); result.push_back(high - 1); } else{ result.push_back(-1); result.push_back(-1); } return result; }};
- Leetcode NO.34 Search for a Range
- LeetCode No.34 Search for a Range
- 【LeetCode】No.34 Search for a Range
- LeetCode(34)Search for a Range
- [LeetCode] Search for a Range [34]
- LeetCode 34 Search for a Range
- [leetcode 34] Search for a Range
- [LeetCode 34]Search for a Range
- leetcode-34 Search for a Range
- Leetcode 34 Search for a Range
- Search for a Range - LeetCode 34
- leetcode 34 -- Search for a Range
- leetcode 34: Search for a Range
- Leetcode#34 Search for a Range
- LeetCode(34) Search for a Range
- LeetCode 34: Search for a Range
- [Leetcode 34, Medium] Search for a Range
- [leetcode-34]Search for a Range(C)
- 移动端知识的一些总结
- 语音通知
- 只能输入带正负号的整数和小数的正则表达式
- 单片机系列(定时器)-2
- Java基础知识之日期操作
- 【LeetCode】No.34 Search for a Range
- css中设置字体的自动降级
- Photoshop 渐变工具使用
- 利用Flume拦截器(interceptors)实现Kafka Sink的自定义规则多分区写入
- java 大数据处理之内存溢出解决办法(一)
- 决策树归纳
- web开发-html和css学习网址-学习笔记二
- 第三次C++上机作业
- 八幅漫画理解使用JSON Web Token设计单点登录系统(简单明了)