第二天开始上难度了,坚持就完事了
有序数组平方:
思路:
暴力解法:全部平方之后,再快排,这一块由于我基础比较薄弱,时间复杂度为O(nLogn),所以暂时不是很理解, 自己之后看完韩顺平之后再回过头来分析:
class Solution {
public:
vector<int> sortedSquares(vector<int>& A) {
for (int i = 0; i < A.size(); i++) {
A[i] *= A[i];
}
sort(A.begin(), A.end()); // 快速排序
return A;
}
};
双指针:这是今天的主角,再次用到双指针的思想,不过是一个在左,一个在右,由于是有序的,所以最大值肯定在数组的最左或者最右,也就是指针的位置left和right,对指针定义后,判断,如果左边的平方,大于右边的平方,那么就把左边的平方赋值到新数组的最右边;否则,如果右边的平方大,或者等于,就把右边的覆给新数组,接下来是自己写的时候出现的问题和注意点:
1. 要明确解题要什么东西,一个left指针,一个right指针,一个空数组,和一个数组的index从右边开始num.len-1
2. 记住left就是0, 但是right是num.length-1 (因为是从0开始的)
3.while判断条件注意是 left <= right, 这可以保证两个元素相等时也被处理了
4.给数组赋值,所以是result [k] = 平方和
7.写一个条件else就行了,因为等于时left,right都可
5.同时k--,保证新数组向左移动
6.如果过是左边大,那就left++,向右移动,反则right--
class Solution {
public int[] sortedSquares(int[] num) {
int [] result = new int[num.length];
int k = num.length - 1;
int left = 0;
int right = num.length - 1;
while (left <= right){
if(num[left]*num[left] > num[right]*num[right]){
result[k] = num[left]*num[left];
k--;
left++;
} else { //这里直接else就行了,因为等于时,left和right都可以
result[k] = num[right]*num[right];
k--;
right--;
}
}
return result;
}
}
==================================================================================================================================================
长度最小的子数组
这道题开始上难度了,来了一道medium难度的,题目看了很久才懂什么意思,rm写了好几遍才对(太折磨了......),也有暴力算法,用两个for循环,更好的使用滑动窗口的方法,该方法用一个循环就可以发挥两个循环的作用.
滑动窗口
思路和注意事项:
1. 明确需要什么,left起始指针,right终止指针, 一个result定义集合长度,需要取最大值
2.首先用for循环让尾指针开始遍历,然后需要 a.头尾之前集合的大小sum; b.符合target的集合长度
3.接下来判断, 前面算的sum是否大于等于target,如果是就滑动窗口, 严格遵守步骤:
a. 首先算出现在的target长度 subL = right - left +1;
b.更新sum = sum - nums[left] (之前已定义sum的右边间不断向右,这里修改左边界即可)
c.前面收集了所有的subL, 这里利用Math.min来不断更新最小的subL, 也就是我们要的长度
d.如果大于target, left起始指针也会继续向右更新,看新的窗口是否符合要求
4.记得最后return的时候要加个三元,防止一开始的target不在范围内
(right指针和之前的remove element一样会不停向右, left指针是只有大于等于target才会向右动,保证窗口的滑动, 继续寻找长度subL, 遍历完之后, 利用min来判断哪个长度最小)
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0; //定义左起始指针
int sum = 0; //定义集合间的sum
int result = Integer.MAX_VALUE; //定义窗口长度,取最大值
for(int right = 0; right < nums.length; right++){
sum += nums[right];
while(sum >= target) {
int subL = right - left + 1;
//要-1因为len是子数组的长度,不是间隔长度
sum = sum - nums[left];
result = Math.min(result, subL);
left++;
}
}
return result==Integer.MAX_VALUE? 0 : result;
}
}
==================================================================================================================================================
螺旋矩阵II
这道题也比较复杂, 虽然没有涉及太多的算法,但是对细节,边界的把握十分重要, 这里也和二分法的左闭右开有异曲同工之妙, 对四个边的细节调整非常重要. 我这里并没有完全按照carl的思路来进行,但总体来说是相同的
思路:
创建一个二维数组,利用左闭右开, 四个边界,然后往里面填充数字
操作细节:
1.想清楚需要什么,一个空的二维数组,左右上下四个border, 一个开始值start, 一个结束值end
2.首先建立一个while循环,条件是start<=end.
3.然后利用左右上下的border来对四条边的数值进行填充, 注意,由于使用左闭右开, bottom和right应该赋值为n-1
4.上边: 条件就是 i<=right, 然后mat[t][i] = start++, 因为纵坐标不变,横坐标随改变
5.右边:条件是i<=bottom, 这里是横坐标不变,纵坐标随着i改变
6.下边:是反方向移动,条件是i>=left, mat[b][i], 改变边界就是b--
7. 就是在for循环里,每个边填满数字,填满后将边界缩小.
(还是会忘记创建对象 int XXX = ......)
class Solution {
public int[][] generateMatrix(int n) {
int[][] mat = new int[n][n];
int l = 0, t = 0, r = n - 1, b = n - 1;
int start = 1;
int end = n*n;
while(start <= end){
for(int i = l; i <= r ; i++ ) //最上面的边
mat[t][i] = start++; //for不加括号就执行后面一句
t++;
for(int i = t; i <= b ; i++) //右边
mat[i][r] = start++;
r--;
for(int i = r; i >= l; i-- ) //下边
mat[b][i] = start++;
b--;
for(int i = b; i >= t; i--) //左边
mat[i][l] = start++;
l++;
}
return mat;
}
}