본문 바로가기

알고리즘/문제 풀이

99클럽 코테 스터디 5일차 TIL + Programmers 베스트 앨범

1. 문제 설명

문제 링크

2. 접근 방식

KEY WORD: Sorting, HashMap

  1. 음악 객체를 만든다. ( 멤버 변수: 자신의 고유번호, 장르, 플레이 횟수 )
  2. 입력 값들을 전부 음악 객체로 바꿔서 ArrayList에 추가한다.
  3. HashMap을 만든다. Key = 장르 , value = 장르에 해당하는 곡들의 플레이 총합
  4. 2번에서 만든 ArrayList를 정렬한다. 정렬 기준은 문제 그대로다. -> Comparator를 단순화한 Lamda 식을 이용해 구현
  5. 답변용 ansList를 만들고, 답변에 장르별로 몇 번 들어갔는지를 나타내는 genreAddedCount라는 HashMap도 하나 더 만든다.
  6. genreAddedCount는 Key = 장르, value = 장르 별로 답변 List에 나온 횟수 이다. .get(장르) < 2 이면 답변에 넣고 아니면 그냥 지나친다.
    (이렇게 하면, 장르에 해당하는 곡이 하나인 경우도 따로 예외처리 없이 추가 및 넘어갈 수 있고, 3개 이상인 경우도 2개만 넣고 넘어갈 수 있다.)

3. 코드 분석

import java.io.*;
import java.util.*;

class Solution {
    public int[] solution(String[] genres, int[] plays) {
        // 1.
        ArrayList<Music> playList = new ArrayList<>(); 
        // 2.
        HashMap<String, Integer> map = new HashMap<>();
        // -- 입력 
        for(int i = 0; i < genres.length; i++){
            playList.add(new Music(i, genres[i], plays[i]));
            map.put(genres[i], map.getOrDefault(genres[i], 0) + plays[i]);
        }
        // 플레이리스트 정렬
        Collections.sort(playList, (o1,o2) -> {
            // 장르도 같고, 플레이 횟수도 같으면 고유번호로 오름차순
            if(o1.genre.equals(o2.genre) && o2.play == o1.play){
                return o1.num - o2.num;
            }
            // 장르가 같다면 플레이 횟수로 내림차순
            if(o1.genre.equals(o2.genre)){
                return o2.play - o1.play;
            }
            // 제일 많이 플레이된 장르가 앞에 오도록 내림차순
            return map.get(o2.genre) - map.get(o1.genre);
        });
        // 장르 나온 횟수 세는 것 
        HashMap<String, Integer> genreAddedCount = new HashMap<>();

        ArrayList<Integer> ansList = new ArrayList<>();

        for(Music now : playList) {
            // 한 번 쭉 확인해보세요! 
            System.out.printf("번호: %d, 장르: %s, 플레이 수: %d\n", now.num, now.genre, now.play);

            if(genreAddedCount.getOrDefault(now.genre, 0) < 2){
                ansList.add(now.num);
                genreAddedCount.put(now.genre, genreAddedCount.getOrDefault(now.genre, 0) + 1);
            }

        }
        return ansList.stream().mapToInt(i -> i).toArray();
    }
}

class Music {
    int num;
    String genre;
    int play;

    public Music(int num, String genre, int play){
        this.num = num;
        this.genre = genre;
        this.play = play;
    }
}

4. 성장하기

(1) Stream 사용법

ansList.stream().mapToInt(i -> i).toArray();

mapToInt(i -> i)는 Stream< Integer > 라는 WrapperClass를 IntStream 이라는 원시형 클래스의 Stream으로 바꿔준다. 이와 비슷한 함수로는 mapToLong, mapToDouble 등이 있다.

toArray(): Stream< T >를 Object[] 로 변환하는 것이 기본이지만, 해당 문제에서는 mapToInt()를 통해 Stream 클래스를 IntStream이란 원시형 Stream으로 변환했다. IntStream.toArray는 int[]를 반환한다.

(2) 마지막 출력에서의 스파게티 코드

해당 답변은 GPT의 조언을 구하며 만들었다. 그 전 나의 코드는 100점 중 86.7점을 받으며, 테스트케이스 2개를 통과하지 못했다. GPT가 짜준 코드와 다른 것은 다 일치했으나, 맨 밑에 장르 별 2개의 인기곡만 뽑아서 앨범에 넣기를 실패했다.

        for(Music now : playList) {
            System.out.printf("번호: %d, 장르: %s, 플레이 수: %d\n", now.num, now.genre, now.play);
            // 현재 보고 있는 장르의 나온 곡 수 < 2 && 이전 장르와 같을 시
            if(cnt < 2 && now.genre.equals(prev)){
                // 고유 번호 입력
                ans[iter++] = now.num; 
                // 장르에서 나온 곡 수 ++ 
                cnt++;
                // 장르에 곡이 하나밖에 없다면 그냥 바로 2로 처리 
                if(map_cnt.get(now.genre) == 1){
                    cnt = 2;
                }
            }
            // 2인채로 지나가는 곡들 전부 continue로 지나가기 
            if(cnt == 2 && now.genre.equals(prev)){
                continue;
            }
            // cnt == 2인데, 장르가 바뀌었다.
            if(cnt == 2 && !now.genre.equals(prev)){
                // 해당 곡을 바로 저장하므로, cnt == 1인채로 시작
                cnt = 1;
                // 답변에 넣기, 이전 장르 갱신 
                ans[iter++] = now.num;
                prev = now.genre;
            }
        }

이와 같이 cnt == 2가 되는 경우를 일일히 셌는데, 이렇게 하니, 장르가 바뀔 떄, 장르에 해당하는 곡이 한 개 뿐일 때, 초기화 작업에서 오류가 난 것 같다.
나는 장르 안에 곡이 한 개인 경우도 예외처리를 하며 모든 경우의 수에 대해서 생각해줘야 한다고 느껴서 코드를 어지럽게 짰다. GPT한테 내 코드의 반례를 만들어달라 했지만, GPT가 만든 건 다 통과해서 오류를 완벽히 찼는데는 실패했다. 하지만, 예외 처리를 각각 하지 않으면서도 간편한 코드를 알게되어 기쁘다.

// GPT 코드 
        for(Music now : playList) {
            System.out.printf("번호: %d, 장르: %s, 플레이 수: %d\n", now.num, now.genre, now.play);
            if(genreAddedCount.getOrDefault(now.genre, 0) < 2){
                ansList.add(now.num);
                genreAddedCount.put(now.genre, genreAddedCount.getOrDefault(now.genre, 0) + 1);
            }

        }

위와 같이 map의 .getOrDefault()를 사용하면 단 두 줄이면 된다는 점에서 간편했다.