算法题打卡力扣第15题:三数之和(mid)

题目描述


解法一:暴力解

用三个for循坏枚举出所有等于的三元组然后再排序筛选出不同的三元组

代码

cpp 复制代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        std::set<std::vector<int>> resultSet;
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                for(int k=j+1;k<n;k++){
                    if(nums[i]+nums[j]+nums[k]==0){
                        std::vector<int> triplet = {nums[i],nums[j],nums[k]};
                        std::sort(triplet.begin(),triplet.end());
                        resultSet.insert(triplet);
                    }
                }
            }
        }
        std::vector<std::vector<int>> finalResult(resultSet.begin(),resultSet.end());
        return finalResult;
    }
};

执行结果:

超出时间限制了。

复杂性分析:

时间复杂度分析:O(n^3)

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

解法二:排序+降维+双指针

a + b + c = 0 等价于 b + c = -a 如果我们能固定一个数 a (即numsi),那么问题就转化成了:在数组剩下的部分中,寻找两个数,它们的和等于 -a。 这就变成了我们非常熟悉的 "两数之和" (Two Sum) 问题!

具体步骤:

  • 排序:首先,对整个 nums 数组进行升序排序。这一步至关重要,它是双指针法和去重逻辑的基础。时间复杂度 O(n log n)。
  • 主循环 (固定 a):使用一个 for 循环遍历排序后的数组,i 从 0 到 n-3。numsi 就是我们固定的那个数 a。
    优化/剪枝:如果 numsi > 0,由于数组已经排序,后面的 numsleft 和 numsright 也必然大于 0,三者之和不可能等于 0。所以,一旦 numsi > 0,就可以直接 break 循环了。
    去重 a:这是第一个关键的去重逻辑。如果当前的 numsi 和上一个 numsi-1 相同,那么以 numsi 为基础找到的三元组,必然和以 numsi-1 为基础的完全一样。为了避免重复,我们需要跳过这种情况。代码实现:if (i > 0 && numsi == numsi-1) { continue; }
  • 双指针 (寻找 b 和 c):对于每一个 i,我们在它右边的区间 i+1, n-1 内使用双指针。
    初始化左指针 left = i + 1。
    初始化右指针 right = n - 1。
    计算目标和 target = -numsi
  • 移动双指针:进入一个 while (left < right) 循环。
    计算当前和 sum = numsleft + numsright
    计算当前和 sum = nums left+ nums right

情况一:sum == target

我们找到了一个解!将 (numsi, numsleft, numsright) 添加到结果列表中。

去重 b 和 c:这是第二个关键的去重逻辑。在我们找到一组解后,需要同时移动 left 和 right 指针。但如果 numsleft 的下一个元素和它自己相同,或者 numsright 的前一个元素和它自己相同,就会产生重复解。所以,我们需要跳过所有与当前 numsleft 和 numsright 相同的元素。

代码实现:

while (left < right && numsleft == numsleft+1) left++;

while (left < right && numsright == numsright-1) right--;

跳过重复项后,left 和 right 还需要再各前进一步,去寻找下一组不同的解:left++; right--;

情况二:sum < target

和太小了。为了让 sum 变大,我们需要一个更大的数。所以,左指针 left 向右移动:left++。

情况三:sum > target

情况三:sum < 目标

和太大了。为了让 sum 变小,我们需要一个更小的数。所以,右指针 right 向左移动:right--。

  • 循环结束后,返回结果列表。
    g老师
    代码实现:
cpp 复制代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
                if (second == third) {
                    break;
                }
                if (nums[second] + nums[third] == target) {
                    ans.push_back({nums[first], nums[second], nums[third]});
                }
            }
        }
        return ans;
    }
};

复杂度分析

时间O(n^2)

空间O(n)

相关推荐
GUO_PP11 分钟前
win11英雄联盟打开以后,自动改变音效,开启免提模式的问题修正
人工智能·算法
变量未定义~23 分钟前
排列数字、 n-皇后问题
数据结构·算法
我命由我1234531 分钟前
Excel - Excel 单元格格式同时设置日期时间
运维·学习·职场和发展·excel·求职招聘·职场发展·学习方法
BirdenT38 分钟前
20260604紫题训练
c++·算法
元启数宇39 分钟前
疏散指示AI实战:规范布点与路径推演全流程
人工智能·算法
tg:;42 分钟前
Catkin 常用命令
开发语言·c++·算法
暖阳华笺1 小时前
【高频考点】回溯(暴力搜索)
数据结构·c++·算法·回溯法
hunterkkk(c++)1 小时前
学习dijkstra算法(c++)
c++·学习·算法
我命由我123451 小时前
Excel - Excel 覆盖模式与编辑模式
运维·学习·职场和发展·excel·求职招聘·职场发展·运维开发
lightqjx1 小时前
【算法】数据结构_单调队列
数据结构·算法·单调队列