LeetCode 三数之和的多种可能(双指针)

给定一个整数数组 A,以及一个整数 target 作为目标值,返回满足 i < j < k 且 A[i] + A[j] + A[k] == target 的元组 i, j, k 的数量。

由于结果会非常大,请返回 结果除以 10^9 + 7 的余数。

示例 1:

输入:A = [1,1,2,2,3,3,4,4,5,5], target = 8
输出:20
解释:
按值枚举(A[i],A[j],A[k]):
(1, 2, 5) 出现 8 次;
(1, 3, 4) 出现 8 次;
(2, 2, 4) 出现 2 次;
(2, 3, 3) 出现 2 次。

示例 2:

输入:A = [1,1,2,2,2,2], target = 5
输出:12
解释:
A[i] = 1,A[j] = A[k] = 2 出现 12 次:
我们从 [1,1] 中选择一个 1,有 2 种情况,
从 [2,2,2,2] 中选出两个 2,有 6 种情况。

提示:

3 <= A.length <= 3000
0 <= A[i] <= 100
0 <= target <= 300

思 路 分 析 : \color{blue}思路分析: 这种类似排列组合的问题肯定会有很多道友直接上蛮力法,三层循环,时间复杂度在O(n3)级别,然而这种题一般测试数据量都比较大,所以还是尽量避开用蛮力法。
前面有一系列的“两数之和”、“三数之和”等等这种题目 LeetCode 两数之和系列,这道题也是类似的处理方法。

为了降低复杂度,我们首先对数组进行排序,排序之后我们使用thirdPtr指针作为三个数中最大的数,然后使用firstPtr、secondPtr指针在thirdPtr的左边扫描剩下的两个数,并且保持firstPtr < secondPtr,这样满足firstPtr < secondPtr < thirdPtr,防止重复计算。

class Solution {
public:
    int threeSumMulti(vector<int>& A, int target) {
        int resCount = 0, aSize = A.size();
        sort(A.begin(), A.end());//升序排序
        //hashMap[num]记录num在A数组中出现的次数,firstIndex[num]记录num在A(排序后)中第一次出现的下标,lastIndex[num]记录num在A(排序后)中最后一次次出现的下标
        vector<int> hashMap(101, 0), firstIndex(101, 0), lastIndex(101, 0);
        for (int i = 0; i < aSize; ++i){
            hashMap[A[i]] += 1;//次数自增
            lastIndex[A[i]] = i;//更新A[i]最后一次出现的下标
        }
        for (int i = aSize - 1; i >= 0; --i){
            firstIndex[A[i]] = i;//更新A[i]第一次出现的下标
        }
        //thirdPtr从后往前扫描,固定三个数的右边界
        for (int thirdPtr = aSize - 1; thirdPtr > 0; --thirdPtr){
            hashMap[A[thirdPtr]] -= 1;//当secondPtr指向的值与thirdPtr指向的值相同时,这时修正A[thirdPtr]的次数
            int firstPtr = 0, secondPtr = thirdPtr - 1, tempCount = 0, tempTarget = target - A[thirdPtr];//tempCount记录以thirdPtr为左边界的情况数
            while (firstPtr < secondPtr){//firstPtr、secondPtr双指针
                if (A[firstPtr] + A[secondPtr] < tempTarget){
                    firstPtr = lastIndex[A[firstPtr]] + 1;//右移firstPtr,增大A[firstPtr] + A[secondPtr]的和
                }
                else if (A[firstPtr] + A[secondPtr] == tempTarget){
                    if (A[firstPtr] == A[secondPtr]){//当A[firstPtr] 、A[secondPtr]相等,这时需要特别注意重复计算,保持firstPtr小于secondPtr,根据排列组合计算公式
                        tempCount += hashMap[A[firstPtr]] * (hashMap[A[firstPtr]] - 1) / 2;
                        break;
                    }
                    else {//当A[firstPtr]、A[secondPtr]不相等,组合情况为两个数次数乘积
                        tempCount += hashMap[A[firstPtr]] * hashMap[A[secondPtr]];
                        firstPtr = lastIndex[A[firstPtr]] + 1;//更新firstPtr 
                        secondPtr = firstIndex[A[secondPtr]] - 1;//更新secondPtr 
                    }
                }
                else {//左移secondPtr,减小A[firstPtr] + A[secondPtr]的和
                    secondPtr = firstIndex[A[secondPtr]] - 1;
                }
            }
            resCount = (resCount + tempCount) % 1000000007;
        }
        return resCount;
    }
};

LeetCode 三数之和的多种可能(双指针)_第1张图片

你可能感兴趣的:(LeetCode,数组,双指针)