leetcode算法练习
977.有序数组的平方
思路:
暴力解法:把所有数都平方 然后进行一个快排 时间 复杂的:O(nlogn) 取决于快排
双指针:设置两个指针 分别在数组的两端 定义一个一样大小的新数组 根据两个指针的变化 每次将最大的数放入新数组中 即新数组由下标从大到小更新 时间复杂度:O(n)
注意:
1. 双指针中for循环判断条件是 < 还是 <=
方法一:暴力解法
class Solution {
public int[] sortedSquares(int[] nums) {
for(int i = 0; i < nums.length; i++){
nums[i] = (int) Math.pow(nums[i], 2);
}
Arrays.sort(nums);
return nums;
}
}
方法二:双指针
想到双指针的原因:因为数组是排好序的 那么一定是在数组的两边找到了最大的数 故而可以用两个指针的一个操作逐步向中间合拢的过程 从而得到一个由大到小的数组 将得到的数放入新数组中 以下标由大到小来更新
class Solution {
public int[] sortedSquares(int[] nums) {
int k = nums.length - 1;
int[] result = new int[nums.length];
// i=j时 指向的这个元素也要判断后放入reslut数组中
// 如果是i<j 那么就会将i=j的这个元素漏判
// 该for循环中i++ j-- 不写在for()循环条件中 因为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];
k--;
i++;
}else {
result[k] = nums[j] * nums[j];
k--;
j--;
}
}
return result;
}
}
209.长度最小的子数组
思路:
暴力解法:两层for循环 不断寻找符合条件的子数组 时间复杂度为O(n²) 此方法会超时
滑动窗口:利用滑动窗口的思想来解决 实际还是双指针 只是取中间的集合 更像是一个滑动的窗口
注意:
1. for(j…) j是滑动窗口的起始位置还是终止位置
终止位置是要把后面的所有元素遍历一遍 才能返回以j为起始位置的集合 再去判断>= target 中 取最小的 这和暴力法没有区别了
2. 如何移动起始位置
所有j一定是表示的终止位置 起始位置需要一个动态移动的策略:当j到起始位置这个集合里面的所有元素和 >= target的时候 再去移动起始位置 这样就可以动态地调整起始位置 来去收集不同区间里的和
方法一:暴力解法
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++) { // 设置子序列起点为i
sum = 0;
for (int j = i; j < nums.length; j++) { // 设置子序列终止位置为j
sum += nums[j];
if (sum >= target) { // 一旦发现子序列和超过了s,更新result
subLength = j - i + 1; // 取子序列的长度
result = result < subLength ? result : subLength;
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == Integer.MAX_VALUE ? 0 : result;
}
}
方法一:滑动窗口
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int j = 0;
int sum = 0;
int result = Integer.MAX_VALUE;
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
while (sum >= target) {
// 存放最小的和
result = Math.min(result, i - j + 1);
// 窗口起始位置的移动
sum -= nums[j++];
}
}
return result == Integer.MAX_VALUE ? 0 : result;
}
}
59.螺旋矩阵II
思路:
注意:
1. 边界的处理 在之前二分法中 就已经做到了循环不变量 在此题也要遵循该原则 即坚持对每一条边的处理规则 从一而终选择左闭右闭[] 还是左闭右开()
2. 一共转了几圈 通过n/2来判断 而当n为奇数的时候 则需要注意 最中心的那个数要单独处理
3. for循环结束后变量i j的值就已经被改变了 可以不用去再重新定义他们的值
class Solution {
public int[][] generateMatrix(int n) {
int startx = 0;
int starty = 0;
int offset = 0; // 定义每一圈的终止位置
int count = 1; // 用来计数
int i,j;
int[][] nums = new int[n][n];
while(offset++ < n / 2) {
// 每一圈的起始位置都不是固定的 所以不可能写死 例如 i = 0
// 起始位置提前定义好
// 左上到右上
for(j = starty ; j < n - offset; j++) {
nums[startx][j] = count++;
}
// j此时已变为n - offset
// 右上到右下
for(i = startx ; i < n - offset; i++) {
nums[i][j] = count++;
}
// i此时已变为n - offset
// 右下到左下
for(; j > starty; j--){
nums[i][j] = count++;
}
// 左下到左上
for(; i > startx; i--){
nums[i][j] = count++;
}
// 每转一圈 都要修改起始位置
startx++;
starty++;
}
// 如果n为奇数 最中心的位置要单独赋值
if(n % 2 == 1) {
nums[startx][starty] = count++;
}
return nums;
}
}