今天第二天刷题,基础欠缺还很多,慢慢一点一点补!加油!今天主要学习整理977,快速学习209和59。
977 有序数组的平方
题目
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序排序。
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
https://leetcode.cn/problems/squares-of-a-sorted-array
初始思路
根据提示使用双指针,定义了一个快指针一个慢指针分别比较前一个元素和后一个元素,将最小值赋给慢指针,再将慢指针的值赋给下标为0的元素,以此类推。
看上去思路好像还行,但实际写起来并没有写成功,可能还需要继续改进,也可能这个思路本身存在着问题。(留待以后继续写写看)。但很明显的是,就算这个思路可行,其时间复杂度也会很大,不是一个很理想的解。
初始解题时发现的点
平方的运算:java中求整数a的平方不能写为a^2。详细参照http://t.csdn.cn/WgbU4。a的平方运算应该为a*a 或 Math.pow(n,2)。
运行中出现了Exception in thread “main“ java.lang.ArrayIndexOutOfBoundsException:Index 5 out of bounds for length报错。原代码如下:
for(int i =1; i < nums.length; i++){
int fast, slow = 0;
fast = nums[i+1]*nums[i+1];
slow = nums[i]*nums[i];
通过参照http://t.csdn.cn/VCWB4发现问题出现在fast中nums[i+1]的下标已经在i=nums.length时会超出数组大小,因此会报错。因此可以改为:
for(int i =1; i < nums.length; i++){
int fast, slow = 0;
fast = nums[i]*nums[i];
slow = nums[i-1]*nums[i-1];
回到题目,让我们来学习Leetcode官方给出的解题方法:
方法1 直接排序
将数组nums中的数平方后用Arrays.sort直接排序
class Solution {
public int[] sortedSquares(int[] nums) {
int[] ans = new int[nums.length];
for (int i = 0; i < nums.length; ++i) {
ans[i] = nums[i] * nums[i];
}
Arrays.sort(ans);
return ans;
}
}
作者:LeetCode-Solution
时间复杂度:O(nlogn), 空间复杂度:O(logn) 【需要logn的栈空间进行排序】
Arrays.sort() 默认将数组从小到大进行排序。其根据数据量级选择插入、归并和快速排序,时间复杂度为O(nlogn)。
方法二 双指针(赋值最小值)
可以用双指针的条件为已知数组为非降序(升序)排列。方法二设neg为数组nums中负数与非负数的分界线。nums[0]到nums[neg] 均为负数,nums[neg+1] 到 nums[n-1] 均为非负数。因此我们可以以neg和neg+1为双指针分别在负数区域和非负数区域运动,比较平方的大小。
指针neg从负数区域的最右侧往左移,neg最初所指的元素一定为负数区域中平方后的最小数。
指针neg+1从非负数区域最左侧往右移,neg+1最初所指的元素一定为非负数区域中平方后的最小数。
因此,比较两个指针所指的平方的大小,将最小值赋给输出的新数组ans的第一个值, 以此类推进行比较和循环。
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int negative = -1;
for (int i = 0; i < n; ++i) {
if (nums[i] < 0) {
negative = i;
} else {
break;
}
}
int[] ans = new int[n];
int index = 0, i = negative, j = negative + 1;
while (i >= 0 || j < n) {
if (i < 0) {
ans[index] = nums[j] * nums[j];
++j;
} else if (j == n) {
ans[index] = nums[i] * nums[i];
--i;
} else if (nums[i] * nums[i] < nums[j] * nums[j]) {
ans[index] = nums[i] * nums[i];
--i;
} else {
ans[index] = nums[j] * nums[j];
++j;
}
++index;
}
return ans;
}
}
作者:LeetCode-Solution
时间复杂度O(n),空间复杂度O(1)
方法三 双指针(赋值最大值)
两个指针分别指向位置0和n-1。如下图所示。
无论何种情况,数组位于位置0和n-1的两个元素中的一个一定是平方后的最大值。此方法避免了方法二去寻找负数和非负数的临界点。两个指针所指的元素平方后谁更大谁写入输出数组ans的最后一位pos。这样答案就被逆序放入数组ans中。
代码如下:
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int[] ans = new int[n];
for (int i = 0, j = n - 1, pos = n - 1; i <= j;) {
if (nums[i] * nums[i] > nums[j] * nums[j]) {
ans[pos] = nums[i] * nums[i];
++i;
} else {
ans[pos] = nums[j] * nums[j];
--j;
}
--pos;
}
return ans;
}
}
作者:LeetCode-Solution
时间复杂度O(n), 空间复杂度O(1)
209 长度最小的子数组
看了卡哥的视频https://www.bilibili.com/video/BV1tZ4y1q7XE/?vd_source=2b661ed7e8639bf2834608f46f8c8d5f,没有写代码。后面有时间自己再写一写。
滑动窗口
本题的思路为滑动窗口。其实可以理解为双指针及其中间的集合。
要点总结:
1. 两个指针:起始指针、终点指针
2. j为终点指针
3. 先移动终点指针,当集合的和sum>=target后再移动起始指针
4. 判断sum >= s用while循环而不用if,因为要保证起始指针持续地向后移动
59 螺旋矩阵||
看到题给我惊呆了,看完解法又给我惊呆了。https://www.bilibili.com/video/BV1SL4y1N7mV/?spm_id_from=333.788&vd_source=2b661ed7e8639bf2834608f46f8c8d5f。视频看懂了,后面试着自己写一写。
边界处理--循环不变量
本题的主要思路 --模拟转圈,边界处理问题。坚持循环不变量原则。
循环不变量,即坚持用同一个规则处理每一条边。采用“左闭右开”规则。永远不处理每条边的最后一个节点。把它留给下一条边作为起始点。
要点总结:
while进入循环,n/2判断转几圈。若n为奇数,则在后面把多出来的数加上;
j遍历横线上的点, i遍历竖线上的点;
每次循环的起始点为(start x,start y),初始start x,start y都等于0。后面start x++, start y++,下一个循环开始。
每一圈想要做到左闭右开的“右开”, i或j最大都不能超过n-offset。减去offset即要减去最后一个节点。(我一开始在想为什么不直接定义offset为1,后来发现offset只有第一圈的时候为1,到转到第二圈也就是再往里面螺旋的时候,随着圈的缩小需要减去的offset也要变多。因此设置offset方便调试。将初始offset=1,后面offset++即可)
后续任务
209和59的思路简单整理了下,等后面自己再亲自上手写写。
总结数组。