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]
]

解答:

自己解答代码:

vector<vector<int>> fourSum(vector<int>& nums, int target) 
{
    vector<vector<int>> result;
    sort(nums.begin(), nums.begin() + nums.size());

    auto isExist = [&](vector<int>& vec)
    {
        for (auto val : result)
        {
            if (val.at(0) != vec.at(0))
                continue;
            if (val.at(1) != vec.at(1))
                continue;
            if (val.at(2) != vec.at(2))
                continue;
            if (val.at(3) != vec.at(3))
                continue;

            return true;
        }
        return false;
    };

    int idx1 = 0, idx2;
    for (int i = 0; i < nums.size(); i++)
    {
        if (i > 0 && nums.at(i) == nums.at(i - 1))//跟上一个重复,不处理
            continue;

        for (int j = i+1; j < nums.size(); j++)
        {
            idx1 = j + 1;
            idx2 = nums.size() - 1;
            while (idx1 < idx2)
            {
                if (idx1 > j + 1 && nums.at(idx1) == nums.at(idx1 - 1))
                {
                    idx1++;
                    continue;
                }

                int val = nums.at(i) + nums.at(j) + nums.at(idx1) + nums.at(idx2);
                if (val < target)
                {
                    idx1++;
                }
                else if (val > target)
                {
                    idx2--;
                }
                else
                {
                    vector<int> vec = { nums.at(i), nums.at(j), nums.at(idx1), nums.at(idx2) };
                    bool has = isExist(vec);
                    if (!has)
                        result.push_back({ nums.at(i), nums.at(j), nums.at(idx1), nums.at(idx2) });
                    
                    idx1++;
                    idx2--;
                }
            }
        }
    }
    return result;
}

问题:1.去重部分太复杂,使用lambda表达式每次遍历一遍是否重复,太耗时

参考三数之和的去重,解答如下:

vector<vector<int>> fourSum2(vector<int>& nums, int target)
{
    vector<vector<int>> result;
    sort(nums.begin(), nums.begin() + nums.size());

    int idx1 = 0, idx2;
    for (int i = 0; i < nums.size(); i++)
    {
        if (i > 0 && nums.at(i) == nums.at(i - 1))//只有上一个是找到的才跳过,跟上一个重复,不处理
            continue;

        for (int j = i + 1; j < nums.size(); j++)
        {
            if (j > i + 1 && nums.at(j) == nums.at(j - 1))
            {
                continue;//这里的j不用j++了,因为continue后j就自动++了
            }

            idx1 = j + 1;
            idx2 = nums.size() - 1;
            while (idx1 < idx2)
            {
                if (idx1 > j + 1 && nums.at(idx1) == nums.at(idx1 - 1))
                {
                    idx1++;
                    continue;
                }

                int val = nums.at(i) + nums.at(j) + nums.at(idx1) + nums.at(idx2);
                if (val < target)
                {
                    idx1++;
                }
                else if (val > target)
                {
                    idx2--;
                }
                else
                {
                    result.push_back({ nums.at(i), nums.at(j), nums.at(idx1), nums.at(idx2) });

                    idx1++;
                    idx2--;
                }
            }
        }
    }
    return result;
}

问题:整个代码的主体流程与解答一致,但是运行速度较差,比较了解答,发现有许多判断条件用于减少循环次数,修改代码如下:

vector<vector<int>> fourSum3(vector<int>& nums, int target)
{
    vector<vector<int>> result;
    if (nums.size() < 4)    //如果输入小于4个则直接返回空vec
        return result;

    sort(nums.begin(), nums.begin() + nums.size());

    int idx1 = 0, idx2;
    for (int i = 0; i < nums.size(); i++)
    {
        if (i > 0 && nums.at(i) == nums.at(i - 1))//只有上一个是找到的才跳过,跟上一个重复,不处理
            continue;

        //在这里判断是否有继续执行的必要,即i i+1 i+2 i+3之和大于target则退出循环
        if (i + 3 < nums.size() && (nums.at(i) + nums.at(i + 1) + nums.at(i + 2) + nums.at(i + 3) > target))
            break;

        for (int j = i + 1; j < nums.size(); j++)
        {
            if (j > i + 1 && nums.at(j) == nums.at(j - 1))
            {
                continue;//这里的j不用j++了,因为continue后j就自动++了
            }

            //i加上j j+1 j+2如果大于target则直接break
            if ((j+2)<nums.size() && nums.at(i) + nums.at(j) + nums.at(j + 1) + nums.at(j + 2) > target)
                break;

            idx1 = j + 1;
            idx2 = nums.size() - 1;
            while (idx1 < idx2)
            {
                int val = nums.at(i) + nums.at(j) + nums.at(idx1) + nums.at(idx2);
                if (val < target)
                {
                    idx1++;
                }
                else if (val > target)
                {
                    idx2--;
                }
                else
                {
                    result.push_back({ nums.at(i), nums.at(j), nums.at(idx1), nums.at(idx2) });

                    while (idx1 < idx2 && nums.at(idx1) == nums.at(idx1 + 1))
                    {
                        idx1++;
                    }
                    while (idx1 < idx2 && nums.at(idx2) == nums.at(idx2 - 1))
                    {
                        idx2--;
                    }
                    idx1++;
                    idx2--;
                }
            }
        }
    }
    return result;
}

总结:

1.同样的算法,一些预处理、判断条件的不同,最终运行速度也会差的很大;

2.在循环中对i j++时要注意,因为for每次循环后都会自动++,而while需要手动++,注意针对不同情况的写法,很容易引入bug。

posted @ 2020-11-16 10:05  adfas  阅读(72)  评论(0编辑  收藏  举报