算法题打卡力扣第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)

相关推荐
05Kevin2 小时前
lk每日冒险题--数据结构6.27
算法
To_OC13 小时前
从一次栈溢出报错说起,我把递归彻底扒明白了
javascript·算法·程序员
千纸鹤安安18 小时前
千问Qwen-AgentWorld来了:一个语言模型搞定七大Agent场景,GPT-5.4都输了
算法
七牛开发者20 小时前
MCP 到底是什么?为什么 Agent 都想接上它
算法·aigc·agent
kisshyshy1 天前
从递归到迭代,一文吃透二叉树的核心知识与 JavaScript 实现
javascript·算法·代码规范
To_OC2 天前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
用户938515635072 天前
从 O(n²) 到 O(nlogn):一文读懂快速排序的“快”与“妙”
javascript·算法