977.有序数组的平方
题目链接: 977. 有序数组的平方
参考文章链接:代码随想录_977. 有序数组的平方
- 个人思考层面
在拿到题目时,会因为给出的数组有序这一条件,在思考如何利用,或是根据这个条件能够得到时间复杂度较低的算法,反而忽视了可以直接排序这种方式,而其他方法也没有很好的实现,就走上了一条尝试自己造出比已有算法耗时更低的算法的死胡同(一方面是对语言的不熟悉,另一方面也是没怎么做过题的原因,脑子里一直没有闪现过排序算法的想法)。另一点问题,没能意识到题目未对空间进行限制,解题时未想到新造数组,这是读题方面的失误。
- 解题思路
方式一:如果不追求更低的时间复杂度,这题其实较为容易解决,只需要在将数组元素平方后,调用
sort
函数对数组元素进行排序,就能得到想要的结果,或是也可手动实现快排等排序算法,时间复杂度保持在 O ( n + l o g n ) O(n + logn) O(n+logn)。
代码如下:
class Solution {
public int[] sortedSquares(int[] nums) {
int len = nums.length;
for (int i = 0; i < len; i++) {
nums[i] = nums[i] * nums[i];
}
Arrays.sort(nums);
return nums;
}
}
方式二:再利用到上述所提到的原数组有序的条件,因为数组可能存在负数的原因,平方后的元素从两边至中间大小是递减的,只需要想到可以新建一个空数组,原数组中用
left
和right
两个指针分别指向起始和终止位置,并比较大小,较大的那个复制到新数组中(从后往前填放),然后该指针前移。不断重复这一步骤,填满元素的新数组就是目标值。
代码如下:
class Solution {
public int[] sortedSquares(int[] nums) {
int left = 0, right = nums.length - 1;
//分别指向原数组的起始和终止位置
int[] result = new int[nums.length];
int index = result.length - 1;
//指向新数组的终止位置
while (left <= right) {
//如果 left > right,说明数组元素已经遍历完,跳出循环
if (nums[left] * nums[left] >= nums[right] * nums[right]) {
//将较大的元素复制到新数组的索引值较大的位置,从而新数组实现从小到大排序
result[index--] = nums[left] * nums[left];
left++;
} else {
result[index--] = nums[right] * nums[right];
right--;
}
}
return result;
}
}
209.长度最小的子数组
题目链接:209.长度最小的子数组
参考文章链接:代码随想录_209.长度最小的子数组
- 个人思考层面
根据题目的描述,很容易能想到暴力解法,两层循环,外层进行限定连续子数组中的起始位置,内层限定结束位置,找到原数组中每个元素为始的连续子数组长度,记录并取最小值。
(虽然力扣更新了数据后,暴力解法变得超时了)
代码如下:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
//申请一个新数组,对原数组遍历,新数组中对应位置存储,需要多少个加起来才能大于等于target
//事实上可以不需要新数组的加入,只是每次记录最小的那个值并保存即可,可节省空间,但时间复杂度不变
int len = nums.length;
int[] arr = new int[len];
for (int i = 0; i < len; i++) {
int sum = 0;
for (int j = i; j < len; j++) {
sum += nums[j];
if (sum >= target) {
arr[i] = j - i + 1;
break;
}
}
}
int minIndex = 0;
for (int i = 0; i < len; i++) {
if (arr[i] < arr[minIndex] && arr[i] != 0)
minIndex = i;
}
return arr[minIndex];
}
}
- 滑动窗口
更好的做法是利用滑动窗口的思想,能够在 O ( n ) O(n) O(n)的时间复杂度内完成问题的求解。其主要思想是用一个循环只记录该华东窗口的终止位置,实现长度最小子数组的查找。如果当前窗口内的元素和大于等于
val
,那个窗口的起始位置就要向后移动(窗口缩小);窗口内元素和小于val
,窗口的终止位置向后移动,即循环里的索引向后移动(窗口增大)
代码如下:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0;
int result = Integer.MAX_VALUE;
//初值设定为整形最大值
int sum = 0;
for (int right = 0; right < nums.length; right++) {
//窗口的右指针
sum += nums[right];
//计算窗口内的元素和
while (sum >= target) {
result = Math.min(result, right - left + 1);
sum -= nums[left++];
//窗口的左指针右移
}
}
return result == Integer.MAX_VALUE ? 0 : result;
//判断是否不存在符合条件的子数组
}
}
59.螺旋矩阵II
题目链接:59.螺旋矩阵II
题解链接:lc题解
个人觉得过于复杂,看到题目后没什么思路,边去查看了题解,觉得下面这种写法尤为优雅且简洁易操作。
最难的部分在于对边界的控制,这种解题思路中每一圈分上下左右进行操作,并分别用变量t
、b
、l
、r
对上下左右进行限定。例如,上面一行从左至右进行遍历后,为防止后续元素重复令t += 1
,相当于上边界向内缩1.
class Solution {
public int[][] generateMatrix(int n) {
int[][] res = new int[n][n];
int l = 0, r = n - 1, t = 0, b = n - 1;
//初始的四个边界位置
int num = 1;
int tar = n * n;
while (num <= tar) {
for (int i = l; i <= r; i++) res[t][i] = num++;
t++;
for (int i = t; i <= b; i++) res[i][r] = num++;
r--;
for (int i = r; i >= l; i--) res[b][i] = num++;
b--;
for (int i = b; i >= t; i--) res[i][l] = num++;
l++;
}
return res;
}
}