Problem Solving/Online Judge

[프로그래머스] 베스트 앨범

se0m 2024. 8. 21. 16:42
 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

💬 문제 설명

노래들의 장르와 재생 횟수가 주어졌을 때, 주어진 조건대로 정렬하고 각 장르 별로 최대 2개의 노래만 리턴하도록 한다.

1. 속한 노래가 많이 재생된 장르를 먼저 수록합니다. (장르 별 총 재생횟수내림차 순)
2. 장르 내에서 많이 재생된 노래를 먼저 수록합니다. (재생 횟수내림차 순)
3. 장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호가 낮은 노래를 먼저 수록합니다. (고유 번호오름차 순)

 

📌 주의 사항

  • 장르 별로 가장 많이 재생된 노래를 최대 두 개까지 모아 베스트 앨범을 출시하므로 특정 장르에 노래가 하나인 경우에는 하나의 노래만 수록하도록 한다.

 

👌 문제 풀이

  1. 장르별 총 재생 횟수를 담는` Map`장르별 노래 리스트를 담는 `Map`을 선언한다.
  2. 노래 정보를 담을 Song 클래스를 정의하고 `Comparable` 인터페이스를 구현하여 `compareTo()`를 재정의한다.
  3. 장르별 총 재생 횟수와 장르별 노래 리스트를 구한다.
  4. 장르별 총 재생 횟수 `Map`에서 값(총 재생 횟수)으로 `Map`을 정렬한다.
  5. 위에서 정렬한 순서대로 장르를 받아 해당 장르의 노래 리스트를 정렬한다.
  6. 사이즈에 따라 1개 or 2개를 출력한다.
  7. 리스트를 배열로 변환한다.

 

💻 코드

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

class Solution {
    public int[] solution(String[] genres, int[] plays) {
        List<Integer> answer = new ArrayList<>();
        
        // 장르: 총 재생 횟수
        Map<String, Integer> genrePlayCnt = new HashMap<>();
        // 장르: 노래 리스트
        Map<String, List<Song>> genreSongs = new HashMap<>();
        
        for (int i = 0; i < genres.length; i++) {
            // 장르별 총 재생 횟수를 구한다.
            if (genrePlayCnt.get(genres[i]) == null) {
                // 초기화
                genrePlayCnt.put(genres[i], 0);
            }
            
            int cnt = genrePlayCnt.get(genres[i]) + plays[i];
            genrePlayCnt.put(genres[i], cnt);
            
            // 장르별 노래 리스트를 구성한다.
            if (genreSongs.get(genres[i]) == null) {
                // 초기화
                genreSongs.put(genres[i], new ArrayList<>());
            }
            
            List<Song> temp = genreSongs.get(genres[i]);
            temp.add(new Song(i, plays[i]));
            genreSongs.put(genres[i], temp);
        }
        
        // 키(장르)만 뽑아내서 리스트 구성
        List<String> keySet = new ArrayList<>(genrePlayCnt.keySet());

        // 키 리스트를 이용해서 값(총 재생 횟수)의 내림차순 정렬
        keySet.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return genrePlayCnt.get(o2).compareTo(genrePlayCnt.get(o1));
            }
        });
        
        for (String genre : keySet) {
            // 장르별 노래 리스트 정렬
            Collections.sort(genreSongs.get(genre));

            for (int i = 0; i < 2; i++) {
                // 사이즈에 따라 1개, 2개 출력 결정
                if (genreSongs.get(genre).size() > i) {
                    answer.add(genreSongs.get(genre).get(i).id);
                }
            }
        }
        
        // 리스트 -> 배열
        int[] answerArray = new int[answer.size()];
        for (int i = 0; i < answer.size(); i++) {
            answerArray[i] = answer.get(i);
        }
        
        return answerArray;
    }
}

class Song implements Comparable<Song> {
    int id;
    int playCnt;
    
    public Song(int id, int playCnt) {
        this.id = id;
        this.playCnt = playCnt;
    }
    
    @Override
    public int compareTo(Song other) {
        // 재생 횟수가 같으면 고유 번호의 오름차 순 정렬
        if (other.playCnt == this.playCnt) {
            return this.id - other.id;
        }

        // 재생횟수의 오름차 순으로 정렬
        return other.playCnt - this.playCnt;
    }
}

 

🎓 배운 것

  1. Map에서 Value 기준으로 정렬할 때는 `keySet()`으로 Map의 Key 값만 빼와서 리스트를 구성하고 이 리스트로 `Comparator` 인터페이스를 구현해서 정렬한다.
☞ 결과 값은 값을 기준으로 정렬된 키의 리스트 ❗ 이므로 정렬된 Map을 사용하려면 이 정렬된 키 리스트를 이용해서 Map의 값에 접근해야 한다.