【LeetCode】 15.三数之和

本文详解LeetCode第15题‘三数之和’的两种解法:暴力解法(时间复杂度O(n³))与双指针优化解法(时间复杂度O(n²))。核心步骤包括数组排序、去重处理及利用双指针在有序子数组中查找两数之和为-target的组合。重点突出排序、双指针、去重等关键算法思想。

一、题目信息

  • https://leetcode.cn/problems/3sum/description/

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i !=
j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0
。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

示例 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 。

二、题解1:暴力解法

1、解题思路

  • 输入一个整数数组,要求从数组中找出三个元素,最终三个元素的和为0,这样的三个元素存在多个,返回他们组成的数组

  • 用三层for循环,让每一个都能与其他两个元素进行相加,并求最后三个元素的和是否等于0

  • 需要过滤相同元素数组中的值,所以先要对整数数组进行排序,然后再遍历的时候需要过滤掉相同的元素值

2、代码实现

vector<vector<int>> threeSum1(vector<int> &nums) {
  vector<vector<int>> res;
  int size = nums.size();
  // 为数组nums排序
  std::sort(nums.begin(), nums.end());

  for (int i = 0; i < size; i++) {
    if (i > 0 && nums[i] == nums[i - 1]) {
      continue;
    }
    for (int j = i + 1; j < size; j++) {
      if (j > i + 1 && nums[j] == nums[j - 1]) {
        continue;
      }
      for (int k = j + 1; k < size; k++) {
        if (k > j + 1 && nums[k] == nums[k - 1]) {
          continue;
        }
        if (nums[i] + nums[j] + nums[k] == 0) {
          res.push_back({nums[i], nums[j], nums[k]});
        }
      }
    }
  }

  return res;
}

3、时间复杂度

  • 三层for循环,时间复杂度为n的三次方 n^3

三、题解2:双指针解法

1、解题思路

  • 该题是之前两数之和的升级版,先对数组进行排序,从数组中找出下标分别为i,j,k,并且他们的顺序是递增的

  • 先从i开始,i元素固定,然后在后面的[i+1,n-1]范围内找出两个元素的和等于-nums[i]的大小,并且该范围的数组已经是递增的了,这个操作和之前的求两数之和的操作一样的了

    • 可以采用双指针解法来实现

  • 接下来就是过滤重复子数组的情况,需要在排序后的数组中过滤掉后一位元素与当前元素值相同的元素即可

2、代码实现

vector<vector<int>> threeSum(vector<int> &nums) {
  vector<vector<int>> res;
  int size = nums.size();
  // 为数组nums排序
  std::sort(nums.begin(), nums.end());

  int i = 0;
  int j = 0;
  int k = 0;
  while (i < size) {
    while (i > 0 && (i < size) &&
           (nums[i] == nums[i - 1])) { // 过滤与前一个相同的元素
      i++;
    }
    if (i >= size) {
      break;
    }

    // 从 i+1到 size-1范围寻找两个元素位置j,k,使得他们的和等于 -nums[i] 的大小,
    j = i + 1;
    k = size - 1;
    int target = -(nums[i]);
    while (j < k) {
      int sum = nums[j] + nums[k];
      if (sum == target) {
        res.push_back({nums[i], nums[j], nums[k]});
        j++;
        while (j < size && (nums[j] == nums[j - 1])) { // 过滤与前一个相同的元素
          j++;
        }
        k--;
      } else if (target > sum) {
        j++;
      } else {
        k--;
      }
    }
    i++;
  }
  return res;
}

3、时间复杂度

  • n^2

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值