代码随想录算法训练营第二天 | 977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II
977.有序数组的平方
题目链接:977.有序数组的平方
注:题目所给条件为 1.非递减顺序排序的数组;2.时间复杂度为 O(n)。
暴力解法
思路:遍历数组,对每个元素进行平方后,快速排序。该方法时间复杂度为O(nlogn),即快速排序的时间复杂度。
class Solution {
public int[] sortedSquares(int[] nums) {
for (int i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums); //快速排序
return nums;
}
}
双指针法
思路:由于原数组已按非递减顺序排列,则最大值只可能出现在原数组两端。因此分别从数组首尾开始对比(双指针),将其中平方后的较大值从后往前添加至新数组,对应指针向原数组中间靠拢。最后返回新数组即可。
class Solution {
public int[] sortedSquares(int[] nums) {
int[] result = new int[nums.length]; //最后返回的新数组
int k = nums.length - 1; //从后往前向新数组添加元素
for (int i = 0, j = nums.length - 1; i <= j;){ //若i < j,则会遗漏最中间的元素
if (nums[i] * nums[i] > nums[j] * nums[j]){
result[k] = nums[i] * nums[i];
k--;
//以上两句可合并为 result[k--] = nums[i] * nums[i];
i++;
} else {
result[k] = nums[j] * nums[j];
k--;
j--;
}
}
return result;
}
}
209.长度最小的子数组
题目链接:209.长度最小的子数组
注:题目要求空间复杂度为O(1)。
暴力解法
思路:两个for循环。 不断寻找符合条件的子数组,该方法的时间复杂度为O(n2),空间复杂度为O(1)。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int result = Integer.MAX_VALUE; //最终返回的长度
int sum = 0; //子数组的元素之和
int subLength = 0; //子数组的长度
for(int i = 0; i < nums.length; i++){
sum = 0;
for(int j = i; j < nums.length; j++){
sum += nums[j];
if(sum >= target){
subLength = j - i + 1;
result = result < subLength ? result : subLength;
break;
}
}
}
//若result为初始值,则说明没有符合条件的子数组
return result == Integer.MAX_VALUE ? 0 : result;
}
}
双指针法(滑动窗口)
思路:类似一个向前滑动的窗口,不断调节子数组的起始位置和终止位置。
使用该方法要点有三:
- ①窗口内元素是什么?
- ②如何移动窗口的起始位置?
- ③如何移动窗口的终止位置?
针对本题,窗口内元素即为子数组的和;若当前窗口内值的和大于target,则起始位置向后移动;终止位置即for循环遍历数组的指针。精髓在于根据当前子数组和的大小,不断变更子数组的起始位置。该方法的时间复杂度为O(n),空间复杂度为O(1)。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int i = 0; //滑动窗口起始位置
int sum = 0; //滑动窗口的数值之和
int subLength = 0; //滑动窗口的长度(子数组长度)
int result = Integer.MAX_VALUE; //最后返回的结果,取最大值旨在便于最后的返回
for (int j = 0; j < nums.length; j++){
sum += nums[j];
//while循环不断比较子数组是否符合条件,并更新起始位置(i)
//注意不能使用if语句,例如[1,1,1,1,1,100],target也为100的情况
while (sum >= target){
subLength = (j - i + 1); //获取子数组长度
result = result < subLength ? result : subLength;
sum -= nums[i];
i++;
// 以上两句等于 sum -= nums[i++]; 此处即为关键部分,不断变更起始位置(i)
}
}
//若result为初始的最大值,则说明没有符合条件的子数组,因此返回0
return result == Integer.MAX_VALUE ? 0 : result;
}
}
59.螺旋矩阵II
题目链接:59.螺旋矩阵II
注:本题并不涉及算法,主要考察对代码的掌控能力。
解法
思路:while循环模拟由外向内一圈圈打印数组。 坚持循环不变量原则:每条边均以左闭右开的方式进行遍历。
class Solution {
public int[][] generateMatrix(int n) {
int[][] result = new int[n][n];
int startX = 0; //起始行
int startY = 0; //起始列
int count = 1; //表示每个待填入的数值
int offset = 1; //用以控制每圈变化的边长
int i = 0;
int j = 0;
int loop = n / 2; //共转几圈
int middle = n / 2; //矩阵中间位置
while (loop-- != 0){
// [i, j]
//四个for模拟顺时针转圈打印四条边的过程
for (j = startY; j < n - offset; j++) { //j向右移动
result[startX][j] = count++;
}
for (i = startX; i < n - offset; i++) { //i向下移动
result[i][j] = count++;
}
for (; j > startY; j--) { //j向左移动
result[i][j] = count++;
}
for (; i > startX; i--) { //i向上移动
result[i][j] = count++;
}
startX++; //修改下一圈的初始行
startY++; //修改下一圈的初始列
offset += 1; //层数加深,每条边长减小
}
if (n % 2 == 1) {
result[middle][middle] = n * n;
}
return result;
}
}