Bootstrap

当你初学C++时VS大佬眼中的C++

当你初学C++时VS大佬眼中的C++

问题描述

给定一个包含 n 个整数的数组 S,判断是否存在三个元素 a, b, c,使得 a + b + c = 0?找出所有满足条件的三元组,且这些三元组中的元素必须按非降序排列(即 a ≤ b ≤ c),并且解集中不能包含重复的三元组。

示例:

输入:S = [-10, 0, 10, 20, -10, -40]
输出:[[-10, -10, 20], [-10, 0, 10]]

初学时:双重遍历,先求两个数字之和,第三个数字缺什么找什么。我记得有个find函数。找到了!
什么还要排序?创建一个新的函数,我真聪明! // 其实到这一步我已经后悔了,我想重头再来的
什么还不能重复,b事真多!
不应该啊
怎么会不全呢
原来如此
再加两个函数
为什么显示栈溢出呢
逻辑上没问题啊
难道是这个地方出问题了
再来个函数
快晕了,不过我竟然能写这么多函数还能调用成功,真厉害
快了,快了,运行到第11/20个案例了
怎么会呢,明明可以运行
哪个啥鸟函数给我把数组顺序打乱了
终于
<-<-<-
两小时后~
<-<-<-
终于
终于成功了

大佬眼中:
排序,循环枚举,指针查重,秒了。(三分钟)


解题思路

1. 暴力解法

最直观的解法是使用三重循环枚举所有可能的三元组,然后判断它们的和是否为零。这种方法的时间复杂度为 O(n^3),在 n 较大时(如 n = 1000)会非常慢,无法在合理时间内得到结果。

2. 排序 + 双指针优化

为了优化时间复杂度,我们可以先对数组进行排序,然后使用双指针技巧来减少不必要的枚举。

步骤如下:

  1. 排序:首先对数组进行排序,这样我们可以利用有序性来减少枚举的次数。
  2. 枚举第一个数:使用一个循环枚举第一个数 num[i],然后在剩下的数组中使用双指针寻找另外两个数 num[l]num[r],使得 num[i] + num[l] + num[r] = 0
  3. 双指针:初始化左指针 l = i + 1,右指针 r = n - 1。根据当前三数之和与零的关系,移动指针:
    • 如果和大于零,说明右指针的数太大,需要将右指针左移。
    • 如果和小于零,说明左指针的数太小,需要将左指针右移。
    • 如果和等于零,找到一个有效的三元组,记录下来,并移动指针以避免重复。
  4. 去重:在枚举第一个数和移动指针时,跳过重复的元素,以避免重复的三元组。

时间复杂度:排序的时间复杂度为 O(n log n),双指针部分的时间复杂度为 O(n^2),因此总的时间复杂度为 O(n^2)

空间复杂度:除了存储结果的数组外,只使用了常数级别的额外空间,因此空间复杂度为 O(1)

代码实现

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& num) {
        vector<vector<int>> ans;
        sort(num.begin(), num.end());  // 先对数组进行排序
        for (int i = 0; i + 2 < num.size(); i++) {
            int l = i + 1, r = num.size() - 1;
            while (l < r) {
                while (l < r && num[i] + num[l] + num[r] > 0) r--;  // 和大于零,右指针左移
                if (l == r) break;  // 指针相遇,退出循环
                if (num[i] + num[l] + num[r] == 0) {
                    ans.push_back(vector<int>{num[i], num[l], num[r]});  // 找到一个有效的三元组
                    while (l < r && num[l + 1] == num[l]) l++;  // 跳过重复的左指针元素
                }
                l++;  // 左指针右移
            }
            while (i + 1 < num.size() && num[i + 1] == num[i]) i++;  // 跳过重复的第一个数
        }
        return ans;
    }
};

代码解析

  1. 排序:首先对数组进行排序,使得我们可以利用有序性来减少枚举的次数。
  2. 枚举第一个数:使用 for 循环枚举第一个数 num[i],并在循环中跳过重复的元素。
  3. 双指针:在每次枚举第一个数后,初始化左指针 l = i + 1 和右指针 r = num.size() - 1,然后根据三数之和与零的关系移动指针。
  4. 去重:在找到有效的三元组后,跳过重复的左指针元素,并在枚举第一个数时跳过重复的元素。

附上铁头代码——主打我就不信行不通

#include <algorithm>
#include <cstdio>
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param num int整型vector 
     * @return int整型vector<vector<>>
     */
    vector<vector<int> > threeSum(vector<int>& num) {
        // write code here
        int n = num.size();
        int m=0;
        vector<vector<int> > result;
        for(int i=0; i<n; i++){
            for(int j=i+1; j<n; j++){
                int obj = 0 - num[i] -num[j];
                int z = findNumber(num, obj,j);
                if(obj ==4){
                    cout << num[i] << num[j] <<num[z] <<endl;
                }
                
                if(z > 0 ){
                    
                    bool AAA = moto(result,num[i],num[j],num[z]);
                    int opc =num[i];  // 此时已经麻木了
                    int ua =num[j];
                    int can =num[z];
                    sortThreeDescending(opc,ua,can);
                    // cout << num[i] << num[j] <<num[z] <<endl;
                    if(!AAA){
                        
                        int pos = position(result, opc,ua,can);
                        // cout << pos << num[i] << num[j] <<num[z] <<endl;
                        auto p = result.begin() + pos;
                        result.insert(p,{opc,ua,can});
                    }
                    
                }

            }
        }
        return result;
    }

    int findNumber(const std::vector<int>& arr, int target, int j) {
        for (int i = 0; i < arr.size(); i++) {
            if (arr[i] == target && i > j) {
                return i; // 返回目标值的位置
            }
        }
        return -1; // 如果未找到,返回 -1
    }

    bool moto(vector<vector<int> >& result, int i, int j, int z) {
        int m = result.size();
        for (int a = 0; a < m; a++) {
            // 检查当前三元组是否包含i, j, z
            bool contains_i = false, contains_j = false, contains_z = false;
            for (int b = 0; b < 3; b++) {
                if (result[a][b] == i && contains_i == false) {contains_i = true;}
                else if (result[a][b] == j && contains_j == false) {contains_j = true;}
                else if (result[a][b] == z && contains_z == false) {contains_z = true;}
            }
            // 如果三个数都在当前三元组中,返回true
            if (contains_i && contains_j && contains_z) {
                return true;
            }
        }
        // 未找到匹配的三元组,返回false
        return false;
    }

    void sortThreeDescending(int& a, int& b, int& c) {
        if (a > b) std::swap(a, b);
        if (a > c) std::swap(a, c);
        if (b > c) std::swap(b, c);
    }

    int position(vector<vector<int> >& result, int i, int j, int z){
        int m = result.size();
        int pos = 0;
        for (int a = 0; a < m; a++){
            if( i > result[a][0] ){
                pos = a+1;
           }
            else if(i == result[a][0])
            {
                if( j > result[a][1] ){
                    pos = a+1;
                    
                }
                else if( j  == result[a][1]){
                    if(z > result[a][2]){ pos = a+1;  }
                    else{ return a;  }
                }
                else{
                    return a;
                }   
            }
            else{
                return pos;
            }
            
        }

        return pos;

        if(m ==0){
            return 0;
        }
        
    }
};

我就说能行!!!!

图片名称

点个赞吧

;