977 有序数组的平方
动手写
总思路是先平方再排序,排序有很多种方法,但现有记忆只能写效率最低的冒泡排序:
public int[] sortedSquares(int[] nums) {
// [-3,-1,0,2]
// 先平方
for (int i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i];
}
// [9,1,0,4]
// 后排序 - 冒泡排序
for (int i = 0; i <= nums.length - 2; i++) {
for (int i1 = i + 1; i1 <= nums.length - 1; i1++) {
if (nums[i1] < nums[i]){
// 交换位置
int temp = nums[i1];
nums[i1] = nums[i];
nums[i] = temp;
}
}
}
return nums;
}
解答成功:
执行耗时:637 ms,击败了5.02% 的Java用户
内存消耗:44.5 MB,击败了5.11% 的Java用户
看视频
双指针法:
为什么会想到双指针法?
虽然和第一天名字一样,但仅仅是名字一样用了两个指针,原理不同。
结合本题的特点,由于该数组是递增的,且包含了负数,负数有可能平方之后更大
**因此,越往两边的数平方后越大。**那么就设置两个指针在头尾比较,最大的插入新数组。
对比起来,双指针更针对本题的情况,取得了最佳性能。
而传统的方法更为通用,无论数组是否有序都可以使用。
反思:优化算法可以从“是否充分运用题目条件,题目特点”思考
双指针代码:
// 双指针
int k = nums.length - 1;
int[] result = new int[nums.length];
// 注意i和j范围,由于i == j时还要判断并且写入新数组,因此为<=
for (int i = 0, j = nums.length - 1; i <= j; ) {
if ((nums[i] * nums[i]) > (nums[j] * nums[j])) {
// 给新数组赋予最大值
result[k] = nums[i] * nums[i];
i++;
k--;
}
else {
// 给新数组赋予最大值
result[k] = nums[j] * nums[j];
k--;
j--;
}
}
return result;
此时的时间复杂度为O(n)
执行耗时:1 ms,击败了100.00% 的Java用户
内存消耗:43.7 MB,击败了39.33% 的Java用户
209 长度最小的子数组
动手写
想法:使用双指针先选定一个连续大区域,然后左指针往右缩小值最小。以此不断生成最小区域,取最小的数组长度值min。但自己敲的代码由于时间复杂度高,面对大量数组测试时TimeOut
public int minSubArrayLen(int target, int[] nums) {
// 双指针
int l = 0;
int r;
int min = 0;
for (r = 0; r < nums.length; r++) {
// 指针内区域相加 - 获得区域
int sum = 0;
for (int i = l; i <= r; i++) {
sum = sum + nums[i];
}
// 优化区域
if (sum >= target) {
int num = minification(++l, r, nums, target);
if (min == 0 || num < min){
min = num;
}
l++;
}
}
return min;
}
public int minification(int l, int r, int[] nums, int target) {
// 缩小范围
int sum1 = 0;
for (int i = l; i <= r; i++) {
sum1 = sum1 + nums[i];
}
if (sum1 >= target) {
// 如果还是>= 则继续优化
return minification(++l, r, nums, target);
} else {
// 出现<情况时返回l指针
l--;
System.out.print(l);
System.out.print(r);
return r - l + 1;
}
}
看视频后
计算sum时候写的太复杂了,借助上层循环完成相加操作,根据视频优化后:
// 双指针
int l = 0;
int r;
int min = Integer.MAX_VALUE;
int sum = 0;
for (r = 0; r < nums.length; r++) {
// 指针内区域相加 - 获得区域
sum = sum + nums[r];
// 优化区域
while (sum >= target) {
// 记录当前值
min = Math.min(min, r - l + 1);
sum = sum - nums[l];
l++;
}
}
return min == Integer.MAX_VALUE? 0 : min;
反思:利用已做的事情,优化不必要的时间复杂度
59 螺旋矩阵 II
本题对昨天的"循环不变量"进行巩固,四次循环如果还不设定一个标准,就乱套了。
卡哥:
大家写二分法经常写乱,主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
代码:左闭右开[ )
public int[][] generateMatrix(int n) {
int x = 0;
int y = 0;
int offset = 1;
int count = 1;
int loop = 0;//循环次数
int[][] arr = new int[n][n];
while (loop++ < n / 2) {
int i = x;
int j = y;
// →
for (; j < n - offset; j++) {
arr[i][j] = count++;
}
// ↓
for (; i < n - offset; i++) {
arr[i][j] = count++;
}
// ←
for (; j >= offset; j--) {
arr[i][j] = count++;
}
// ↑
for(; i >= offset; i--){
arr[i][j] = count++;
}
// 改变参数
offset++;
x++;
y++;
}
// 特殊:当n为奇数时,填补中间数值
if (n % 2 == 1) {
arr[x][y] = count;
}
return arr;
}
要非常注意四条边的边界划分: