1.977.有序数组的平方
class Solution {
public int[] sortedSquares(int[] nums) {
int[] arr = new int[nums.length];
int a = 0, b = nums.length - 1;
for(int i = nums.length - 1; i >= 0; i--){
if(nums[a] * nums[a] < nums[b] * nums[b]){
arr[i] = nums[b] * nums[b];
b--;
}else{
arr[i] = nums[a] * nums[a];
a++;
}
}
return arr;
}
}
讲解用while循环,我用的是for,遇到的问题就是一开始把a++写成了i++,debug的时候发现i一直在3和4打转,发现了问题。
2.209.长度最小的子数组
这道题用到的思想是滑动窗口,不断调节子序列的起始位置和终止位置。暴力解法利用两层for循环,第一层控制起始位置,第二层循环控制终止位置,遍历所有可能的子数组。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int length = Integer.MAX_VALUE;//将默认子序列长度设置为最大值
int l = 0;//滑动窗口的左边界
int sum = 0;
for(int r = 0; r < nums.length; r++){
sum += nums[r];
while(sum >= target){
length = length > (r - l + 1) ? (r - l + 1): length;
sum -= nums[l++];
}
}
return length == Integer.MAX_VALUE? 0 : length;
}
}
两个注意点:
(1)对于子数组长度的默认长度得设置为超大整数:Integer.MAX.VALUE
(2)关于时间复杂度:看每个元素被操作的次数,每个元素在滑动窗后进来一次,出去一次,每个元素都是被操作两次,所以时间复杂度为O(n)
扩展题
904. 水果成篮
题目链接:904
class Solution {
public int totalFruit(int[] fruits) {
int kind = 0;
int[] count = new int[fruits.length + 10];
int l = 0, ans = 0;
for(int r = 0; r < fruits.length; r++){
if(count[fruits[r]] == 0){
kind++;
}//判断水果种类
count[fruits[r]]++;//摘水果放篮子
while(kind > 2){
//水果种类超过2了,需要右移l
count[fruits[l]]--;
if(count[fruits[l]] == 0){
//这是该种类唯一的水果
kind--;
}
l++;
}
ans = ans > (r - l + 1) ? ans : (r - l + 1);
}
return ans;
}
}
这道题的难点在于如何统计水果的种类。通过定义一个数组count,即各种水果的篮子,果子种类为3,那么放入该果子则count[3]自加一。
那么人站在一棵树前,摘果子的步骤为:
(1)判断该果子的种类,是否为新出现的水果
if(count[fruits[r]] == 0){
kind++;
}
(2)将该水果放入对应的篮子中
count[fruits[r]]++;
摘水果的过程已经完成,那么接下来需要判断这蓝水果是否需要,则需要判断此时kind是否超过2,如果超过,则需要调整窗口使得起始位置。
76. 最小覆盖子串
题目链接:76
class Solution {
public String minWindow(String s, String t) {
//通过比较字符串长度将不可能的情况排除掉(这步忘记了)
int sLen = s.length();
int tLen = t.length();
if(sLen == 0 || tLen == 0 || sLen < tLen){
return "";
}
//先把字符串转变为字符数组
char[] sAr = s.toCharArray();
char[] tAr = t.toCharArray();
int[] ss = new int[128];
int[] tt = new int[128];
//窗口滑动时发现,需要定义一个变量来让我知道在什么时候s的子串已经包含了t,先认为是t中字符种类
//但是觉得种类这个变量不行,种类相同并不代表着完全包含
//答案定义了一个变量,来反映s子串中包含t字符的个数
int dis = 0;
//统计字符出现的次数
//不需要统计s中所有字符出现的次数
for(int i = 0; i < tLen; i++){
tt[tAr[i]]++;
if(tt[tAr[i]] == 1){
}
}
//需要返回最小覆盖字串,则需要记录符合条件的窗口的起始和子字符串长度
int l = 0, lWin = 0, minLen = sLen + 1;//加1的目的是为了区分s是最小覆盖子串和找不到子串的情况
//开始滑动窗口
for(int rWin = 0; rWin < sLen; rWin++){
if(ss[sAr[rWin]] < tt[sAr[rWin]]){
//说明sAr[rWin]在t中
dis++;
}
ss[sAr[rWin]]++;
//直到dis=tLen的时候可以移动窗口的左边界
while(dis == tLen){
已经得到最小覆盖子串
if((rWin - lWin + 1) < minLen){
l = lWin;
minLen = rWin - lWin + 1;
}
if(ss[sAr[lWin]] == tt[sAr[lWin]]){
//说明sAr[lWin]在t中,删掉它不行
dis--;
}
ss[sAr[lWin++]]--;
}
}
if(minLen != sLen + 1){
return s.substring(l, l + minLen);
}else{
return "";
}
}
}
这道题的难点在于如何确定s的子串已经包含了t。这道题在理解了思路自己写的时候,一直报错,后来发现是对s.substring(l, l + minLen)存在误解,是不包括endIndex的。目前还是对常见字符串操作不熟悉。
难点总结
滑动窗口最难的地方就在于什么时候可以右移起始位置。
3.59.螺旋矩阵II
题目链接:螺旋矩阵II
文档讲解: 代码随想录
视频讲解: 代码随想录
class Solution {
public int[][] generateMatrix(int n) {
int[][] nums = new int[n][n];
int startX = 0, startY = 0;
int loop = 1;//记录当前圈数
int offset = 1;
int count = 1;//矩阵中需要填写的数字;
int i,j;
while(loop <= n/2){
//左闭右开
//上行
for(j = startY; j < n - offset; j++){
nums[startX][j] = count++;
}
//右列
for(i = startX; i < n - offset; i++){
nums[i][j] = count++;
}
//下行
for(; j> startY; j--){
nums[i][j] = count++;
}
//左列
for(; i > startX; i--){
nums[i][j] = count++;
}
startX++;
startY++;
offset++;
loop++;
}
//若n为奇数
if(n % 2 == 1){
nums[startX][startY] = count;
}
return nums;
}
}
第一遍看是不会的,理解思路的话写起来是简单的,关键在于要坚持循环不变量原则,对于拐角处的处理要坚持左闭右开或者左开右闭。