一、同向双指针
209. 长度最小的子数组
给定一个含有
n
个正整数的数组和一个正整数target
。找出该数组中满足其总和大于等于
target
的长度最小的 连续子数组[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回0
。输入:target = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 输入:target = 4, nums = [1,4,4] 输出:1
int minSubArrayLen(int target, int* nums, int numsSize) {
int l=0,r=0;
int sum=0;
int ans=INT_MAX;
while(r<numsSize){
sum+=nums[r];
while(sum>=target){
ans=fmin(ans,r-l+1);
sum-=nums[l];
l++;
}
r++;
}
return ans==INT_MAX?0:ans;
}
713. 乘积小于 K 的子数组
给你一个整数数组
nums
和一个整数k
,请你返回子数组内所有元素的乘积严格小于k
的连续子数组的数目。输入:nums = [10,5,2,6], k = 100 输出:8 解释:8 个乘积小于 100 的子数组分别为:[10]、[5]、[2],、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。 需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。
int numSubarrayProductLessThanK(int* nums, int numsSize, int k) {
int l=0,r=0;
int sum=1;
int ans=0;
while(r<numsSize){
sum*=nums[r];
//注意此处l不能超过r
while(l<=r&&sum>=k){
sum/=nums[l];
l++;
}
ans+=r-l+1;
r++;
}
return ans;
}
LCR 016. 无重复字符的最长子串
给定一个字符串
s
,请你找出其中不含有重复字符的 最长连续子字符串 的长度。输入: s = "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子字符串是 "abc",所以其长度为 3。 输入: s = "bbbbb" 输出: 1 解释: 因为无重复字符的最长子字符串是 "b"所以其长度为 1。
此题不是只有字母,因此无法通过,有除字母以外的字符之外出现的话需要用map进行记录。
int lengthOfLongestSubstring(char * s){
if(s[0]==' ') return 1;
int flag[256]={0};
int l=0,r=0;
int ans=0;
int n=strlen(s);
while(r<n){
flag[s[r]]++;
while(flag[s[r]]>1){
flag[s[l]]--;
l++;
}
ans=fmax(ans,r-l+1);
r++;
}
return ans;
}
c++版本:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n=s.size();
int left=0;
int answer=0;
set<char> st;
for(int right=0;right<n;++right){
while(st.find(s[right])!=st.end()){//若集合中已经出现过
st.erase(s[left]);//讲其从集合中删去
++left;//左指针右移
}
st.insert(s[right]);//若出现过,则加入集合中
answer=max(answer,right-left+1);//更新最大长度
}
return answer;
}
};
LCR 119. 最长连续序列
给定一个未排序的整数数组
nums
,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。输入:nums = [100,4,200,1,3,2] 输出:4 解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
int cmp(int* a,int* b){
return *a-*b;
}
int longestConsecutive(int* nums, int numsSize){
if(numsSize==0) return 0;
qsort(nums,numsSize,sizeof(int),cmp);
int ans=1,cur=1;
int l=0,r=1;
while(r<numsSize){
if(nums[r-1]+1==nums[r]){
cur++;
ans=fmax(ans,cur);
}
//若遇到相同元素,什么也不做
//若遇到不同元素,且不连续,就更新各值
else if(nums[r-1]!=nums[r]){
l=r;
cur=1;
}
r++;
}
return ans;
}
2414. 最长的字母序连续子字符串的长度
字母序连续字符串 是由字母表中连续字母组成的字符串。换句话说,字符串
"abcdefghijklmnopqrstuvwxyz"
的任意子字符串都是 字母序连续字符串 。
- 例如,
"abc"
是一个字母序连续字符串,而"acb"
和"za"
不是。给你一个仅由小写英文字母组成的字符串
s
,返回其 最长 的 字母序连续子字符串 的长度。输入:s = "abacaba" 输出:2 解释:共有 4 个不同的字母序连续子字符串 "a"、"b"、"c" 和 "ab" 。 "ab" 是最长的字母序连续子字符串。
int longestContinuousSubstring(char* s) {
int ans=1,n=strlen(s);
int count=1;
for(int i=1;i<n;++i){
if(s[i-1]+1==s[i]){
count++;
}else{
count=1;
}
ans=fmax(ans,count);
}
return ans;
}
674. 最长连续递增序列
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标
l
和r
(l < r
)确定,如果对于每个l <= i < r
,都有nums[i] < nums[i + 1]
,那么子序列[nums[l], nums[l + 1], ..., nums[r - 1], nums[r]]
就是连续递增子序列。输入:nums = [1,3,5,4,7] 输出:3 解释:最长连续递增序列是 [1,3,5], 长度为3。
双指针
int findLengthOfLCIS(int* nums, int numsSize) {
if(numsSize==0) return 0;
int ans=1;
int l=0,r=1;
while(r<numsSize){
if(nums[r-1]<nums[r]){
r++;
ans=fmax(ans,r-l);
}else{
l=r;
r++;
}
}
return ans;
}
朴素解法,计数
int findLengthOfLCIS(int* nums, int numsSize) {
if(numsSize==0) return 0;
int ans=1;
int count=1;
for(int i=1;i<numsSize;++i){
if(nums[i-1]<nums[i]){
count++;
}else{
count=1;
}
ans=fmax(ans,count);
}
return ans;
}
1574. 删除最短的子数组使剩余数组有序
给你一个整数数组
arr
,请你删除一个子数组(可以为空),使得arr
中剩下的元素是 非递减 的。一个子数组指的是原数组中连续的一个子序列。请你返回满足题目要求的最短子数组的长度。输入:arr = [1,2,3,10,4,2,3,5] 输出:3 解释:我们需要删除的最短子数组是 [10,4,2] ,长度为 3 。剩余元素形成非递减数组 [1,2,3,3,5] 。 另一个正确的解为删除子数组 [3,10,4] 。
int findLengthOfShortestSubarray(int* arr, int arrSize){
int r=arrSize-1;
while(r&&arr[r-1]<=arr[r]){
r--;
}
//当前数列已经是递增的
if(r==0) return 0;
int ans=r;
//枚举左区间起点
for(int l=0;l==0||arr[l-1]<=arr[l];++l){
while(r<arrSize&&arr[r]<arr[l]){
r++;
}
ans=fmin(ans,r-l-1);
}
return ans;
}
1004. 最大连续1的个数 III
给定一个二进制数组
nums
和一个整数k
,如果可以翻转最多k
个0
,则返回 数组中连续1
的最大个数 。输入:nums = [1,1,1,0,0,0,1,1,1,1,0], K = 2 输出:6 解释:[1,1,1,0,0,1,1,1,1,1,1] 粗体数字从 0 翻转到 1,最长的子数组长度为 6。输入:nums = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3 输出:10 解释:[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1] 粗体数字从 0 翻转到 1,最长的子数组长度为 10。
int longestOnes(int* nums, int numsSize, int k) {
int l=0,r=0;
int zero=0;
int ans=0;
while(r<numsSize){
if(nums[r]==0) zero++;
while(zero>k){
if(nums[l]==0) zero--;
l++;
}
ans=fmax(ans,r-l+1);
r++;
}
return ans;
}
1234. 替换子串得到平衡字符串
有一个只含有
'Q', 'W', 'E', 'R'
四种字符,且长度为n
的字符串。假如在该字符串中,这四个字符都恰好出现
n/4
次,那么它就是一个「平衡字符串」。给你一个这样的字符串s
,请通过「替换一个子串」的方式,使原字符串s
变成一个「平衡字符串」。你可以用和「待替换子串」长度相同的 任何 其他字符串来完成替换。请返回待替换子串的最小可能长度。如果原字符串自身就是一个平衡字符串,则返回
0
。输入:s = "QWER" 输出:0 解释:s 已经是平衡的了。输入:s = "QQWE" 输出:1 解释:我们需要把一个 'Q' 替换成 'R',这样得到的 "RQWE" (或 "QRWE") 是平衡的。
int balancedString(char* s) {
int hash[26]={0};
int l=0,r=0;
int len=strlen(s);
int n=len/4;
int ans=len;
//记录每个字母出现的次数
for(int i=0;i<len;++i){
hash[s[i]-'A']++;
}
//如果满足条件,直接返回0
if(hash['Q'-'A']==n&&hash['Q'-'A']==n&&hash['W'-'A']==n&&hash['R'-'A']==n){
return 0;
}
//快指针向前遍历
for(r=0;r<len;++r){
//快指针每走一步状态更新
hash[s[r]-'A']--;
//慢指针按条件收缩窗口
while(l<=r&&hash['Q'-'A']<=n&&hash['W'-'A']<=n&&hash['E'-'A']<=n&&hash['R'-'A']<=n){
//慢指针每走一步状态更新
hash[s[l]-'A']++;
//记录窗口最值
ans=fmin(ans,r-l+1);
l++;
}
}
return ans;
}
1658. 将 x 减到 0 的最小操作数
给你一个整数数组
nums
和一个整数x
。每一次操作时,你应当移除数组nums
最左边或最右边的元素,然后从x
中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。如果可以将x
恰好 减到0
,返回 最小操作数 ;否则,返回-1
。输入:nums = [1,1,4,2,3], x = 5 输出:2 解释:最佳解决方案是移除后两个元素,将 x 减到 0 。
方法一:逆向思维+双指针
把问题转换成「从 nums 中移除一个最长的子数组,使得剩余元素的和为 x。换句话说,要从 nums 中找最长的子数组,其元素和等于 s−x,这里 s 为 nums 所有元素之和。
int minOperations(int* nums, int numsSize, int x) {
//从nums中移除一个最长的子数组,使得剩余元素的和为x
int target=0;
for(int i=0;i<numsSize;++i){
target+=nums[i];
}
target-=x;
if(target<0) return -1;
int l=0,r=0;
int ans=-1,sum=0;
for(r=0;r<numsSize;++r){
sum+=nums[r];
while(sum>target){
sum-=nums[l];
l++;
}
if(sum==target){
ans=fmax(ans,r-l+1);
}
}
return ans<0?-1:numsSize-ans;
}
二、对撞双指针
15. 三数之和
给你一个整数数组
nums
,判断是否存在三元组[nums[i], nums[j], nums[k]]
满足i != j
、i != k
且j != k
,同时还满足nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为
0
且不重复的三元组。注意:答案中不可以包含重复的三元组。
输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]] 解释: nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。 nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。 nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。 不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。 注意,输出的顺序和三元组的顺序并不重要。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++) {
// 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
if (nums[i] > 0) {
return result;
}
if (i > 0 && nums[i] == nums[i - 1]) {//去重
continue;
}
int left = i + 1;
int right = nums.size() - 1;
while (right > left) {
if (nums[i] + nums[left] + nums[right] > 0) right--;
else if (nums[i] + nums[left] + nums[right] < 0) left++;
else {
result.push_back(vector<int>{nums[i], nums[left], nums[right]});
// 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
while (right > left && nums[right] == nums[right - 1]) right--;
while (right > left && nums[left] == nums[left + 1]) left++;
// 找到答案时,双指针同时收缩
right--;
left++;
}
}
}
return result;
}
};
167. 两数之和 II - 输入有序数组
给你一个下标从 1 开始的整数数组
numbers
,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数target
的两个数。如果设这两个数分别是numbers[index1]
和numbers[index2]
,则1 <= index1 < index2 <= numbers.length
。以长度为 2 的整数数组
[index1, index2]
的形式返回这两个整数的下标index1
和index2
。你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
输入:numbers = [2,7,11,15], target = 9 输出:[1,2] 解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize) {
int *res=malloc(sizeof(int)*2);
*returnSize=2;
int l=0,r=numbersSize-1;
while(l<r){
int sum=numbers[l]+numbers[r];
if(sum<target){
l++;
}else if(sum>target){
r--;
}else{
res[0]=l+1;res[1]=r+1;
return res;
}
}
return res;
}
11. 盛最多水的容器
给定一个长度为
n
的整数数组height
。有n
条垂线,第i
条线的两个端点是(i, 0)
和(i, height[i])
。找出其中的两条线,使得它们与x
轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。输入:[1,8,6,2,5,4,8,3,7] 输出:49 解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
int maxArea(int* height, int heightSize) {
int mmax=0;
int i=0,j=heightSize-1;
while(i<j){
int cur=fmin(height[i],height[j])*(j-i);
if(cur>mmax) mmax=cur;
if(height[i]<height[j]){
i++;
}else{
j--;
}
}
return mmax;
}