12.水果成篮(904)
题目
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits
表示,其中 fruits[i]
是第 i
棵树上的水果 种类 。
你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
- 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
- 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
- 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组 fruits
,返回你可以收集的水果的 最大 数目。
示例 1:
输入:fruits = [1,2,1]
输出:3
解释:可以采摘全部 3 棵树。
示例 2:
输入:fruits = [0,1,2,2]
输出:3
解释:可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。
示例 3:
输入:fruits = [1,2,3,2,2]
输出:4
解释:可以采摘 [2,3,2,2] 这四棵树。
如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。
示例 4:
输入:fruits = [3,3,3,1,2,1,1,2,3,3,4]
输出:5
解释:可以采摘 [1,2,1,1,2] 这五棵树。
提示:
1 <= fruits.length <= 105
0 <= fruits[i] < fruits.length
思路及题解
解法一 滑动窗口
- 本题本质为求只有两种元素的最长子数组大小,考虑用滑动窗口
- 左指针和右指针表示满足条件的窗口的左右边界
- 用哈希表来存储每个数字及其出现的次数(原先有考虑最后一个元素出现的位置、用
count
变量记录元素出现的次数等想法,但均难以实现,以后有键-值对形式的数据考虑用哈希表) - 当哈希表中的元素小于等于两个时,右指针不断向右移动,同时将指向的数字填入哈希表中
- 当哈希表中的元素数量大于2,即出现了第3种数字时,将左指针指向的数字一个个删除,直到某个数字被删完为止,这时的数组为新的满足条件的子数组
- 注意题目条件,当遇到第三个数字时即结束遍历,即使后面还有在这两种数字之中的数字,也不能计数
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int n = fruits.size();
map<int, int> m;
int left = 0, ans = 0;
for (int right = 0; right < n; right++) {
m[fruits[right]]++;
while (m.size() > 2) {
map<int, int>::iterator pos = m.find(fruits[left]);
//注意这里pos是迭代器,不能写pos--
pos->second--;
if (pos->second == 0) {
m.erase(pos);
}
left++;
}
ans = max(ans, right - left + 1);
}
return ans;
}
};
13.螺旋矩阵 II(59)
题目
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
示例 1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入:n = 1
输出:[[1]]
提示:
1 <= n <= 20
思路及题解
解法一 模拟
- 这道题关键是明白循环不变量是什么
- 可以将螺旋矩阵拆分为一次又一次地转圈,一共需要转 n/2 圈
- 确定边界条件尤为重要,分清楚每边是左闭右闭还是左闭右开等,并在每次转圈保持一致,不能这边是一种方式,另外一边又是一种方式,转着转着就转晕了
- 需要注意的是n2为奇数时,最中心的数没有遍历到,需要在最后做一个判断,手动加入
- 这种题直接大胆写代码,在数学上分析、再画图等容易晕
- 真正解决题目的代码都是简洁的,或者有原则性的,而不是一波波的判断,拆了东墙补西墙,代码十分冗余
//这里是左闭右开的写法
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
//注意vector创建二维数组的方法
vector<vector<int>> array(n, vector<int>(n, 0));
//startx 、 starty表示每次新的转圈开始时,行号和列号的起始值
//offset 意为“抵消”,在这里控制每次转圈到达的范围
//count 用于填充数字
int times = 0, startx = 0, starty = 0, offset = 1, count = 1;
int i = 0, j = 0;
//times记录绕圈的次数,达到n/2时退出while
while (times < n / 2) {
//注意要初始化i和j,如果只在前两个循环初始化,遍历到前两个循环不执行时,后面两个循环就会出错
i = startx, j = starty;
//下面四个for为模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (; j < n - offset; j++)
array[i][j] = count++;
// 模拟填充右列从上到下(左闭右开)
for (; i < n - offset; i++)
array[i][j] = count++;
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--)
array[i][j] = count++;
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--)
array[i][j] = count++;
//变量均加一
// 第二圈开始的时候,起始位置要各自加1
startx++;
starty++;
times++;
offset++;
}
//n^2^为奇数时特殊判断
if (n % 2)
array[n / 2][n / 2] = n * n;
return array;
}
};
14.螺旋矩阵(54)
题目
给你一个 m
行 n
列的矩阵 matrix
,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例 1:
输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]
示例 2:
输入:matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100
思路及题解
解法一 模拟
- 和上题的思路一模一样,注意循环不变量即可
- 由于题目要求的结果与上题不同,这里while的循环条件需要变化
- 同样,当矩阵为 n*n 且 n2为奇数时,最后一个数填不进去,需要额外进行判断
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
//注意 matrix 为空的情况
if(array.size() == 0 || array[0].size() == 0) return {};
int startx = 0, starty = 0, m = matrix.size(), n = matrix[0].size();
vector<int> ans;
//当ans的大小达到max值时,所有数字都已经填入,结束遍历
int i, j, offset = 1, max = m * n;
//当矩阵为 n*n 且 n^2^为奇数时,最后一个数填不进去,max值在原来基础上减一
if (m == n && m % 2 == 1)
max--;
while (ans.size() < max) {
i = startx;
j = starty;
for (; j < n - offset && ans.size() < max; j++)
ans.push_back(matrix[i][j]);
for (; i < m - offset && ans.size() < max; i++)
ans.push_back(matrix[i][j]);
//注意这里是 j > starty,而不是 j >= starty
for (; j > starty && ans.size() < max; j--)
ans.push_back(matrix[i][j]);
for (; i > startx && ans.size() < max; i--)
ans.push_back(matrix[i][j]);
startx++;
starty++;
offset++;
}
if (m == n && m % 2 == 1)
//把最后一个数加上,中间的数是 matrix [m / 2][n / 2]
ans.push_back(matrix[m / 2][n / 2]);
return ans;
}
};