본문 바로가기

Algorithm/Baekjoon Oline Judge - Java

[백준] 1269_대칭차집합 / HashSet, removeAll, retainAll, addAll

 

 

Sliver

 

문제

자연수를 원소로 갖는 공집합이 아닌 두 집합 A와 B가 있다. 이때, 두 집합의 대칭 차집합의 원소의 개수를 출력하는 프로그램을 작성하시오. 두 집합 A와 B가 있을 때, (A-B)와 (B-A)의 합집합을 A와 B의 대칭 차집합이라고 한다.

예를 들어, A = { 1, 2, 4 } 이고, B = { 2, 3, 4, 5, 6 } 라고 할 때,  A-B = { 1 } 이고, B-A = { 3, 5, 6 } 이므로, 대칭 차집합의 원소의 개수는 1 + 3 = 4개이다.

 

 

* 입력

첫째 줄에 집합 A의 원소의 개수와 집합 B의 원소의 개수가 빈 칸을 사이에 두고 주어진다. 둘째 줄에는 집합 A의 모든 원소가, 셋째 줄에는 집합 B의 모든 원소가 빈 칸을 사이에 두고 각각 주어진다. 각 집합의 원소의 개수는 200,000을 넘지 않으며, 모든 원소의 값은 100,000,000을 넘지 않는다.

 

출력

첫째 줄에 대칭 차집합의 원소의 개수를 출력한다.

 


 

풀이 0.

 

처음엔 List의 contains를 사용하여 풀이하였지만, 시간초과가 발생하여 다른 방법을 생각해내야 했다.

 

 

풀이1. HashMap

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);   
        Map<Integer, Integer> map = new HashMap<>();
        int N1 = sc.nextInt();
        int N2 = sc.nextInt();
        int count = 0;
        sc.nextLine();
        for(int i=0; i<N1; i++){
            int num = sc.nextInt();
            map.put(num, map.getOrDefault(num, 0)+1);
        }
        sc.nextLine();
        for(int i=0; i<N2; i++){
            int num = sc.nextInt();
            map.put(num, map.getOrDefault(num, 0)+1);
        }

        Set<Integer> keys = map.keySet();
        for(Integer key : keys){
            if(map.get(key) == 1) count += 1;
        }

        System.out.println(count);

        sc.close();

    }

}

 

- 첫 번째 루프: O(N1)

- 두 번째 루프: O(N2)

- 세 번째 루프: O(N1 + N2)

 

위 코드의 전체 시간복잡도는 O(N1 + N2)으로 시간초과 문제를 해결할 수 있게 되었다. 

 

 


 

풀이2. HashSet

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        int N1 = sc.nextInt();
        int N2 = sc.nextInt();
        
        Set<Integer> set1 = new HashSet<>();
        for (int i = 0; i < N1; i++) {
            set1.add(sc.nextInt());
        }
        Set<Integer> set2 = new HashSet<>();
        for (int i = 0; i < N2; i++) {
            set2.add(sc.nextInt());
        }
        
        // 합집합 구하기
        Set<Integer> allSet = new HashSet<>(set1);
        allSet.addAll(set2);
        
        // 교집합 구하기
        Set<Integer> temp = new HashSet<>(set1);
        temp.retainAll(set2);
        
        // 합집합에서 교집합 요소 제거하기 (대칭 차집합 구하기)
        allSet.removeAll(temp);
        
        System.out.println(allSet.size());
        
        sc.close();
    }
}

 

HashSet의 retainAll, removeAll, addAll이라는 메서드를 활용하여 더 간단하게 풀이해 보았다. 

 

1. addAll()

addAll(Collection<? extends E> c)
  •  주로 두 집합의 합집합을 계산할 때 사용된다.
  • 만약 호출한 집합에 이미 존재하는 요소가 컬렉션에 있다면, 요소는 추가되지 않는다 (중복 요소는 허용되지 않음).

 

2. removeAll

removeAll(Collection c)
  • 주로 차집합을 계산할 때 사용된다.
  • 호출한 집합에서 컬렉션에 존재하는 요소들을 제거하여, 그 결과 남은 요소들로만 이루어진 집합을 리턴한다.

 

3. retainAll

retainAll(Collection c)
  • 호출한 집합에서 전달된 컬렉션(c)에 포함된 요소들만 남기고 나머지는 제거한다.
  • 주로 교집합을 계산할 때 사용된다.
  • 호출한 집합과 컬렉션의 공통 요소들로만 이루어진 집합을 리턴한다.

 

 


 

https://www.acmicpc.net/problem/1269