LeetCode27. 移除元素
https://leetcode-cn.com/problems/remove-element/
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
思路
使用双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
删除过程如下:
代码
class Solution
{
public:
int removeElement(vector<int>& nums, int val)
{
int sz = nums.size();
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < sz; ++fastIndex)
{
if (nums[fastIndex] != val)
{
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
};
LeetCode209. 长度最小的子数组
https://leetcode-cn.com/problems/minimum-size-subarray-sum/
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
思路(暴力)
两个for循环,然后不断的寻找符合条件的子序列
代码
class Solution
{
public:
//暴力
int minSubArrayLen(int s, vector<int>& nums)
{
auto sz = nums.size();
int result = INT_MAX;
int subLength = 0;
for (size_t i = 0; i != sz; ++i)
{
int sum = 0;
for (size_t j = i; j != sz; ++j)
{
sum += nums[j];
if (sum >= s)
{
subLength = j - i + 1;
result = min(result, subLength);
break;
}
}
}
return result == INT_MAX ? 0 : result;
}
}
思路(滑动窗口)
以题目中的示例来举例,s=7, 数组是 2,3,1,2,4,3,看一下查找的过程:
使用双指针法(滑动窗口)。
使用滑动窗口,主要确定如下三点:
-
窗口内是什么?
窗口就是 满足其和 ≥ s 的连续子数组。
-
如何移动窗口的起始位置?
如果当前窗口的值大于s了,窗口就要起始位置向前移动一格。
-
如何移动窗口的结束位置?
窗口的结束位置就是遍历数组的指针。
代码
class Solution
{
public:
//滑动窗口
int minSubArrayLen(int s, vector<int>& nums)
{
auto sz = nums.size();
int result = INT_MAX;
int subLength = 0;
size_t i = 0;
int sum = 0;
for (size_t j = 0; j != sz; ++j)
{
sum += nums[j];
//以滑动窗口起始位置为起始位置的子数组已经遍历完毕,将滑动窗口起始位置向前移动一位
//应使用while循环
while (sum >= s)
{
subLength = j - i + 1;
result = min(result, subLength);
sum -= nums[i++];
}
}
return result == INT_MAX ? 0 : result;
}
};
LeetCode15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
思路
使用双指针法。
首先将数组排序,
然后使用一层for循环,i从下标0的地方开始,同时定一个left 定义在i+1的位置上,定义right 在sz - 1的位置上。在数组找到nums[i] + nums[left] + nums[right] == 0,(nums[i], nums[left], nums[right])构成符合条件的三元组。
接下来移动left 和right, 如果nums[i] + nums[left] + nums[right] > 0就说明此时三数之和大了(因为数组经过排序),所以right下表就应该向左移动,这样才能让三数之和小一些;如果 nums[i] + nums[left] + nums[right] < 0说明此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
代码
class Solution
{
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
vector<vector<int>> result;
int sz = nums.size();
if (sz < 3)
{
return result;
}
sort(nums.begin(), nums.end());
for (int i = 0; i < sz; ++i)
{
if (i >= 1 && nums[i] == nums[i - 1])
{
continue;
}
int left = i + 1, right = sz - 1;
while (left < right)
{
if (nums[i] + nums[left] + nums[right] > 0)
{
--right;
}
else if (nums[i] + nums[left] + nums[right] < 0)
{
++left;
}
else
{
result.push_back({nums[i], nums[left], nums[right]});
//该去重逻辑应放在找到一个符合条件的三元组之后
while (left < right && nums[left] == nums[left + 1]) ++left;
while (left < right && nums[right] == nums[right - 1]) --right;
//找到答案时,双指针同时收缩
++left;
--right;
}
}
}
return result;
}
};
LeetCode18. 四数之和
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
思路
使用双指针法。
与LeetCode15. 三数之和相似,四数之和的双指针解法是使用两层for循环nums[i] + nums[j]为确定值,依然是循环内有left = j + 1和right = sz - 1作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的四元组。
代码
class Solution
{
public:
vector<vector<int>> fourSum(vector<int>& nums, int target)
{
vector<vector<int>> result;
auto sz = nums.size();
if (sz < 4) return result;
sort(nums.begin(), nums.end());
for (size_t i = 0; i != sz; ++i)
{
if (i >= 1 && nums[i] == nums[i - 1])
{
continue;
}
for (size_t j = i + 1; j != sz; ++j)
{
if (j >= i + 2 && nums[j] == nums[j - 1])
{
continue;
}
size_t left = j + 1, right = sz - 1;
while (left < right)
{
if (nums[i] + nums[j] + nums[left] + nums[right] < target)
{
++left;
}
else if (nums[i] + nums[j] + nums[left] + nums[right] > target)
{
--right;
}
else
{
result.push_back(vector<int>{nums[i], nums[j], nums[left], nums[right]});
while (left < right && nums[left] == nums[left + 1]) ++left;
while (left < right && nums[right] == nums[right - 1]) --right;
++left;
--right;
}
}
}
}
return result;
}
};
LeetCode59. 螺旋矩阵II
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
思路
本题就是一个模拟过程,关键在于保持区间形式一致,我采用左闭右开的区间形式。
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
由外向内一圈一圈这么画下去。
照左闭右开的区间形式,来画一圈:
代码
class Solution
{
public:
vector<vector<int>> generateMatrix(int n)
{
vector<vector<int>> result(n, vector<int>(n, 0));
int loop = n / 2; //画圈的次数,例如:若n = 3,则画圈一次,中间一个数字单独处理;若n = 4,则画圈两次
int startx = 0, starty = 0; //遍历的横纵坐标起点
int offset = 1; //用于控制每次画圈每一条边的长度
int count = 1; //遍历填充的数字
while (loop--)
{
int i = startx, j = starty;
//四边,左闭右开
//从左到右
for (j = starty; j < starty + n - offset; ++j)
{
result[startx][j] = count++;
}
//从上到下
for (i = startx; i < startx + n - offset; ++i)
{
result[i][j] = count++;
}
//从右到左
for (; j > starty; --j)
{
result[i][j] = count++;
}
//从下到上
for (; i > startx; --i)
{
result[i][j] = count++;
}
//横纵坐标加1
++startx;
++starty;
offset += 2;
}
//奇数,正中心单独处理
if (n % 2)
{
result[n / 2][n / 2] = n * n;
}
return result;
}
};