[leetcode]求和類問題

Two sum i

Given an array of integers, return indices of the two numbers such that they add up to a specific target. 
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

Two sum系列算是leetcode經典題型了,哈希表的經典使用。O(N)

vector<int> twoSum(vector<int>& nums, int target) 
{
    unordered_map<int, int> m;
    for (int i = 0; i < nums.size(); ++i)
        if (m.count(nums[i]))
            return vector<int>({ m.find(nums[i])->second, i });
        else
            m[target - nums[i]] = i;
    return vector<int>();
}

Two Sum ii

Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number. 
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution and you may not use the same element twice.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

雖然這題的tag裏有二分法,但貌似還沒有O(log(N)) 的解法。這題思路是使用雙指針,首尾相加, O(N)

vector<int> twoSum(vector<int>& numbers, int target)
{
    int left = 0, right = numbers.size() - 1, sum;
    while (left < right)
    {
        sum = numbers[left] + numbers[right];
        if (sum == target)
            return vector<int>({ left + 1, right + 1 });
        else if (sum > target)
            --right;
        else
            ++left;
    }
    return vector<int>({ -1, -1 });
}

Two Sum iii

Design and implement a TwoSum class. It should support the following operations:add and find. 
add - Add the number to an internal data structure.
find - Find if there exists any pair of numbers which sum is equal to the value.
For example,
add(1); add(3); add(5);
find(4) -> true
find(7) -> false

其實跟Tow Sum i差不多的思路,只不過這個題目中有特殊情況:一個數字可能出現多次。
add O(1) , findO(N)

class TwoSum 
{
    unordered_map<int, int> map;
public:
    void add(int number) 
    {
        map[number]++;
    }
    bool find(int value) 
    {
        for (auto &p : map)
        {
            int i = p.first;
            int j = value - i;
            if ((i == j && p.second > 1) || (i != j && map.count(j)))
                return true;
        }
        return false;
    }
};

3Sum

Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero. 
Note: The solution set must not contain duplicate triplets.
For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]

The same as find an element a in S that existing two elements b and c that b + c = - a. First sort the array, then for each elements nums[i], find if there are two elements in range [i + 1, i + 2,…, n - 1] that nums[j] + nums[k] = - nums[i]. So this problem could reduce to Two Sum ii.
Complexity: O(N2) .

vector<vector<int>> threeSum(vector<int>& nums) {
    sort(nums.begin(), nums.end());
    vector<vector<int>> ret;
    int N = nums.size();
    for (int i = 0; i < N - 2; ++i)
    {
        // skip duplicates
        if (i > 0 && nums[i] == nums[i - 1])
            continue;
        int sum = -nums[i];

        int left = i + 1, right = nums.size() - 1;
        while (left < right)
        {
            int target = nums[left] + nums[right];
            if (target < sum)
                ++left;
            else if (target > sum)
                --right;
            else
            {
                ret.push_back(vector<int>({ nums[i], nums[left], nums[right] }));
                // skip duplicates
                for (left = left + 1; left < right && nums[left] == nums[left - 1]; ++left);
                for (right = right - 1; left < right && nums[right] == nums[right + 1]; --right);
            }
        }
    }
    return ret;
}

3Sum-closest

Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.

    For example, given array S = {-1 2 1 -4}, and target = 1.

    The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

Same as 3sum, just keep record of every minimum gap. O(N2)

int threeSumClosest(vector<int>& nums, int target)
{
    int gap = INT_MAX;
    int ret;
    sort(nums.begin(), nums.end());
    int N = nums.size();
    for (int i = 0; i < N - 2; ++i)
    {
        if (i > 0 && nums[i] == nums[i - 1])
            continue;
        int left = i + 1, right = nums.size() - 1;
        while (left < right)
        {
            int sum = nums[i] + nums[left] + nums[right];
            if (sum == target)
                return target;
            else if (sum < target)
                ++left;
            else
                --right;
            if (abs(target - sum) < gap)
            {
                gap = abs(target - sum);
                ret = sum;
            }
        }
    }
    return ret;
}

4sum

Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
Note: The solution set must not contain duplicate quadruplets.
For example, given array S = [1, 0, -1, 0, -2, 2], and target = 0.
A solution set is:
[
  [-1,  0, 0, 1],
  [-2, -1, 1, 2],
  [-2,  0, 0, 2]
]

This problem could be reduced to 3sum. Just add an extra loop for the extra variable. O(N3)

vector<vector<int>> fourSum(vector<int>& nums, int target) {
    sort(nums.begin(), nums.end());
    vector<vector<int>> ret;
    int n = nums.size();
    for (int i = 0; i < n - 3; ++i)
    {
        if (i > 0 && nums[i] == nums[i - 1])
            continue;
        for (int j = i + 1; j < n - 2; ++j)
        {
            if (j > i + 1 && nums[j] == nums[j - 1])
                continue;
            int t1 = target - nums[i] - nums[j];
            // if the smallest two number's sum is still larger, just try next i
            if (nums[j + 1] + nums[j + 2] > t1)
                break;
            // if the largest two number's sum is still smaller, just try next j.
            if (nums[n - 1] + nums[n - 2] < t1)
                continue;
            int left = j + 1, right = nums.size() - 1;
            while (left < right)
            {
                int sum = nums[left] + nums[right];
                if (sum == t1)
                {
                    ret.push_back(vector<int>({ nums[i], nums[j], nums[left], nums[right] }));
                    // skip the duplicates
                    for (left = left + 1; left < right && nums[left] == nums[left - 1]; ++left);
                    for (right = right - 1; left < right && nums[right] == nums[right + 1]; --right);
                }
                else if (sum < t1)
                    ++left;
                else
                    --right;
            }
        }
    }
    return ret;
}

The trimming here is important to reduce the running time. Comments are added.

4Sum II

Given four lists A, B, C, D of integer values, compute how many tuples (i, j, k, l) there are such that A[i] + B[j] + C[k] + D[l] is zero.
To make problem a bit easier, all A, B, C, D have same length of N where 0 ≤ N ≤ 500. All integers are in the range of -228 to 228 - 1 and the result is guaranteed to be at most 231 - 1.
Example:
Input:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
Output:
2
Explanation:
The two tuples are:
1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

Idea: use a hashmap to store all combinations of Ai+Bj , then for each combination of sum=Ck+Dh , find if sum exists in the previous built hash-table. O(N2)

int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) 
{
    int N = A.size();
    unordered_map<int, int> sumMap;
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            ++sumMap[A[i] + B[j]];
    int res = 0;
    for (int i = 0; i < N; ++i)
        for (int j = 0; j < N; ++j)
            if (sumMap.count(0 - C[i] - D[j]))
                res += sumMap[0 - C[i] - D[j]];
    return res;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章