代码题(65)— 在排序数组中查找元素的第一个和最后一个位置、长度最小的子数组
1、34. 在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:输入:nums = [], target = 0
输出:[-1,-1]
O(logn) 的算法,使用两次二分查找法,第一次找到左边界,第二次调用找到右边界即可,具体代码如下:
class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { vector<int> res(2, -1); if(nums.empty()) return res; int low = 0; int high = nums.size()-1; while(low<high){ int mid = (low+high)/2; if(nums[mid]>=target) // 查找左边界 high = mid; else low = mid+1; } if(nums[low] != target) return res; res[0] = low; low = 0; high = nums.size()-1; while(low<high){ int mid = (low+high+1)/2; if(nums[mid]<=target) // 查找右边界,high 需要向上取整 low = mid; else high = mid-1; } res[1] = low; return res; } };
2、209. 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:输入:target = 4, nums = [1,4,4]
输出:1
示例 3:输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
解法一:这道题目暴力解法当然是 两个for循环,然后不断的寻找符合条件的子序列,时间复杂度很明显是O(n^2) 。时间复杂度:O(n^2);空间复杂度:O(1)
class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int res = INT32_MAX; if(nums.empty()) return res; int sum = 0; int subLength = 0; for(int i=0;i<nums.size();++i){ sum = 0; for(int j=i;j<nums.size();++j){ sum += nums[j]; if(sum >= target){ // 一旦发现子序列和超过了s,更新result subLength = j-i+1; // 取子序列的长度 res = res<subLength?res:subLength; break;// 因为我们是找符合条件最短的子序列,所以一旦符合条件就break } } } return res == INT32_MAX?0:res; } };
解法二:
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
这里还是以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,来看一下查找的过程:
class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int res = INT32_MAX; int sum = 0;// 滑动窗口数值之和 int i = 0;// 滑动窗口起始位置 int subLength = 0;// 滑动窗口的长度 for(int j=0;j<nums.size();++j){ sum += nums[j]; // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件 while(sum >= target){ subLength = j-i+1;// 取子序列的长度 res = res<subLength?res:subLength; sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置) } } // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列 return res == INT32_MAX ? 0 : res; } };