数组刷题笔记

011 盛最多水的容器

请添加图片描述

1.利用双重for循环解决

public static int maxArea(int[] height){
        int n = height.length;
        int max = 0;
        int now_intent = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i+1; j < n; j++) {
                now_intent = (j-i)*(Math.min(height[i], height[j]));
                if(now_intent>max)
                    max = now_intent;
            }
        }
        return max;
    }
  • 结果:通过55/60;
  • 超出时间限制
  • O(n^2)

2.双指针

  • 左右最两端分别设置指针 left & right
  • 左右两端指针之间的长度为 len
  • 容器初始容量: Math.min(left,right)*len
  • 指针移动:低的一端指针向内移动;
  • 将每次比较的最大值保存下来

指针移动:假设 Math.min(left,right) X len
结果为 left X len。若移动的是更高的一端的指针,则下一次的结果一定会小于 left X len,
因为下一次:len-1 < len(容器底部变小了) , Math.min(left , right) <= left(容器两端最低处不会超过上一次的最低处);

因此,指针移动应该干掉低端的,在容器底部变小的情况下,想办法让”短板“变长。

public static int maxArea2(int[] height){
        int left = 0;
        int right = height.length-1;
        int max = 0;
        int now_intent = 0;
        while(left<right){
            now_intent = Math.min(height[left],height[right])*(right-left);
            if(now_intent>max)
                max = now_intent;
            if (height[left] < height[right])
                left++;
            else
                right--;
        }
        return max;
    }
  • 结果:O(n)

015 三数之和

在这里插入图片描述

思路(将题目隐式地转为两数之和):

  • 首先,示例2、3的输出[ ]中括号的情况,是返回了空的List<List<Integer>> resultList的结果
  • 满足条件后,将数组先进行从小到大的排序,若最小的数也大于0,则没有符合条件的结果
  • 两层循环:
  1. 将外层循环的值保存为first;
  2. 内层循环中,设置一个set数组(去重\second的值在其中查找)。内层循环中每次取出一个值记录为third(因为这样可以使first<second<third);
  3. 在set中查找是否有值等于 -(first+third),即记录为second的值
  4. 没有,则把当前third存到set数组中(后续second的值从set中找)
  5. 有,则添加这三个数到resultList中,按大小顺序。不用退出当前的循环,因为后续可能有其他的组合方式;

需要注意的问题:

  1. 外层循环中的first值:如果本次的first值和上一次first值相同,则应该跳过本次循环。因为在上一次循环时,已经考虑完了所有的情况。如果这次再继续,则会发生重复的结果。
  2. 内层循环中,set中找到想要的值后,如果下一次的third值和本次一样,那么应该跳过下一次的third值,否则会产生和本次相同的结果。
public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> resultList = new ArrayList<>();
        if(nums.length<3)
            return resultList;
        Arrays.sort(nums);

        for(int i = 0; i< nums.length;i++){
            //当前排序下的第一个大于零,则后续一定都大于零
            if(nums[i]>0)
                break;

            int first = nums[i];

            //如果本次循环的first和上次一样,则跳过本次循环
            if(i>0 && first == nums[i-1])
                continue;

            Set<Integer> set = new HashSet<>();

            for(int j = i+1; j< nums.length ;j++){
                int third = nums[j];
                int second = -(first+third);
                if(set.contains(second)){
                    resultList.add(Arrays.asList(first,second,third));

                    while(j < nums.length-1 && third == nums[j+1]) {j++; continue;}
                }

                set.add(third);
            }

        }
        return resultList;
    }
    

    public static void main(String[] args) {
//        int[] nums = {-1,-1,0,1,2,3};
//        int sum = Arrays.stream(nums).sum();
        int[] nums={0,0,0,0}; //超出时间,67行应添加j++
        List<List<Integer>> lists = threeSum(nums);
        System.out.println(lists);
    }
  • 结果:O(n^2)

016 最接近的三数之和

在这里插入图片描述

思路:

  • 数组排序
  • 三重循环
  • 当每层循环的和大于target时,就比较abs(result,target)和abs(当前结果,target),更新result,并退出当前这层循环
public static int threeSumClosest(int[] nums, int target) {

    //从小到大排序
    Arrays.sort(nums);

    int result = nums[0]+nums[1]+nums[2];
    //最小的也大于目标值
    if(result>target)
        return result;

    for (int i = 0; i < nums.length-2; i++) {
        if(nums[i]+nums[i+1]+nums[i+2]>target){
            if(Math.abs(result-target)>Math.abs(nums[i]+nums[i+1]+nums[i+2]-target))
                result = nums[i]+nums[i+1]+nums[i+2];
            break;
        }
        for (int j = i+1; j < nums.length-1; j++) {
            if(nums[i]+nums[j]+nums[j+1]>target){
                if(Math.abs(result-target)>Math.abs(nums[i]+nums[j]+nums[j+1]-target))
                    result = nums[i]+nums[j]+nums[j+1];
                break;
            }
            for (int k = j+1; k < nums.length; k++) {


                if(nums[i]+nums[j]+nums[k]>target){
                    if(Math.abs(result-target)>Math.abs(nums[i]+nums[j]+nums[k]-target))
                        result = nums[i]+nums[j]+nums[k];
                    break;
                }
                if(nums[i]+nums[j]+nums[k]==target)
                    //如果差为0;则退出循环
                    return target;
                if(Math.abs(result-target)>Math.abs(nums[i]+nums[j]+nums[k]-target))
                    result = nums[i]+nums[j]+nums[k];

            }

        }
    }
    return result;

}

运行结果:

  • 326 / 381
  • 超出时间限制

思路2:

  • 参考三数之和的思路:排序+双指针
  • 外层循环用于定下first,内层first右侧的数组左右两端设置指针left&right
  • 比较first+nums[left]+nums[right]的值与target的大小:
    • first+nums[left]+nums[right] > target —>使值变小—> right–
    • first+nums[left]+nums[right] < target —>使值变大—> left++
public static int threeSumClosest2(int[] nums, int target) {
    //从小到大排序
    Arrays.sort(nums);

    //初始化目标值
    int result = nums[0]+nums[1]+nums[2];

    //最小的也大于目标值
    if(result>target)
        return result;

    int first;
    int left;
    int right;

    for (int i = 0; i < nums.length-2; i++) {
        //保证和上次枚举的不一样
        if(i>0&&nums[i]==nums[i-1])
            continue;
        first = nums[i];

        //设置双指针,指向first之后的范围
        left = i+1;
        right = nums.length-1;

        //本次循环一开始前三项就大于target,则可以考虑退出循环了
        if(first+nums[i+1]+nums[i+2]>target){
            if(Math.abs(result-target)>Math.abs(first+nums[i+1]+nums[i+2]-target))
                result = nums[i]+nums[i+1]+nums[i+2];
            break;
        }

        while(left<right){
            if(first+nums[left]+nums[right]==target)
                return target;
            else if(first+nums[left]+nums[right]>target)
            {
                if(Math.abs(result-target)>Math.abs(first+nums[left]+nums[right]-target))
                    result = first+nums[left]+nums[right];
                right--;
            }
            else{
                if(Math.abs(result-target)>Math.abs(first+nums[left]+nums[right]-target))
                    result = first+nums[left]+nums[right];
                left++;
            }
        }
    }
    return result;

}

075 颜色分类

思路:

  • 方法一:单指针 时间:O(n) 空间:O(1)

    遍历两次:第一次确定0的位置;第二次确定1的位置

  • 方法二:双指针[1] 时间: O(n) 空间:O(1)

    设置指针 p0、p1:

    • p0用于指向数字0的空间的下一个位置;
    • p1指针指向序列1末尾的下一个位置;

    开始p0 p1指向最开始的元素,从i=0开始遍历;

    • num[i] = 2,则 i++ ; 找下一个元素;
    • num[i] = 1,则交换num[i] 和 num[p1] ,p1++; 即把新找到的1放到1序列中,指针p1向后移动一位
    • num[i] = 0 ,则交换num[i] 和 num[p1] ,p0++,p1++; 即把新找到的0放到0序列中,此时可能会把0后面紧跟的1换到num[i]去了,因此需要再将num[i]的1换到1序列的尾部去。因此两个指针都要向后移动一个。
  • 方法三:双指针[2] 时间: O(n) 空间:O(1)

    • 数组两端设置指针p0,p2,把0换到最前面,2换到后面。

    • 每次交换,要改动其中一个指针的位置(p0右移或者p2左移)

    • 每次交换后,i++;但是

      • 换出来的结果如果不是1,需要 --i,再进行一次判断

代码:

  • 方法一:
public void sortColors(int[] nums) {
    int p = 0;
    //第一次遍历确定0的位置
    for (int i = 0; i < nums.length; i++) {
        if(nums[i]==0){
            nums[i] = nums[p];
            nums[p] = 0;
            p++;
        }
    }
    //第二次遍历确定1的位置
    for (int j = p; j < nums.length; j++) {
        if(nums[j]==1){
            nums[j] = nums[p];
            nums[p] = 1;
            p++;
        }
    }
}
  • 方法二
public void sortColors(int[] nums) {
    int length = nums.length;
    //p0,p1指针初始位置
    int p0=0,p1=0;
    int temp;
    for (int i = 0; i < length; i++) {
        //当前元素是1,则放到1序列中去
        if(nums[i]==1){
            temp = nums[p1];
            nums[p1] = 1;
            nums[i] = temp;
            //1指针向后移动一个
            p1++;
        }
        //当前元素是0,则放到0序列中去
        else if (nums[i]==0){
            temp = nums[p0];
            nums[p0] = 0;
            nums[i] = temp;
            //可能会把0后面的1换走,因此需要把1换到1序列末尾去
            if(nums[i]==1){
                temp = nums[p1];
                nums[p1] = 1;
                nums[i] = temp;
            }
            //1指针一定会在0右边或者和0重合,因此0指针动了,则1也必须动
            p1++;
            p0++;
        }
    }
}
  • 方法三
public void sortColors3(int[] nums) {
    if(nums.length<=1)
        return;
    int p0 = 0;
    int p2 = nums.length-1;
    int i = 0;
    //当i>p2时,说明分组结束了
    while(i<=p2){
        while(p0<=nums.length-1&&nums[p0]==0&&i<=p2) {i++;p0++;}
        while(p2>=0&&nums[p2]==2&&i<=p2) {p2--;}
        if(i>p2) break;
        //把0换到前面
        if(nums[i]==0){
            nums[i] = nums[p0];
            nums[p0]= 0;
            i++;
            p0++;
            //容易产生越界异常
            if(i<= nums.length-1&&nums[i-1]!=1){
                i--;
            }
        }// 把2换到后面
        else if(nums[i]==2){
            nums[i] = nums[p2];
            nums[p2] = 2;
            p2--;
            i++;
            //容易产生越界异常
            if(i<= nums.length-1 && nums[i-1]!=1)
                i--;
        }else{
            i++;
        }
    }

}

026 删除有序数组重复项

在这里插入图片描述

思路:

  • 已经升序,则只需要设置两个指针p0,p1,当指针指向值相等时,p1向后移动,不等时,将p0增1,再将p1值赋值给p0指向位置即可
public int removeDuplicates(int[] nums) {
    if(nums.length==1)
        return 1;

    int p = 0;
    for (int i = 1; i < nums.length; i++) {
        while(i < nums.length&&nums[i]==nums[p])
            i++;
        if(i < nums.length&&nums[i]!=nums[p])
            nums[++p] = nums[i];

    }
    return p+1;
}
  • 优化:
    在这里插入图片描述

    当如图形式时,就不用再操作一次将q的值赋给p+1的值了。因此可以设置一个条件,即 q-p>1 时,才需执行赋值操作

    if(i < nums.length && i-p>1 && nums[i]!=nums[p])
        nums[++p] = nums[i];
    else if(i < nums.length && i-p==1 && nums[i]!=nums[p])
        p++;
    

别人的代码[好简洁]:

public int removeDuplicates(int[] nums) {
    if(nums == null || nums.length == 0) return 0;
    int p = 0;
    int q = 1;
    while(q < nums.length){
        if(nums[p] != nums[q]){
            if(q - p > 1){
                nums[p + 1] = nums[q];
            }
            p++;
        }
        q++;
    }
    return p + 1;
}

031 下一个排列

在这里插入图片描述

思路:

以下思路存在错误

  • 从右往左比较两数大小,左小右大时将两数进行交换,产生交换行为则停止;
  • [1,1,3,5,2] --> [1,1,5,3,2]
  • 将右侧遍历到的数进行升序排序。
  • //(由算法知,右边是天然降序,只需交换两端即可)–错误,如果两数相同,则会出错
  • [1,1,5,3,2] --> [1,1,5,2,3]

正确思路:

  • 从右往左比较两数大小,左小右大时,则停止比较,并记录当前左边小一点的数的位置为p0;
  • 将p0右边的数再次进行遍历,找到比nums[p0]大的数中,最小的数nums[p1]
  • 交换p0和p1位置的数
  • 将p0右侧(不含p0)的数进行升序排序

指定位置的升序公式:

//Arrays.sort(int[] a, int fromIndex, int toIndex)
//fromIndex:起点的下标【含】
//toIndex:终点的下标【不含】
Arrays.sort(nums,p+1,nums.length);
public static void nextPermutation(int[] nums) {
    if(nums.length==1) return;
    int p = nums.length-1;
    int q = p;
    int min_max = -1;
    int temp;
    while(p>0&&nums[p]<=nums[p-1]){
        p--;
    }
    if(p==0){
        Arrays.sort(nums);
    }
    else{
        p--;
        //找到后续大的数之中的最小值
        while(p<q){
            if(min_max==-1&&nums[p]<nums[q])
                min_max = q;
            if(nums[p]<nums[q]&&nums[min_max]>nums[q])
                min_max = q;
            q--;
        }
        //交换
        temp = nums[min_max];
        nums[min_max] = nums[p];
        nums[p] = temp;
        //对后续排序
        Arrays.sort(nums,p+1,nums.length);
    }

}

054.螺旋矩阵

给你一个 mn 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
在这里插入图片描述

  • 思路:

    每一层的遍历顺序为:

    1. 从左到右–> 上边界-1
    2. 从上到下–> 右边界-1
    3. 从右到左–> 下边界-1
    4. 从下到上–> 左边界-1

    停止遍历的条件为:

    • 上下边界重合,或者
    • 左右边界重合

代码实现:

public static List<Integer> spiralOrder(int[][] matrix) {
    int m = matrix.length;
    int n = matrix[0].length;
    List<Integer> aList = new ArrayList<>();

    int up = 0;
    int down = m;
    int left = 0;
    int right = n;
    int j = 0;
    int i = 0;

    while(true){

        j = left;
        //从左往右,横坐标不变,纵坐标++
        while(j!=right){
            aList.add(matrix[up][j++]);
        }
        up++; j--;
        if(up==down) break;

        //从上往下,纵坐标不变,横坐标++
        i = up;
        while(left!=right&&i!=down){
            aList.add(matrix[i++][j]);
        }
        right--; i--;
        if(left==right) break;

        //从左往右,横坐标不变,纵坐标--
        while(j!=left){
            aList.add(matrix[i][--j]);
        }
        down--;
        if(up==down) break;

        //从下往上,纵坐标不变,横坐标--
        while(i!=up){
            aList.add(matrix[--i][j]);
        }
        left++;
        if(left==right) break;

    }
    return aList;
}

059. 螺旋矩阵Ⅱ

给你一个正整数 n ,生成一个包含 1n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix
在这里插入图片描述
思路:

依旧在while循环中按层遍历:

  • 每一层的操作如下:
  1. ​ (top,left)–>(top,right)
  2. ​ (top+1,right)–>(bottom,right)
  3. ​ (bottom,right-1)–>(bottom,left)
  4. ​ (bottom-1,left)–>(top+1,left)
  • 循环的条件是: top<=bottom&&left<=right

  • 注意矩阵的初始化方式是:

    int[][] result = new int[n][n];
    

实现代码:

public int[][] generateMatrix(int n) {
    int[][] result = new int[n][n];

    //按照螺旋数组1的方式,设置边界
    //但是,正方形,可以更优化,有规律可循
    int top = 0;
    int bottom = n-1;
    int left = 0;
    int right = n-1;

    int i = 0;
    int count = 1;

    while(top<=bottom&&left<=right) {
        //(top,left)-->(top,right)
        for (int j = left; j <= right; j++) {
            result[top][j] = count++;
        }

        //(top+1,right)-->(bottom,right)
        for (int j = top+1; j <= bottom; j++) {
            result[j][right] = count++;
        }

        //(bottom,right-1)-->(bottom,left)
        for (int j = right-1; j >= left; j--) {
            result[bottom][j] = count++;
        }

        //(bottom-1,left)-->(top+1,left)
        for (int j = bottom-1; j >= top+1; j--) {
            result[j][left] = count++;
        }

        top++;bottom--;left++;right--;

    }

    return result;
}

遇到的问题

1 List数组的初始化

在初始化
List aList=null; 后,运行时,发生空指针异常;
原因在于,这种初始化方法只在栈中有引用而在堆中没有分配到内存空间

正确的初始化方法应该是

List aList = new ArrayList[];

2 asList()

该方法是将数组转成list,是JDK中java.util包中Arrays类的静态方法。

使用示例:

ArrayList<Integer> copyArrays=new ArrayList<Integer>(Arrays.asList(ob));//这样就得到一个新的list,可对其进行add,remove了
copyArrays.add(222);//正常,不会报错

Collections.addAll(new ArrayList<Integer>(5), ob);//或者新建一个空的list,把要转换的数组用Collections.addAll添加进去

3 static 关键字

static关键字用于内存管理中,可以使用在:

  • 方法
  • 变量
  • 代码块
  • 嵌套类
  1. 静态变量 :
  • 节省内存;静态化的这个字段将只获得内存一次。
  • 每个实例对象都共享操作同一个内存的这个字段。
  1. 静态方法:
  • 静态方法属于类,可以直接在类中调用,无需创建类的实例。
  • 并不属于实例对象。
  • 静态方法可以访问静态数据成员,并可以更改静态数据成员的值。
  • 静态方法限制:
    • 静态方法不能直接使用非静态数据成员或调用非静态方法。
    • thissuper两个关键字不能在静态上下文中使用。
  1. 静态块
  • 初始化静态数据成员
  • 类加载时,在main方法之前执行

4. 矩阵相关

  • 矩阵的行数:matrix.length
  • 矩阵的列数:matrix[0].length
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值