977. 有序数组的平方
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1: 输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100],排序后,数组变为 [0,1,9,16,100]
示例 2: 输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]
思路
数组平方完后,数组平方的最大值就在数组的两端,不是最左边就是最右边(负数平方之后可能成为最大数),不可能是中间。
难点
在循环遍历数组时left<=right,如果是left<right的话,最终两个指针共同指向的那个元素不会进行排序操作
双指针法
核心就是两边同时开始找较大的值,找到了就把最大值扔进新的平方数组
- 左指针:从左边nums[0]开始找与右边相比更大的平方数
- 右指针:从右边nums[length-1]开始找与左边相比更大的平方数
判断如果右边平方数大于左边,那么左边不动,右指针移到下一位
判断如果左边平方数大于右边,那么右边不动,左指针移到下一位
public int[] sortedSquares(int[] nums) {
// 定义左右指针
int left;
int right;
// 定义新数组及新数组下标
int result[] = new int[nums.length];
int len = nums.length-1;
// 遍历原数组
for (left = 0, right = nums.length - 1; left <= right;){
// 如果是left<right的话,最终两个指针共同指向的那个元素不会进行排序操作
if (nums[left] * nums[left] < nums[right] * nums[right]){
// 右边平方数大于左边
result[len--] = nums[right] * nums[right];
right--;
}else {
// 左边平方数大于右边
result[len--] = nums[left] * nums[left];
left++;
}
}
return result;
}
时间复杂度为O(n)
209. 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例:
输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。
思路
用一个滑动窗口框住要计算的数,数多了就让左窗口往前移,和小了就让右窗口往后移
滑动窗口是计算机语言中重要的算法之一,主要用于处理连续的数组数据或者字符串数据,常用来提取数据中的子数组或者子串。 滑动窗口就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
滑动窗口:只用一个for循环,那么这个循环的索引,一定是表示滑动窗口的终止位置。
难点
在本题中实现滑动窗口,主要确定如下三点:
- 窗口内是什么? 答:是满足其和 ≥ s 的长度最小的连续子数组。
- 如何移动窗口的起始位置? 答:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)
- 如何移动窗口的结束位置? 答:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引
完整代码:
public int minSubArrayLen(int target, int[] nums){
// 定义滑动窗口的左右窗口(指针)
int left = 0;
int right;
// 定义窗口内的和
int sum =0;
// 定义窗口内子串长度
int SubArrayLen = 0;
// 定义返回窗口大小为不可能取到的最大值
int result = Integer.MAX_VALUE;
// 右窗口遍历数组
for (right = 0; right < nums.length; right++){
sum += nums[right];
while (sum >= target){
// 计算此时窗口长度
SubArrayLen = right - left + 1;
// 判断此时的窗口长度是否为最小
result = result < SubArrayLen ? result : SubArrayLen;
// 减去左窗口看窗口内和是否还大于等于s
sum -= nums[left++];
}
}
// 如果此时的窗口大小还是初始设置的不可能取到的最大值,那么返回
return result == Integer.MAX_VALUE ? 0 : result;
}
- 补充:
Java的基本数据类型:整型:int , short, long, byte;浮点型: float, double;字符型: char;布尔型: boolean
java的引用数据类型:Byte、Short、Integer、Long、Float、Double、Character、Boolean
其中Integer.MAX_VALUE
表示:int 数据类型的最大值,即:2147483647
Integer.MIN_VALUE
表示:int 数据类型的最小值,即:-2147483648
定义不可能取到的最大值,使数组没有满足要求的窗口时可以返回0。
result < subLength ? result : subLength
可以替换为Math.min(result, minLen)
- 时间复杂度:
O(n)
不要以为for里放一个while就以为是O(n^2), 主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
59. 螺旋矩阵II
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
示例1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入:n = 1
输出:[[1]]
思路
模拟顺时针画矩阵的过程,利用左闭右开区间,打印完整个序列,即每一个拐角处的处理规则,拐角处让给新的一条边来继续画,若n为奇数中心点单独填充
难点
为什么loop的循环终止条件是< n / 2
,因为搜索一圈之后,下一圈的上边会往下走,下一圈的下边会往上走,高度就少2,下一圈的左边会往右走,下一圈的右边会往左走,相当于宽少2了,每次下一圈都会比上一圈的高度宽度都少2,直到这个没有外圈了,没有外圈就是宽度是和高度都是0的时候(偶数的情况),每次宽,高缩小2,直到宽,高是0。因为宽,高都一样,而且是一起缩2的,那么就当高缩2到0的时候就结束了,要缩多少次,就是圈。高假设是偶数,偶数-2-2-2一直到0,不就是这个偶数除2吗,就是圈数啦。
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
完整代码
public int[][] generateMatrix(int n) {
// 定义循环次数,若n为基数需要单独处理中间位置
int loop = 0;
// 定义起始位置,因为起始点的横纵坐标一定相等(n, n)
int start = 0; //循环开始原点
// 坐标常用表示为(i, j) ,因此这里i不动用j横向遍历,j不懂用i纵向遍历
int i,j;
// 定义矩阵大小
int nums[][] = new int[n][n];
// 定义填充数字,从1 ~ n*n
int count = 1;
// 矩阵每转一圈,下一次的坐标都要往内收offset
int offset = 1;
// 循环次数从0开始小于n/2
while (loop < n >> 1){
//上边从左到右
for (j = start; j < n - offset; j++){
// 左开右闭,用小于号使最右的值被右列处理
nums[start][j] = count++;
}
//右边从上到下
for (i = start; i < n - offset; i++){
// 左开右闭,用小于号使最下的值被下边处理
nums[i][j] = count++;
}
//下边从右往坐
for ( ;j > start; j--){
// 左开右闭,用小于号使最左的值被左列处理
nums[i][j] = count++;
}
//左边从下往上
for ( ;i > start; i--){
nums[i][j] = count++;
}
// 初始位置横纵坐标都加1
start++;
// 往内收一圈
offset++;
loop++;
}
if (n % 2 == 1){
nums[n >> 1][n >> 1] = count;
}
return nums;