zoukankan      html  css  js  c++  java
  • LeetCode:两数之和、三数之和、四数之和

    LeetCode:两数之和、三数之和、四数之和

    利用哈希集合减少时间复杂度及双指针收缩窗口的巧妙解法

    No.1 两数之和

    题目:

    给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

    你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素

    示例:

    给定 nums = [2, 7, 11, 15], target = 9
    因为 nums[0] + nums[1] = 2 + 7 = 9
    所以返回 [0, 1]
    

    解法:

    1. 穷举所有组合

      时间复杂度:(O(n^2)),两次循环

      空间复杂度:(O(1))

      两层循环,判断每一种组合

    2. 利用哈希减少时间复杂度

      时间复杂度:(O(n)),一层循环(O(n)) * 哈希查找(O(1))

      空间复杂度:(O(n)),哈希表最多存 n 个元素

      将数组元素存入哈希表,查找对应目标元素是否在数组中

      对于每个元素,查找后面是否有目标元素

      public int[] twoSum(int[] nums, int target) {
          Map<Integer, Integer> map = new HashMap<>();
          for (int i = 0; i < nums.length; i++)
              map.put(nums[i], i);
          for (int i = 0; i < nums.length; i++) {
              int j = target - nums[i];
              if (map.containsKey(j) && map.get(j) != i) {
                  return new int[] { i, map.get(j) };
              }
          }
          return new int[]{};
      }
      

      优化,用一次循环解决问题。对于每个元素,查找前面是否有目标元素

      public int[] twoSum(int[] nums, int target) {
          Map<Integer, Integer> map = new HashMap<>();
          for (int i = 0; i < nums.length; i++) {
              int j = target - nums[i];
              if (map.containsKey(j) && map.get(j) != i) {
                  return new int[]{i, map.get(j)};
              }
              map.put(nums[i], i);
          }
          return new int[]{};
      }
      

      Map 存入相同元素会被覆盖,本题中并不影响结果

    No.15 三数之和

    题目:

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组

    注意:答案中不可以包含重复的三元组

    示例:

    给定数组 nums = [-1, 0, 1, 2, -1, -4]
    满足要求的三元组集合为:
    [
    [-1, 0, 1],
    [-1, -1, 2]
    ]
    

    解法:

    1. 穷举所有组合

      时间复杂度:(O(n^3)),三重循环(O(n^3))

      空间复杂度:(O(1))

      三层循环,判断所有组合,去重

    2. 利用哈希表减少时间复杂度

      时间复杂度:(O(n^2)),排序 (O(nlogn)) + 两重循环 (O(n^2)) * 哈希查找 (O(1))

      空间复杂度:(O(n))

      先进行排序,有利于后面去重

      两层循环,判断目标值是否在数组中,并且保证在后方数据中查找,以免造成重复

      public List<List<Integer>> threeSum(int[] nums) {
              List<List<Integer>> lists = new ArrayList<>();
              if (nums.length < 3) return lists;
              Arrays.sort(nums);//排序
              HashMap<Integer, Integer> map = new HashMap<>();
              for (int i=0;i<nums.length;i++)
                  map.put(nums[i],i);
              for (int i = 0; i < nums.length - 2; i++) {
                  if (i > 0 && nums[i] == nums[i - 1]) continue;//去重
                  for (int j = i + 1; j < nums.length; j++) {
                      if (j > i + 1 && nums[j] == nums[j - 1]) continue;//去重
                      int a = nums[i];
                      int b = nums[j];
                      int c = -a - b;
                      if (map.containsKey(c) && map.get(c) > j) {//向后查找
                          lists.add(new ArrayList<Integer>() {{
                              add(a);
                              add(b);
                              add(c);
                          }});
                      } else continue;
                  }
              }
              return lists;
          }
      
    3. 利用数据特点,双指针收缩窗口

      时间复杂度:(O(n^2))

      空间复杂度:(O(1))

      先进行排序,有利于去重

      三元组 [ k,i,j ],每一个 k,i = k+1,j = length-1

      求和,大于 0 ,j 右移,小于 0,i 左移,等于 0,存入 List

      public List<List<Integer>> threeSum(int[] nums) {
          List<List<Integer>> lists = new ArrayList<>();
          if (nums.length < 3) return lists;
          Arrays.sort(nums);//排序
          for (int k = 0; k < nums.length - 2; k++) {
              if (k > 0 && nums[k] == nums[k - 1]) continue;//去重
              int i = k + 1, j = nums.length - 1;
              while (i < j) {
                  int sum = nums[k] + nums[i] + nums[j];
                  if (sum < 0) i++;
                  else if (sum > 0) j--;
                  else {
                      List<Integer> list = new ArrayList<>();
                      list.add(nums[k]);
                      list.add(nums[i]);
                      list.add(nums[j]);
                      lists.add(list);
                      while (i < j && nums[i] == nums[i + 1]) i++;//去重
                      while (i < j && nums[j] == nums[j - 1]) j--;//去重
                      i++;
                      j--;
                  }
              }
          }
          return lists;
      }
      

    No.18 四数之和

    题目:

    给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组

    注意:答案中不可以包含重复的四元组

    示例:

    给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0
    满足要求的四元组集合为:
    [
    [-1,  0, 0, 1],
    [-2, -1, 1, 2],
    [-2,  0, 0, 2]
    ]
    

    解法:

    1. 利用哈希表减少时间复杂度

      时间复杂度:(O(n^3)),两重循环 (O(n^3)) * 哈希查找 (O(1))

      空间复杂度:(O(n))

      public List<List<Integer>> fourSum(int[] nums, int target) {
          List<List<Integer>> lists = new ArrayList<>();
          if (nums.length < 4) return lists;
          Arrays.sort(nums);
          HashMap<Integer, Integer> map = new HashMap<>();
          for (int i = 0; i < nums.length; i++)
              map.put(nums[i], i);
          for (int i = 0; i < nums.length - 2; i++) {
              if (i > 0 && nums[i] == nums[i - 1]) continue;//去重
              for (int j = i + 1; j < nums.length - 1; j++) {
                  if (j > i + 1 && nums[j] == nums[j - 1]) continue;//去重
                  for (int k = j + 1; k < nums.length; k++) {
                      if (k > j + 1 && nums[k] == nums[k - 1]) continue;//去重
                      int a = nums[i];
                      int b = nums[j];
                      int c = nums[k];
                      int d = target - a - b - c;
                      if (map.containsKey(d) && map.get(d) > k) {//向后查找
                          lists.add(new ArrayList<Integer>() {{
                              add(a);
                              add(b);
                              add(c);
                              add(d);
                          }});
                      } else continue;
                  }
              }
          }
          return lists;
      }
      
    2. 利用数据特点,双指针收缩窗口

      时间复杂度:(O(n^3)),三重循环(O(n^3))

      空间复杂度:(O(n))

      public List<List<Integer>> fourSum(int[] nums, int target) {
          List<List<Integer>> lists = new ArrayList<>();
          if (nums.length < 4) return lists;
          Arrays.sort(nums);
          for (int a = 0; a < nums.length - 3; a++) {
              if (a > 0 && nums[a] == nums[a - 1]) continue;//去重
              for (int b = a + 1; b < nums.length - 2; b++) {
                  if (b > a + 1 && nums[b] == nums[b - 1]) continue;//去重
                  int c = b + 1, d = nums.length - 1;
                  while (c < d) {
                      int sum = nums[a] + nums[b] + nums[c] + nums[d];
                      if (sum < target) c++;
                      else if (sum > target) d--;
                      else {
                          List<Integer> list = new ArrayList<>();
                          list.add(nums[a]);
                          list.add(nums[b]);
                          list.add(nums[c]);
                          list.add(nums[d]);
                          lists.add(list);
                          while (c < d && nums[c] == nums[c + 1]) c++;//去重
                          while (c < d && nums[d] == nums[d - 1]) d--;//去重
                          c++;
                          d--;
                      }
                  }
              }
          }
          return lists;
      }
      
  • 相关阅读:
    Codeforces 1255B Fridge Lockers
    Codeforces 1255A Changing Volume
    Codeforces 1255A Changing Volume
    leetcode 112. 路径总和
    leetcode 129. 求根到叶子节点数字之和
    leetcode 404. 左叶子之和
    leetcode 104. 二叉树的最大深度
    leetcode 235. 二叉搜索树的最近公共祖先
    450. Delete Node in a BST
    树的c++实现--建立一棵树
  • 原文地址:https://www.cnblogs.com/pgjett/p/12389862.html
Copyright © 2011-2022 走看看