题目链接
代码随想录文章讲解链接
比较左右两端的元素的平方的大小,选取大的放置到结果数组的末端,然后向中间遍历。因为剩余元素中,平方值最大的元素一定在左右两头。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int left = 0;
int right = nums.size() - 1;
vector<int> res(nums.size(), 0);
int i = right;
while (left <= right) {
if (nums[left] * nums[left] > nums[right] * nums[right]) {
res[i] = nums[left] * nums[left];
++left;
} else {
res[i] = nums[right] * nums[right];
--right;
}
--i;
}
return res;
}
};
找到数组负数与非负数的分界点,设置两个指针从分界点处向两端遍历,依次把平方值小的元素放到结果数组中。思路与方法一类似,反过来而已,但是方法一更优,无需判断指针是否位于边界,且也不用事先寻找临界位置。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
int size = nums.size();
int left = size - 1;
int right = size;
vector<int> res;
// 找到边界位置
for (int i = 0; i < size; ++i) {
if (nums[i] >= 0) {
left = i - 1;
right = i;
break;
}
}
while (left >= 0 || right < size) {
if (left < 0) {
res.push_back(nums[right] * nums[right++]);
} else if (right >= size) {
res.push_back(nums[left] * nums[left--]);
} else {
if (nums[left] * nums[left] < nums[right] * nums[right]) {
res.push_back(nums[left] * nums[left--]);
} else {
res.push_back(nums[right] * nums[right++]);
}
}
}
return res;
}
};
无
无
题目链接
代码随想录文章讲解链接
维护一个滑动窗口,当窗口内的和小于目标值时,窗口右端往前进,因为只有新增值才有可能达到目标值;
当和大于目标值时,此时的窗口满足要求,记录长度后窗口左端往前进,因为减去一个值也有可能满足要求,并且长度更短。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int left = 0;
int right = 0;
int size = nums.size();
int res = 0;
int sum = nums[0];
while (right < size) {
if (sum < target) {
++right;
if (right < size) sum += nums[right];
} else {
if (res == 0) res = right - left + 1;
else res = min(res, right - left + 1);
sum -= nums[left++];
}
}
return res;
}
};
暴力解法是两层for循环找到满足条件的数组,时间复杂度为 O ( n 2 ) O(n^2) O(n2),其中内层的for循环可以优化,由于数组都是正数,数组的前缀和是单调递增的数组,单调递增就想到二分查找。
首先计算数组的前缀和,用另外一个数组存储,然后以每个位置为开头,查找满足条件的长度,其实就是二分查找寻找当前位置前缀和加上目标值的位置。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int size = nums.size();
vector<int> preSum(size + 1, 0);
int res = 0;
// 计算前缀和并存储
for (int i = 0; i < size; ++i) {
preSum[i + 1] = preSum[i] + nums[i];
}
// 以每个位置为开头,找到满足条件的最短的数组
for (int j = 0; j < size + 1; ++j) {
// 要查找的目标是当前位置的前缀和加上目标值
int t = target + preSum[j];
int left = j;
int right = size;
// 若存在刚好等于t的前缀和则找到的是对应位置
// 若不存在刚好等于t的前缀和,则找到比t大的最小值的位置
while (left <= right) {
int mid = (right - left) / 2 + left;
if (preSum[mid] < t) left = mid + 1;
else if (preSum[mid] > t) right = mid - 1;
else {
left = mid;
break;
}
}
if (left <= size) {
if (res == 0) res = left - j;
else res = min(res, left - j);
}
}
return res;
}
};
方法二虽然性能没有方法一好,但是这种算法优化思想要掌握,特别是如果条件中有数组元素都是正整数,可以考虑前缀和,前缀和又是单调递增,可以考虑二分查找。
方法二中,保存前缀和的数组的长度要比原来的数组长度多一位,即第一位0。
题目链接
kind1
和kind2
记录目前窗口内的的水果种类,last_kind1
和last_kind2
记录两种水果目前最右边的位置,最右边的位置是用于更新窗口左边界。
判断新增的水果的种类是否是kind1
或者kind2
,若是的话继续移动右边界,并更新last_kind
;
若不是的话,则更新结果,再将其中一个种类替换成目前的新增的种类,新增水果左侧的水果种类是要保留的,另一个是要替换掉的,左边界也要右移至丢弃种类水果最右边位置的右边。(通俗说法:来了第三者,要与离它最近的水果组成新的cp,窗口内就要丢弃掉另外一种水果,所以左边界就要把要丢弃的水果全部排出。)
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int kind1 = fruits[0];
int kind2 = -1;
int last_kind1 = 0;
int last_kind2 = -1;
int left = 0;
int right = 0;
int size = fruits.size();
int res = 0;
while (right < size) {
if (fruits[right] == kind1) {
last_kind1 = right;
++right;
} else if (fruits[right] == kind2) {
last_kind2 = right;
++right;
} else if (kind2 == -1) {
last_kind2 = right;
kind2 = fruits[right++];
} else {
res = max(res, right - left);
if (fruits[right - 1] == kind1) {
left = last_kind2 + 1;
last_kind2 = right;
kind2 = fruits[right++];
} else {
left = last_kind1 + 1;
last_kind1 = right;
kind1 = fruits[right++];
}
}
}
return max(res, right - left);
}
};
滑动窗口的精髓在于如何移动左右边界,一定要搞清楚移动的逻辑。
无。
今日有两道中等难度的题目,已经是满头大汗了。看到递增就要考虑到二分查找,看到子数组就要考虑到滑动窗口,牢记,牢记,要记到形成条件反射才行。