두 수와 세 수의 합 문제에 대한 Java 구현 및 분석

1. 문제 소개

알고리즘 인터뷰와 프로그래밍 연습에서 자주 등장하는 **두 수의 합(Two Sum)**과 **세 수의 합(3Sum)**은 배열 처리 문제로, 기본적인 알고리즘 능력을 평가하고 효율적인 검색 기법을 이해하는 데 중요한 역할을 합니다. 이 문서에서는 두 문제를 Java로 구현하고 최적화 방법을 설명합니다.

2. 두 수의 합(Two Sum)

2.1 문제 설명

배열에서 특정한 두 숫자의 합이 주어진 값(target)이 되도록 하는 인덱스를 찾아야 합니다.

2.2 해시 테이블 활용

코드 예제
import java.util.HashMap;
import java.util.Map;

public class TwoNumberSum {
    public int[] findTwoSum(int[] numbers, int targetValue) {
        Map<Integer, Integer> numberMap = new HashMap<>();
        
        for (int index = 0; index < numbers.length; index++) {
            int complementValue = targetValue - numbers[index];
            if (numberMap.containsKey(complementValue)) {
                return new int[] { numberMap.get(complementValue), index };
            }
            numberMap.put(numbers[index], index);
        }
        return new int[0];
    }
}
중요 포인트
특징 설명
시간 복잡도 O(n) 해시 테이블 덕분에 검색 시간이 O(1)로 줄어듦
공간 복잡도 O(n) 최악의 경우 모든 요소를 저장해야 함
중복 방지 현재 값을 넣기 전에 보수를 확인하여 중복을 방지
복잡도 비교
방법 시간 복잡도 공간 복잡도 적합한 상황
완전 탐색 O(n²) O(1) 작은 데이터셋
해시 테이블 O(n) O(n) 일반적인 최적화된 해결책

3. 세 수의 합(3Sum)

3.1 문제 설명

배열에서 세 숫자의 합이 0이 되는 조합들을 모두 찾습니다.

3.2 정렬 + 투포인터 접근

코드 예제
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ThreeNumberSum {
    public List<List<Integer>> findThreeSum(int[] numbers) {
        List<List<Integer>> result = new ArrayList<>();
        if (numbers == null || numbers.length < 3) return result;
        Arrays.sort(numbers); // 필수 사전 처리
        
        for (int i = 0; i < numbers.length - 2; i++) {
            if (i > 0 && numbers[i] == numbers[i - 1]) continue; // 중복 제거
            if (numbers[i] > 0) break; // 더 이상 가능한 조합 없음
            
            int leftPointer = i + 1, rightPointer = numbers.length - 1;
            while (leftPointer < rightPointer) {
                int total = numbers[i] + numbers[leftPointer] + numbers[rightPointer];
                if (total == 0) {
                    result.add(Arrays.asList(numbers[i], numbers[leftPointer], numbers[rightPointer]));
                    while (leftPointer < rightPointer && numbers[leftPointer] == numbers[leftPointer + 1]) leftPointer++;
                    while (leftPointer < rightPointer && numbers[rightPointer] == numbers[rightPointer - 1]) rightPointer--;
                    leftPointer++;
                    rightPointer--;
                } else if (total < 0) {
                    leftPointer++; // 더 큰 값을 필요로 함
                } else {
                    rightPointer--; // 더 작은 값을 필요로 함
                }
            }
        }
        return result;
    }
}
중요 포인트
전략 목적
정렬 투포인터 사용 가능, 중복 쉽게 처리
외부 루프 중복 제거 동일한 시작 요소 반복 방지
투포인터 이동 합이 0일 때 양쪽 포인터 동시에 이동
내부 중복 제거 유효한 조합 찾은 후 동일한 값 스킵
조기 종료 시작 요소가 0보다 크면 불필요한 계산 생략
복잡도 분석
작업 시간 복잡도 설명
정렬 O(n log n) 기본 정렬 알고리즘
투포인터 순회 O(n²) 외부 루프 O(n), 내부 투포인터 O(n)
총 복잡도 O(n²) 주요 작업은 투포인터

4. 비교 요약

항목 두 수의 합 세 수의 합
핵심 아이디어 해시 테이블 활용 정렬 + 투포인터 + 중복 처리
시간 복잡도 O(n) O(n²)
공간 복잡도 O(n) O(1) (결과 저장 제외)
난점 요소의 자기 자신 중복 방지 다차원 중복 처리와 포인터 협력

5. FAQ

Q1: 세 수의 합에서 왜 정렬이 필요한가요? 정렬하면 시간 복잡도를 O(n³)에서 O(n²)로 줄일 수 있고, 중복 요소를 쉽게 건너뛸 수 있습니다.

Q2: 입력 배열의 중복 요소는 어떻게 처리하나요?

  • 외부 루프에서 동일한 시작 요소 스킵
  • 유효한 조합 발견 후 내부 루프에서 포인터 값 중복 스킵

Q3: 왜 해시 테이블이 세 수의 합에 적합하지 않은가요? 해시 테이블은 두 수의 합을 저장할 수 있지만, 중복 처리가 어려우며 공간 복잡도가 증가합니다.

태그: java algorithm hashing

10월 8일 07:59에 게시됨