본문 바로가기
This is my cute cat.

Jaehee

Hi!

Thumbnail of LeetCode - 18. 4Sum

LeetCode - 18. 4Sum

시리즈: LeetCode

작성일 수정일

목차

문제 개요

난이도 - MEDIUM 사용 언어 - C++

지금까지 Two Sum, 3 Sum과 같은 문제를 풀었습니다.

이번에는 4 Sum입니다. 숫자 배열이 주어지면 숫자 네 개를 더했을 때 target 숫자가 나타나는 모든 가짓수를 찾는 문제입니다.

Two Sum은 Hash table 방식을 이용해 풀었고, 3 Sum은 Two pointer 방식을 이용해 풀었습니다. Two pointer 방식은 left 인덱스와 right 인덱스를 이용해 target 숫자에 가까워지도록 두 포인터를 조정하는 방식이었습니다.

3 Sum에서 Two pointer를 활용할 때는 숫자 하나를 고정해두고 left, right 포인터를 조정하는 방식으로 풀었습니다. 즉, 숫자 하나를 고정하고 Two Sum을 수행한 것과 동일합니다.

즉 4 Sum도 숫자 하나를 고정해두고 3 Sum을 수행하면 4 Sum의 결과를 얻을 수 있습니다.

LeetCode의 Solution과 동일하게, 이번에는 4 Sum만 딱 정해서 푸는 것이 아니라 k-Sum에 대해 문제를 풀어보았습니다.

문제 - LeetCode - 18. 4Sum

풀이

My Solutions(Github)

Solution - kSum

이번에 풀어볼 방법은 3 Sum과 동일하게 Two pointer, 즉 left, right 포인터를 target에 가까운 숫자가 되도록 좁혀가는 방식입니다.

Two pointer 방법이 가능한 이유는, 주어진 숫자 배열이 정렬되어 있어 left 포인터가 항상 right 포인터가 가리키는 숫자보다 작거나 같기 때문입니다. 그래서 이 방법을 사용할 때는 반드시 숫자 배열을 먼저 정렬해야 합니다.

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

    return kSum(nums, target, 0, 4);
}

vector<vector<int>> kSum(vector<int>& nums, int target, int start, int k)
{
    ...
}

그리고 kSum으로 일반화한 함수를 k = 4인 경우로 호출합니다.

앞서 설명했듯이 3 Sum은 숫자 하나를 고정하고 나머지 숫자들로 2 Sum을 수행하는 방식으로 설명할 수 있습니다.

동일하게 4 Sum 또한 숫자 하나를 고정하고 3 Sum을 호출하는 방법으로 결과를 구할 수 있습니다.

for (int i = start; i < nums.size(); i++)
{
    if (i != start && nums[i - 1] == nums[i]) continue;

    auto subsets = kSum(nums, target - nums[i], i + 1, k -1);

    for (auto subset : subsets)
    {
        subset.push_back(nums[i]);

        result.push_back(subset);
    }
}

k - 1 Sum을 구하는 방법은 매우 간단합니다.

수 하나(여기선 nums[i])를 고정하고, 고정한 수를 제외하고 k - 1 Sum을 호출합니다. 즉, 재귀적으로 k Sum이 이루어지게 됩니다.

if (i != start && nums[i - 1] == nums[i]) continue;

위 if문은 이전에 고정한 숫자와 현재 고정할 숫자가 같으면 건너뜁니다. 이전과 똑같은 숫자를 고정해 탐색하면 nums[i - 1]에 대한 subset을 다시 탐색하는 것이기 때문입니다. 자세한 설명은 3 Sum을 참조하시면 좋습니다.

for (auto subset : subsets)
{
    subset.push_back(nums[i]);

    result.push_back(subset);
}

k - 1 Sum으로 반환된 결과에 현재 고정한 숫자를 합쳐서 결과에 반영합니다.

만약 k=2가 된다면 twoSum을 호출하는 게 맞겠죠.

if (k == 2)
{
    return twoSum(nums, target, start);
}

twoSum 함수는 여기서 따로 설명하지 않겠습니다. 3 Sum 게시글을 참고하시거나, 게시글 하단의 코드 전문을 보시면 확인할 수 있습니다.

이게 끝입니다. 지금까지 수행한 과정은 다음과 같습니다.

  1. 입력 배열을 정렬한다.
  2. kSum을 호출한다. (여기선 k=4)
  3. k-1 Sum을 재귀적으로 호출한다.
  4. k = 2일 경우 Two Sum을 호출한다.
  5. Two Sum으로 반환된 결과에 고정된 숫자를 합쳐 반환한다.

제출 결과

Solution 1 result

코드 전문
class Solution 
{
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) 
    {
        sort(nums.begin(), nums.end());

        return kSum(nums, target, 0, 4);
    }

    vector<vector<int>> kSum(vector<int>& nums, int target, int start, int k)
    {
        vector<vector<int>> result;

        if (start >= nums.size())
        {
            return result;
        }

        int average = target / k;

        if  (nums[start] > average || average > nums.back()) 
        {
            return result;
        }

        if (k == 2)
        {
            return twoSum(nums, target, start);
        }

        for (int i = start; i < nums.size(); i++)
        {
            if (i != start && nums[i - 1] == nums[i]) continue;

            auto subsets = kSum(nums, target - nums[i], i + 1, k -1);

            for (auto subset : subsets)
            {
                subset.push_back(nums[i]);

                result.push_back(subset);
            }
        }
        
        return result;
    }

    vector<vector<int>> twoSum(vector<int>& nums, int target, int start)
    {
        vector<vector<int>> result;

        int left = start;
        int right = nums.size() - 1;

        while (left < right)
        {
            int sum = nums[left] + nums[right];

            if (sum < target)
            {
                left++;
            }
            else if (sum > target)
            {
                right--;
            }
            else
            {
                 result.push_back({nums[left], nums[right]});

                left++;
                while (nums[left] == nums[left - 1] && left < right) left++;
            }
        }

        return result;
    }
};

LeetCode 시리즈의 다른 게시물 보기

Thumbnail of LeetCode - 22. Generate Parentheses

정수 N이 주어질 때 N개의 소괄호로 이뤄지는 모든 조합을 생성합니다.

Thumbnail of LeetCode - 21. Merge Two Sorted Lists

정렬되어 있는 두 연결 리스트를 하나로 합쳐야 합니다.

Thumbnail of LeetCode - 20. Valid Parentheses

주어진 문자열에서 열린 괄호와 닫힌 괄호가 올바르게 존재하는지 확인합니다.

Thumbnail of LeetCode - 19. Remove Nth Node From End of List

단방향 연결 리스트의 끝에서 N번째 노드를 제거합니다.

Thumbnail of LeetCode - 18. 4Sum

정수 배열 내 숫자 네 개를 더해 target 숫자를 구할 수 있는 모든 가짓수를 찾습니다.

Thumbnail of LeetCode - 17. Letter Combinations of a Phone Number

2에서 9 사이의 숫자로 구성된 문자열 입력 시 핸드폰 키보드에 매핑되는 문자열 조합을 생성합니다.

Thumbnail of LeetCode - 16. 3Sum Closest

정수형 배열과 세 정수를 더했을 때 target 숫자와 가장 가까운 세 숫자의 덧셈을 반환합니다.

Thumbnail of LeetCode - 15. 3Sum

정수형 배열이 주어질 때 세 정수의 합이 0이 되는 모든 가짓수를 찾습니다.