代码随想录Day2
数组
有序数组的平方
这个题,一开始就猛猛偷懒了。关键代码直接用Arrays.sort()代替。
在IDEA中重新尝试了自己熟知的几个小排序算法:
-
选择排序
-
冒泡排序
-
快速排序
public class Main {
public static void main(String[] args) {
int[] nums={-4,-1,0,3,10};
for(int i=0;i<nums.length;i++){
nums[i]*=nums[i];
}
quickSort(nums,0,nums.length-1);
for(int i=0;i<nums.length;i++){
System.out.print(nums[i]+" ");
}
}
static void seleSort(int[] nums){
/*
选择排序的思想是每轮找到最小的值,并放到“最前”
*/
for(int i=0;i<nums.length;i++){ //分别找能放到当前i位置 最小的元素
for(int j=i+1;j<nums.length;j++){ //每一轮把“最小的值”放到前面之后,下一轮前面已放过去的就没必要遍历了,所以是j=i+1
if(nums[j]<nums[i]){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
}
}
static void maopaoSort(int[] nums){
//冒泡排序其实是每轮把最大的“石头” 沉入到最下面
for(int i=0;i<nums.length-1;i++){ //一共有nums.length个元素,每轮沉1个石头,那么只需要遍历nums.length-1轮 结果就出来了
//j的界限是变化的,每一轮不一样,比如第一轮最大的放到了最后,那么第二轮就只需要遍历到倒数第二个数就行了
for(int j=0;j<nums.length-1-i;j++){
if(nums[j]>nums[j+1]){
int temp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=temp;
}
}
}
}
//快速排序
static void quickSort(int[]nums,int left,int right){
if(left>right)
return;
int i=left;
int j=right;
int pivot=nums[left];//pivot用来记录基准值,这里就用nums[left]来指定
while(i<j){
while(i<j && nums[j]>=pivot){ //每次从右边往左走,直到找到小于基准值的数
j--;
}
while(i<j && nums[i]<=pivot){//每次从左边往右走,直到找到大于基准值的数
i++;
}
if(i<j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
nums[left]=nums[i]; //把“相交的数”放到最左边
nums[i]=pivot; //最初始的基准数放到中间
quickSort(nums,left,i-1);//递归
quickSort(nums,i+1,right);
}
}
补充: 以上的排序方式,即便是快排,其时间复杂度也是O(nlogn),达不到进阶的O(n)时间复杂度。
明天学习一下此题别的解法
长度最小的子数组
根据题目的提示: 时间复杂度要为 O(n),脑海中第一时间就想到整个数组只能遍历一次。那么就使用了快慢指针(提交过后看题解才知道原来这个叫做滑动窗口)。
在数据结构方面入手,其实滑动窗口本质上有点类似于队列。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int sum=0;
for(int i=0;i<nums.length;i++){
if(nums[i]==target) //反正遍历了 顺带判断一下也无所谓
return 1;
sum+=nums[i];
}
if(sum<target)
return 0;
//上面已经排除了target直接存在于nums中 以及 target根本不可能存在于nums中的情况
//并且上面同样也已经排除了nums.length==1的情况
sum=0;
int numsLen=nums.length;
int outLen=nums.length+1; //默认把要调用的outlen调整至比nums.length还大
int slow=0;
int fast=0;
while(slow<numsLen){
if(sum<target){
if(fast==numsLen) //这个时候,快指针已经越界,说明不能再添加元素
break;
else{
sum+=nums[fast];
fast++;
}
}
else{ //sum大于等于target的情况
outLen=(fast-slow)<outLen?(fast-slow):outLen; //获取较小值
sum-=nums[slow];
slow++;
}
}
if(outLen==nums.length+1)
return 0;
else
return outLen;
}
}
补充: 看了Carl哥的讲解视频之后,感觉他的方法更加简洁明了,并且理解上也更容易。自己在IDEA中复现了一下代码。如下所示:
public static void main(String[] args) {
int[] nums={2,3,1,2,4,3};
System.out.println(result(nums,7));
}
static int result(int[] nums,int target){
int slow=0;
int fast=0;
int outLen=nums.length+1;
int sum=0;
for(fast=0;fast<nums.length;fast++){
sum+=fast;
while(sum>=target){
outLen=(fast-slow+1)<outLen?(fast-slow+1):outLen;
sum-=nums[slow];
slow++;
}
}
if(outLen>nums.length)
return 0;
else
return outLen;
}
个人理解:
-
对于我个人而言,复现的时候最怕出现边界情况,即当fast指针到了最右边的时候,此时程序该如何运行?
- 当fast指针到达最右边(即fast==nums.length-1)时,此时只要sum>=target,那么slow指针就会一直往右走,直到sum<target,并最后跳出整个大循环。
螺旋矩阵
螺旋矩阵的代码,再一次强调了循环不变量的概念。
- 正因为是循环,个人理解,一个好的循环,它应当是每次循环它的“界限”是定的。下方代码为左闭右开[ )
public static void main(String[] args) {
int n=4;
int[][] nums=new int[n][n];
int loop=1;
int startX=0;
int startY=0;
int i=0,j=0;
int number=1;
//对于n*n的二维数组 每循环一圈(一圈指的是四周),纵向看,高度就会少2,每一个loop少2 所以loop<=n/2
//我这里loop的初始值是从1开始的,因为发现如果从1开始的话 loop也就能同步运用到下方for循环之中了
while(loop<=n/2){
//循环不变量!—— 对于循环来说,我们希望每次循环它的“界限”是一定的。这里采用左闭右开[ ) 的原则
//即每一条边的最后一个元素,交给下一个循环去处理,当做其第一个元素。
for(j=startY;j<n-loop;j++){ //第一条边 最上面的
nums[startX][j]=number++;
}
for(i=startX;i<n-loop;i++){ //第二条边 最右边的
nums[i][j]=number++;
}
for(;j>startY;j--){ //第三条边,最下面的
nums[i][j]=number++;
}
for(;i>startX;i--){
nums[i][j]=number++;//第四条边,最左边的
}
startX++;
startY++;
loop++;
}
if(n%2==1){
nums[n/2][n/2]=number;
}
for(i=0;i<n;i++){
for(j=0;j<n;j++){
System.out.print(nums[i][j]+" ");
}
System.out.println();
}
}