977.有序数组的平方
package com.liqi.day.day2;
import java.util.Arrays;
public class LeetCode_977 {//977.有序数组的平方,给定一个有序数组,要得到该数组中各个元素平方后的有序数组
public static void main(String[] args) {
int[] nums = {-2, -1, 0, 3, 5, 6};
int[] ints = sortedSquares(nums);
System.out.println(Arrays.toString(ints));
}
public static int[] sortedSquares(int[] nums) {
//思路:首先看题目要求:每个元素都进行平方,
// 那么我们就先什么都变的情况下把数组中的每个元素都平方后会发现规律:最大的一定是在左边或者右边,不可能在中间,因为绝对值最大的数要么在左边要么在右边
//由左or右 我们可以联想到双指针的解法->左右两个指针找出最大值依次放入新数组 (当然了,因为是最大所以新数组应该是从后往前遍历)
int left = 0;
int right = nums.length - 1;
int[] squareNums = new int[nums.length];//这里只能新创建一个数组,遍历的是新的数组,用原来的数组来遍历的话会出现得到值是平方的平方的情况
for (int i = squareNums.length - 1; i >= 0; i--) {//另一种循环写法(while left<=right,也相当于把原来数组中的所有元素遍历完了
int leftSquare = nums[left] * nums[left];
int rightSquare = nums[right] * nums[right];
if (leftSquare >= rightSquare) {//这里如果取等号那右边大的时候就不用取
squareNums[i] = leftSquare;
left++;
} else {
squareNums[i] = rightSquare;
right--;//这里注意,因为是从右往中间走所以是减不是加!(第一遍的时候写错了)
}
}
return squareNums;
//双指针的实现时间复杂度是o(n),
// 还有一种最容易想到的暴力思路:先对数组中的元素依次平方(o(n)),然后再进行一次快排(o(logn)),所以时间复杂度是o(n+logn)
}
}
209.长度最小的子数组
package com.liqi.day.day2;
public class LeetCode_209 {// 长度最小的子数组:给定一个含有 n 个正整数的数组和一个正整数 target
// 找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度 如果不存在符合条件的子数组,返回 0
public static void main(String[] args) {
int[] nums = {2, 3, 1, 2, 4, 3};
int target = 7;
System.out.println(minSubArrayLen2(7, nums));
}
public static int minSubArrayLen(int target, int[] nums) {
//思路:题目要求的是 子数组要在数组中连续并且和大于等于target,然后要得到满足这个条件且长度最短的那一个,
// 最暴力的思路当然是 两个for循环(第一层for遍历子数组起始位置,第二层for遍历终止位置,然后不断的寻找符合条件的子序列,时间复杂度很明显是o(n^2)
//那么有没有更时间复杂度更优的方法呢?前面也刷了好几道数组双指针的题目了,我发现双指针往往能够将原来需要两层循环的事情优化成一层循环
//所以就要想到使用双指针,第一个指针定义的是子数组的起始位置,第二个指针定义终止位置
// 滑动窗口(虽然是双指针,但其实是通过双指针实现一个滑动窗口,与前面不同的是我们取的是这个双指针区间内的值)
int left = 0;
int sum = 0;
int result = Integer.MAX_VALUE;//todo 这里我没想出来,先把长度定到最大方便后面比较子数组长度大小
for (int right = 0; right < nums.length; right++) {
sum += nums[right];
while (sum >= target) {
result = Math.min(result, right - left + 1);// 当前满足条件的子数组长度=right-left+1
sum -= nums[left++];// 右指针一直向数组末尾遍历,那么左指针什么时候移动呢?
//todo 就是在当前子数组满足和大于target的条件后,左指针往后移,代表着开始寻找下一个满足条件的子数组
}
}
return result == Integer.MAX_VALUE ? 0 : result;//双指针滑动窗口一层for循环直接搞定,时间复杂度是O(n)
}
public static int minSubArrayLen2(int target, int[] nums) {//暴力解法
int result=Integer.MAX_VALUE;
int sum = 0;
int sublength = 0;
for (int start = 0; start < nums.length; start++) {//起始位置从0开始
sum=0;
for (int end = start;end<nums.length;end++ ){//终止位置从起始位置开始
sum+=nums[end];
if (sum>=target){
sublength=end-start+1;
result=sublength<result?sublength:result;
}
}
}
if (result==Integer.MAX_VALUE){//result没有被赋值,代表这个数组中没有任何满足条件的子数组
return 0;
}
return result;
}
}
59.螺旋矩阵
public static int[][] generateMatrix(int n) {
// 思路:顺时针->模拟转圈的过程,生成一个n x n的顺时针螺旋矩阵的过程就是转2/n圈,每一圈画出一个矩形框,且每一圈跟上一圈相比少两行两列
// 不过如果n是奇数的话多出来的那一个数就直接填充不需要转圈
//todo 关键点:循环不变量是什么?
// 我们遍历每圈中的每条边的时候都遵循同一个遍历原则:左闭右开->我们只遍历每条边的头结点到尾节点的前一个值,尾节点交给下一条边(作为下条边的头节点)
int count = 1;//count从1一直到n^2
int start = 0;//起始点
int loop = 1;
int[][] matrix = new int[n][n];
int i, j;
int offset = 1;//每转一圈偏移量+1,起始点的x和y都+1 ->每转一圈消两行两列
while (loop <= n / 2) {//总共转2/n圈
//从矩形的左上顶点开始向右遍历
for (j = start; j < n - offset; j++) {//尾结点留给下条边作为头节点,所以是x<n-1
matrix[start][j] = count++;
}
//右侧从上往下
for (i = start; i < n - offset; i++) {
matrix[i][j] = count++;
}
//右侧从右往左
for (; j > start; j--) {// todo 自己写的时候没有注意控制回去的时候是回到当前圈的起始位置,不应该是直接等于0
matrix[i][j] = count++;
}
//左侧从下往上
for (; i >start; i--) {
matrix[i][j] = count++;
}
start++;
offset++;
loop++;
}
//转圈循环结束后别忘记判断n是奇数的情况,这种情况还要将最后一个值(矩形最中间的那个位置)填入
if (n % 2 == 1) {//n为奇数
matrix[start][start] = count;
}
return matrix;
}