【数组篇】力扣刷题1

test88.合并两个有序数组

题目如下:
在这里插入图片描述
示例1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3][2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

示例2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1][] 。
合并结果是 [1]

示例3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [][1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

在这里插入图片描述

解题思路
这道题,基本上是简单粗暴,先将nums2添加到nums1中,然后进行排序。

Java代码

class Solution {
    
    
    public void merge(int[] nums1, int m, int[] nums2, int n) {
    
    
        int a = 0;
        for (int i=m;i<nums1.length;i++) {
    
    
            if (a<n) {
    
    
                nums1[i] = nums2[a];
                a++;
            }
        }
        Arrays.sort(nums1);
    }
}

JavaScript代码

/**
 * @param {number[]} nums1
 * @param {number} m
 * @param {number[]} nums2
 * @param {number} n
 * @return {void} Do not return anything, modify nums1 in-place instead.
 */
var merge = function(nums1, m, nums2, n) {
    
    
    let a = 0
      for (let i = m; i < m + n; i++) {
    
    
        if (a < n) {
    
    
          nums1[i] = nums2[a];
          a++;
        }
      }
    nums1.sort((a, b) => a - b);
};

test11. 盛最多水的容器

题目如下:
在这里插入图片描述
在这里插入图片描述
示例1:

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49

示例2:

输入:height = [1,1]
输出:1

在这里插入图片描述
解题思路
最开始我想的比较直观,直接通过双重for循环将每一种可能出现的结果存到list集合中,然后对其进行排序取最大值。就在我以为这道题就这么“轻松”地解出来的时候,时间复杂度告诉我——我果然是想得太简单了,超出了时间限制,呜呜呜呜~
在这里插入图片描述
后来看了一些题解之后,才知道原来有个“双指针解法”。

  1. 通过示例中的图解可以得出:储水面积=两个指针指向的数字中较小值∗指针之间的距离。
    设两个指针分别为i,j,即水槽板高度为height[i],height[j],储水有效高度为height[i],height[j]中最短的水槽板高度,指针之间的距离为(j-i);
    因此可以推出公式:area = min( height[i] , height[j] ) * ( j - i );
    此时我们得到的数据中有两个变量:

    • 储水高度:两个指针指向的数字中较小值
    • 储水宽度:两个指针之间的距离
  2. 要使储水量面积最大,储水高度尽可能最大,储水宽度尽可能最大。但是在不知道指针指向的数字是多少的情况下,我们只能先让宽度取最大值,即取首末两端指针(作为容器边界的所有位置的范围)。那么接下来该如何移动水槽板取值呢?在首末端指针向中间移动取值的过程中,储水的宽度必然会变小,然而在同等缩小的条件下我们需要考虑到两种情况:

    • 如果变换短板 ,min( height[i] , height[j] ) 可能变大,则下个水槽的储水面积可能增大 。
    • 如果变换长板 ,min( height[i] , height[j] ) 不变或变小,则下个水槽的储水面积一定变小 。

所以,对应数字较小的那个指针就不可能作为容器的边界了。
不得不承认,这个解法真的很——优雅,实在是优雅。
Java代码

class Solution {
    
    

//    超出时间限制————
//    public static int maxArea(int[] height) {
    
    
//        ArrayList<Integer> list = new ArrayList<Integer>();
//        for (int i=0;i<height.length;i++) {
    
    
//            for (int j=i;j< height.length;j++) {
    
    
//                if (height[i]>height[j]) {
    
    
//                    list.add(height[j] * (j-i)) ;
//                } else {
    
    
//                    list.add(height[i] * (j-i)) ;
//                }
//            }
//        }
//        Collections.sort(list);
//        return list.get(list.size()-1);
//    }

//    采用双指针的解法
    public static int maxArea(int[] height) {
    
    
        int i = 0;
        int j = height.length-1;
        int area = 0;
        int max = 0;
        while(i<j) {
    
    
            area = Math.min(height[i],height[j])*(j-i);
            max = Math.max(area,max);
            if (height[i]<=height[j]) {
    
    
                i++;
            } else {
    
    
                j--;
            }
        }
        return max;
    }
}

JavaScript:

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
    
    
      let i = 0;
      let j = height.length - 1;
      let area = 0;
      let max = 0;
      while (i < j) {
    
    
        area = Math.min(height[i], height[j]) * (j - i)
        max = Math.max(area, max)
        if (height[i] <= height[j]) {
    
    
          i++;
        } else {
    
    
          j--;
        }
      }
      return max;
};

test15. 三数之和

在这里插入图片描述
示例1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1][-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。

示例2:

输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0

示例3:

输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0

在这里插入图片描述
解题思路
看到这道题第一眼,我原是“不屑”的,这么简单的题,让我一下子就有了“灵感”。于是乎,直接三层for循环加if条件限制,然后通过排序进行数组去重。但是!如此“暴力”的解决方式,自然是不被允许的啊~ 毕竟,大家都是“文明人”。
在这里插入图片描述

最简单的题型往往要采用“优雅”的解决方式——没错,又是“双指针解法”。
题中所需要满足的条件:

  • 三元组满足: i!=j,i!=k,j!=k
  • nums[i] + nums[j] + nums[k] == 0
  1. 我们先对数组进行排序,此时的排序在接下来的步骤中有一个重要的作用。(官方题解中排序还有一个作用:用于判断该数组最小的数是否大于0,如果大于0就不用进行后续操作,直接输出为[],我的解题代码中并未写出此点)
  2. 然后进行遍历,取该数组的a+1位和最后一位作为指针,而第a位则作为不变项,且两指针不会重复。
  3. 将满足条件的三元组添加到list集合中进行排序,方便去重。
  4. 那么两个指针该如何移动呢?此时,我们最开始的排序操作开始发挥它的功能了——
    • 当满足 nums[a] + nums[a+1] + nums[nums.length-1] == 0,我们就不需要考虑是否存在nums[a+1] ==nums[a+2]或者nums[nums.length-1]==nums[nums.length-2],因为需要数组去重,所以我们让两个指针都往中间移动。
    • 当满足 nums[a] + nums[a+1] + nums[nums.length-1] > 0时,如果指向 nums[a+1] 的指针往中间移动,那么得到的结果有一种可能>0;而只有当指向 nums[nums.length-1] 的指针往中间移动,得到的结果可能会<0,==0。基于此,我们只能移动 nums[nums.length-1] 的指针。
    • 同理可得,当满足 nums[a] + nums[a+1] + nums[nums.length-1] < 0时,说明得到的和太小了,我们要想让它==0,只能移动 nums[a+1] 指针,才能让它变大。

Java代码

class Solution {
    
    

//    超出时间限制————
//    public static List<List<Integer>> threeSum(int[] nums) {
    
    
//        List<List<Integer>> lists = new ArrayList<>();
//        for (int i = 0;i<nums.length;i++) {
    
    
//            for (int j = i;j<nums.length;j++) {
    
    
//                for (int k = j;k< nums.length;k++) {
    
    
//                    if (nums[i] + nums[j] + nums[k]==0 && i!=j && j!=k && i!=k) {
    
    
//                        List list = new ArrayList<Integer>();
//                        list.add(nums[i]);
//                        list.add(nums[j]);
//                        list.add(nums[k]);
//                        Collections.sort(list);
//                        if (!lists.contains(list)) {
    
    
//                            lists.add(list);
//                        }
//                    }
//
//                }
//            }
//        }
//        return lists;
//    }


//   排序 + 双指针解法
    public static List<List<Integer>> threeSum(int[] nums) {
    
    
      List<List<Integer>> lists = new ArrayList<>();
      Arrays.sort(nums);
        for (int a=0;a< nums.length;a++) {
    
    
            int b = a+1;
            int c = nums.length-1;
            while(b<c) {
    
    
                if (nums[a]+nums[b]+nums[c]==0&&a!=b&&b!=c&&c!=a) {
    
    
                    List list = new ArrayList<Integer>();
                    list.add(nums[a]);
                    list.add(nums[b]);
                    list.add(nums[c]);
                    Collections.sort(list);
                    if (!lists.contains(list)) {
    
    
                        lists.add(list);
                    }
                    b++;
                    c--;
                } else if (nums[a]+nums[b]+nums[c]>0) {
    
    
                    c--;
                } else if (nums[a]+nums[b]+nums[c]<0) {
    
    
                    b++;
                }
            }
        }
        return lists;
    }
}

JavaScript代码

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    
    
let lists = [];
      nums.sort((a, b) => a - b);
      for (let i = 0; i < nums.length; i++) {
    
    
        if (i > 0 && nums[i] == nums[i - 1]) continue;
        let j = i + 1;
        let k = nums.length - 1;
        while (j < k) {
    
    
          if (nums[i] + nums[j] + nums[k] == 0 && i != j && j != k && k != i) {
    
    
            let list = [nums[i], nums[j], nums[k]];
            lists.push(list);
            while (j < k && nums[j] == nums[j + 1]) j++;
            while (j < k && nums[k] == nums[k - 1]) k--;
            j++;
            k--;
          } else if (nums[i] + nums[j] + nums[k] > 0) {
    
    
            k--;
          } else if (nums[i] + nums[j] + nums[k] < 0) {
    
    
            j++;
          }
        }
      }
      return lists;
};

test16. 最接近的三数之和

在这里插入图片描述
示例1:

输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2)

示例2:

输入:nums = [0,0,0], target = 1
输出:0

在这里插入图片描述
解题思路
看到这道题很难不让我“会心一笑”啊,在经历了前两道“超出时间限制”的警告下,我不得不承认“优雅永不过时”。这不是摆明了要我用“双指针解法”嘛。
在这里插入图片描述

  1. 首先,我们先来对该数组进行排序。
  2. 取一个变量m存放和target最接近的值(对m初始化时,需要保证,target不会取到该值)
  3. 接着,进行遍历,取该数组的i+1位和最后一位作为指针,而第 i 位则作为不变项,且两指针不会重复。
  4. 采用两数相减取绝对值的方式,来判断哪一个和与target最接近。
  5. 重头戏来了!那么两个指针该如何移动咧——
    • 当 sum>target 时,那么我们需要让sum变小,便向中间移动指向 nums[nums.length-1]的指针;
    • 当 sum<target 时,那么我们需要让sum变大,便向中间移动指向 nums[i+1]的指针;
    • 当 sum==target时,那么还犹豫什么呢?直接跳出遍历,输出 m 啊!

Java代码

class Solution {
    
    
    public static int threeSumClosest(int[] nums, int target) {
    
    
        Arrays.sort(nums);
        int m = 10000000;
        for (int i=0;i<nums.length;i++) {
    
    
            int a = i+1;
            int b = nums.length-1;
            while(b>a) {
    
    
                int sum = nums[i]+nums[a]+nums[b];
                if (sum>target) {
    
    
                    m = Math.abs(m-target)<Math.abs(sum-target)?m:sum;
                    b--;
                } else if (sum<target) {
    
    
                    m = Math.abs(m-target)<Math.abs(sum-target)?m:sum;
                    a++;
                } else if(sum==target) {
    
    
                    m=sum;
                    break;
                }
            }
        }
        return m;
    }
}

JavaScript代码

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var threeSumClosest = function(nums, target) {
    
    
let m = 10000000;
      nums.sort((a, b) => a - b);
      for (let i = 0; i < nums.length; i++) {
    
    
        let a = i + 1;
        let b = nums.length - 1;

        while (a < b) {
    
    
          let sum = nums[i] + nums[a] + nums[b];
          if (sum > target) {
    
    
            m = Math.abs(m - target) < Math.abs(sum - target) ? m : sum;
            b--;
          } else if (sum < target) {
    
    
            m = Math.abs(m - target) < Math.abs(sum - target) ? m : sum;
            a++;
          } else if (sum == target) {
    
    
            m = sum;
            break;
          }
        }
      }
      return m;
};

test4. 寻找两个正序数组的中位数

在这里插入图片描述
示例1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

在这里插入图片描述
解题思路
咳咳,我是带着勇气做这道题的,因为它标的难度是“困难”,而且我在看题时,有被那个时间复杂度给吓到(众所不知,对于一个“算法小白”,时间和空间复杂度一直是我的“心头之患”。)
但是,我还是抱着try一try的心态尝试了一下。
在这里插入图片描述
我们先来看题中给的条件和要求:

  • 所给的数组都是从小到大排序
  • 返回这两个数组的中位数
  1. 首先,我们先将两个数组进行合并;
  2. 然后,对合并后的数组进行排序;
  3. 接着,我们需要考虑两种情况:
    • 当得到的新数组长度是偶数时,我们需要取到最中间的两位数nums[nums.length/2-1],nums[nums.length/2],中位数取这两位和的平均值。
    • 当得到的新数组长度是奇数时,我们直接取最中间的那位数作为中位数就行。

按照以上思路,在我一顿操作提交运行下,竟然“顺利”地通过了。说实话,有被“惊讶”到!

Java代码

class Solution {
    
    
    public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
    
    
        int[] nums = new int[nums1.length+ nums2.length];
        for (int i=0;i<nums1.length;i++) {
    
    
            nums[i] = nums1[i];
        }
        int j = nums1.length;
        for (int i=0;i<nums2.length;i++) {
    
    
            nums[j++] = nums2[i];
        }
        double res = 0;
        Arrays.sort(nums);
        if (nums.length%2==0) {
    
    
            int a = nums.length/2-1;
            int b = nums.length/2;
            res = (double) (nums[a]+nums[b])/2;
        } else {
    
    
            int a = nums.length/2;
            res = nums[a];
        }
        return res;
    }
}

JavaScript代码

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number}
 */
var findMedianSortedArrays = function (nums1, nums2) {
    
    
      let nums = [];
      for (let i = 0; i < nums1.length; i++) {
    
    
        nums[i] = nums1[i];
      }
      let j = nums1.length;
      for (let i = 0; i < nums2.length; i++) {
    
    
        nums[j++] = nums2[i];
      }

      let res = 0;
      nums.sort((a, b) => a - b);

      if (nums.length % 2 == 0) {
    
    
        let a = parseInt(nums.length / 2 - 1);
        let b = parseInt(nums.length / 2);
        res = (nums[a] + nums[b]) / 2;
      } else {
    
    
        let a = parseInt(nums.length / 2);
        res = nums[a];
      }
      return res;
    };

test18. 四数之和

在这里插入图片描述示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

示例 2:

输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]

在这里插入图片描述
解题思路
这道题和前面的 test15.三数之和 类似,唯一不同的是此题求的是 四数之和,但是解法都一样。
本题我们需要考虑到是否会数据类型范围。看下面代码注释掉的一行是我原本写的代码,执行之后发现它爆红了!!!在这里插入图片描述
在这里插入图片描述
Java代码

class Solution {
    
    
    public static List<List<Integer>> fourSum(int[] nums, int target) {
    
    
        List<List<Integer>> lists = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0;i<nums.length-3;i++) {
    
    
            for (int j = i+1;j<nums.length-2;j++) {
    
    
                int k = j+1;
                int l = nums.length-1;
                while (k<l) {
    
    
//                  int sum = nums[i]+nums[j]+nums[k]+nums[l];  -- 空间复杂度
                    long sum =(long) nums[i]+nums[j]+nums[k]+nums[l];
                    if (sum>target) {
    
    
                        l--;
                    } else if (sum<target) {
    
    
                        k++;
                    } else if (sum==target&&i!=j&&j!=k&&k!=l&&l!=i) {
    
    
                        List list = new ArrayList<Integer>();
                        list.add(nums[i]);
                        list.add(nums[j]);
                        list.add(nums[k]);
                        list.add(nums[l]);
                        Collections.sort(list);
                        if (!lists.contains(list)) {
    
    
                            lists.add(list);
                        }
                        l--;
                        k++;
                    }
                }
            }
        }
        return lists;
    }
}

JavaScript代码

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[][]}
 */
var fourSum = function (nums, target) {
    
    
      let lists = []
      nums.sort((a, b) => a - b);
      for (let i = 0; i < nums.length - 3; i++) {
    
    
        if (i > 0 && nums[i] === nums[i - 1]) continue;
        for (let j = i + 1; j < nums.length - 2; j++) {
    
    
          if (j > i + 1 && nums[j] === nums[j - 1]) continue;
          let k = j + 1;
          let l = nums.length - 1;
          while (k < l) {
    
    
            let sum = nums[i] + nums[j] + nums[k] + nums[l];
            if (sum > target) {
    
    
              l--;
            } else if (sum < target) {
    
    
              k++;
            } else if (sum == target && i != j && j != k && k != l && l != i) {
    
    
              while (k < l && nums[k] == nums[k + 1]) k++;
              while (k < l && nums[l] == nums[l - 1]) l--;
              lists.push([nums[i], nums[j], nums[k], nums[l]]);
              l--;
              k++;
            }
          }
        }
      }
      return lists;
    };

test31. 下一个排列

在这里插入图片描述
示例1:

输入:nums = [1,2,3]
输出:[1,3,2]

示例2:

输入:nums = [3,2,1]
输出:[1,2,3]

示例3:

输入:nums = [1,1,5]
输出:[1,5,1]

在这里插入图片描述
解题思路:
我觉得这道题其实挺难的,甚至一开始没怎么理解题意,反复看了好几遍之后,才大概懂了它问的是什么——数组的排列组合其实也是有顺序的,看题目中举的例子其实说的已经很详细了。
我一开始想的解法就是——

  1. 找到交换数:先从这个数组对应[下标大]的开始往前判断,即从nums[nums.length-1]开始往前顺,从后往前找到第一个非递增的数,确定这个数作为交换数;
  2. 找跟它做交换的数,即被交换数,从我们刚刚遍历过来的数先找出所有比它大的数,从中找到与它最接近的来和它交换;
  3. 然后对交换数对应下标往后的所有数(即遍历过的数)进行升序排序。

事实证明,我的想法应该没问题,但用于实践好像不太行,我改了好几遍代码还是有十几条案例未通过,没有办法,我还是求助了解析。

正解思路:“双指针解法”——
我们先来想,要满足题意需要如何去做,我们需要使当前排列在此排列的基础上尽可能小的变大,那就需要将数组左边的一个较小数与数组右边的较大数交换。同时,要求这个较小数尽可能靠右,较大数尽可能小。

  • 找到交换数:先通过while循环,从后往前找到第一个非递增的数;
  • 找到被交换数:再次通过循环遍历,从后往前找到第一个大于交换数的数;

交换完成后,较大数右边的数需要升序排列。
所以,大家能看懂此解法如何巧妙地使用“双指针法”了嘛?

Java错误代码:

    public static int[] nextPermutation(int[] nums) {
    
    
        int[] numsa = new int[nums.length];
        for (int i=0;i<numsa.length;i++) {
    
    
            numsa[i] = -100000;
        }
        int flag = 0;
        for (int i=nums.length-1;i>=1;i--) {
    
    
            if (nums[i] <= nums[i-1]) {
    
    
                flag++;
                numsa[i] = nums[i];
            } else if (nums[i]>nums[i-1]) {
    
    
                numsa[i] = nums[i];
                int renum = nextClosest(numsa,nums[i-1]);
                int temp = nums[i-1];
                nums[i-1] = nums[renum];
                nums[renum] = temp;
                nextReverse(nums,i);
                break;
            }
        }
        if (flag==nums.length-1) {
    
    
            nums = nextReverse(nums,0);
        }
        return nums;
    }
    public static int nextClosest(int[] numsa,int count) {
    
    
        int min = 0;
        for (int i=1;i<numsa.length;i++) {
    
    
            if (numsa[i]!=-100000 && numsa[i]>count) {
    
    
                min = Math.abs(numsa[i]-count)<Math.abs(numsa[i-1]-count)?i:i-1;
            }
        }
        return min;
    }
    public static int[] nextReverse(int[] nums,int start) {
    
    
        int end = nums.length-1;
//        while (nums[start]>nums[end] && start<nums.length) {
    
    
        while (end>start && nums[start]>nums[end]) {
    
    
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start ++;
            end--;
        }

        return nums;
    }

Java正确代码:

class Solution {
    
    
    public void nextPermutation(int[] nums) {
    
    
        int litter = nums.length - 2;
        while (litter >= 0 && nums[litter] >= nums[litter + 1]) {
    
    
            litter--;
        }
        if (litter >= 0) {
    
    
            int bigger = nums.length - 1;
            while (bigger >= 0 && nums[bigger] <= nums[litter]) {
    
    
                bigger--;
            }
            swap(litter, bigger, nums);
        }
        reverse(litter + 1, nums);
    }

    public void swap(int i, int j, int[] nums) {
    
    
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    public void reverse(int start, int[] nums) {
    
    
        int end = nums.length - 1;
        while (start < end) {
    
    
            swap(start, end, nums);
            start++;
            end--;
        }
    }
}

果然做算法需要细心和耐心,还需要多方面考虑。上面的代码虽然最终都运行通过了,但仔细看的话,还是不够严谨。如果你有更好的解法,请和我分享叭~
如有错误,请指正!
在这里插入图片描述在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/aDiaoYa_/article/details/127455108