Algorithm

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

PlatinumeOlive 2024. 7. 2. 23:20

출처

https://school.programmers.co.kr/learn/courses/30/lessons/42579

 

프로그래머스

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

programmers.co.kr

 

문제

 

노래의 장르와 재생횟수가 주어진다. 총 재생횟수가 많은 장르부터 출력을 하는데, 한 장르당 가장 많이 재생된 곡 2개를 출력한다. 만약 2개가 아니라면 1개만 출력한다. 만약 곡의 재생횟수가 같으면 고유번호 i가 작은 순서로 출력한다.

 

풀이

이 문제는 복잡하게 생각하지 않고, 내가 만약 코딩이 아니라 손으로 직접 이 과정을 수행한다면 어떤 과정을 통해 수행할까? 라는 생각 아래, 다음과 같은 생각들을 거쳐 해결하였다.

 

먼저, 장르별로 재생된 총 횟수를 구하여 순서를 매겨야 한다. 만약 내가 직접 손으로 한다면 수첩에 장르 이름을 적고 옆에 장르별 횟수를 세고 더하면서 적어놓을 것이다.

 

이를 코딩으로 구현하기 위해 HashMap을 이용하여 장르별 총 재생횟수를 구해주었다. 이후, 우리는 총 재생횟수 보다는 장르의 순서가 필요하기 때문에, 정렬된 keySet만 구해주었다. 이를 람다식을 활용하여 정렬해주었다.

Map<String, Integer> rank = new HashMap<>();

for (int i = 0; i < genres.length; i++) {
    // 장르별 누적 재생횟수 계산
    rank.put(genres[i], rank.getOrDefault(genres[i], 0) + plays[i]);
}

List<String> keySet = new ArrayList<>(rank.keySet());
// keySet 정렬. Map에 저장된 value를 기준으로 정렬한다.
keySet.sort((o1, o2) -> rank.get(o2).compareTo(rank.get(o1)));

 

이제 우리는 keySet에 많이 재생된 장르의 순서를 가지게 되었다. 다음 단계는, 장르 별로 가장 인기 많은 2개의 곡을 선별하여야 한다. 이것을 어떻게 저장할까? 라는 고민을 하다가 곡의 정보가 들어있는 객체를 생성하기로 하였다. songDetail이라는 class를 만들어주었고, 이것의 List를 HashMap의 value로 넣어주었다.

    ....
    
    Map<String, Integer> rank = new HashMap<>();
        // 장르별 노래 정보를 담아놓을 Map
        Map<String, List<SongDetail>> song = new HashMap<>();
        
        for (int i = 0; i < genres.length; i++) {
            // 장르별 누적 재생횟수 계산
            rank.put(genres[i], rank.getOrDefault(genres[i], 0) + plays[i]);

            // 장르별 songDetail객체 추가
            List<SongDetail> tmp = song.getOrDefault(genres[i], new ArrayList<>());
            tmp.add(new SongDetail(i, plays[i]));
            song.put(genres[i], tmp);
        }
        
        //장르별 순위를 계산
        List<String> keySet = new ArrayList<>(rank.keySet());
        keySet.sort((o1, o2) -> rank.get(o2).compareTo(rank.get(o1)));

. . . . . . .


class SongDetail implements Comparable<SongDetail> {
        public int i;
        public int plays;
        
        public SongDetail(int i, int plays) {
            this.i = i;
            this.plays = plays;
        }
        // 비교연산자
        @Override
        public int compareTo(SongDetail other) {
            if (other.plays == this.plays) {
                return Integer.compare(this.plays, other.plays);
            }
            return Integer.compare(other.plays, this.plays);
        }
    }

 

장르별 재생횟수를 구하는 for문 내부에, 장르별 곡의 정보(재생 횟수, 고유번호)를 저장하도록 하였다. 이때 객체는 Comparable<SongDetail>을 상속받는데,  sort()를 통해 객체 List 안에서 재생횟수를 비교하고 내림차순으로 정렬하기 위해 compareTo 함수를 override하여 작성하였다.

 

이제 정렬을 해서 같은 장르에 속한 곡의 재생횟수를 기준으로 오름차순 정렬을 완료 한 뒤, 만약 곡이 1개라면 1개만 답안에 작성하고, 2개라면 2개를 출력하도록 구성하면 문제는 해결된다.

 

전체 코드

import java.util.*;
class Solution {

    class SongDetail implements Comparable<SongDetail> {
        // 노래 인덱스
        public int i;
        // 노래 재생횟수
        public int plays;
        
        public SongDetail(int i, int plays) {
            this.i = i;
            this.plays = plays;
        }
        
        // 비교연산자
        @Override
        public int compareTo(SongDetail other) {
            if (other.plays == this.plays) {
                return Integer.compare(this.plays, other.plays);
            }
            return Integer.compare(other.plays, this.plays);
        }
    }

    public int[] solution(String[] genres, int[] plays) {
        // 장르별 순위를 계산할 Map
        Map<String, Integer> rank = new HashMap<>();
        // 장르별 노래 정보를 담아놓을 Map
        Map<String, List<SongDetail>> song = new HashMap<>();
        
        for (int i = 0; i < genres.length; i++) {
            // 장르별 누적 재생횟수 계산
            rank.put(genres[i], rank.getOrDefault(genres[i], 0) + plays[i]);

            // 장르별 songDetail객체 추가
            List<SongDetail> tmp = song.getOrDefault(genres[i], new ArrayList<>());
            tmp.add(new SongDetail(i, plays[i]));
            song.put(genres[i], tmp);
        }
        
        //장르별 순위를 계산
        List<String> keySet = new ArrayList<>(rank.keySet());
        keySet.sort((o1, o2) -> rank.get(o2).compareTo(rank.get(o1)));
        

        List<Integer> answer = new ArrayList<>();
        // 장르별 곡 정보 객체를 정렬하고, answer에 추가
        for (String x: keySet) {
            List<SongDetail> tmp = song.get(x);
            Collections.sort(tmp);
            answer.add(tmp.get(0).i);
            if (tmp.size() >= 2) {
                answer.add(tmp.get(1).i);
            }
        }
        
        return answer.stream().mapToInt(i -> i).toArray();
    }

}