💡 문제
베스트앨범 (https://school.programmers.co.kr/learn/courses/30/lessons/42579)
자세한 문제 설명과 입출력 예는 링크를 참고해주세요.
📝 선행 개념
1. Comparable 인터페이스
public interface Comparable<T> {
public int compareTo(T o);
}
이 메서드는 현재 객체와 지정된 객체를 비교하여 다음과 같은 값을 반환해야 합니다
2. Collections.sort
Collections.sort 메서드는 Java의 Collections 클래스에 정의된 정적 메서드로, 리스트를 정렬할 때 사용됩니다. Comparable 인터페이스를 구현한 객체들의 리스트를 정렬할 때 유용합니다.
List<Song> songs = new ArrayList<>();
songs.add(new Song(0, 500));
songs.add(new Song(1, 600));
songs.add(new Song(2, 150));
Collections.sort(songs); // Song 클래스의 compareTo 메서드 기준으로 정렬
위의 예제에서 Collections.sort(songs)는 songs 리스트를 Song 클래스의 compareTo 메서드 기준으로 정렬합니다.
🤓 문제 풀이
- 각 장르의 총 재생 횟수를 계산하여 많이 재생된 장르 순으로 정렬
- 각 장르 내에서 노래를 재생 횟수 내림차순으로 정렬하고, 재생 횟수가 같은 경우 고유 번호 오름차순으로 정렬
- 렬된 정보를 바탕으로 각 장르에서 상위 두 개의 노래를 선택하여 결과값 구성
/*
Comparable을 이용한 정렬:
Collections.sort(songs)를 사용하여 각 장르별 노래 리스트를 정렬합니다.
keySet 리스트를 정렬하여 재생 횟수가 많은 순서대로 장르를 처리합니다.
*/
public class PRO_Hash_42579_3 {
// Song 클래스 정의, Comparable 인터페이스를 구현하여 정렬 기준을 설정
public static class Song implements Comparable<Song> {
/*
Song 클래스:
Song 클래스는 각 노래의 인덱스와 재생 횟수를 저장하며, Comparable<Song>을 구현하여 정렬 기준을 정의합니다.
compareTo 메서드는 재생 횟수를 기준으로 내림차순 정렬하고, 재생 횟수가 같으면 인덱스를 기준으로 오름차순 정렬합니다.
*/
int index; // 노래의 인덱스
int plays; // 노래의 재생 횟수
public Song(int index, int plays) {
this.index = index; // 인덱스 초기화
this.plays = plays; // 재생 횟수 초기화
}
@Override
public int compareTo(Song other) {
// 재생 횟수가 같으면 인덱스 오름차순 정렬
if (this.plays == other.plays) {
return this.index - other.index;
}
// 재생 횟수가 다르면 재생 횟수 내림차순 정렬
return other.plays - this.plays;
}
}
public static int[] getBestAlbum(String[] genres, int[] plays) {
/*
자료 구조 업데이트:
music 맵은 HashMap<String, List<Song>> 형태로 변경되어 장르별 노래 리스트를 저장합니다.
*/
ArrayList<Integer> answer = new ArrayList<>(); // 최종 답을 저장할 리스트
HashMap<String, Integer> num = new HashMap<>(); // 장르별 총 재생 횟수를 저장할 맵
HashMap<String, List<Song>> music = new HashMap<>(); // 장르별 노래 리스트를 저장할 맵
for (int i = 0; i < plays.length; i++) {
// 장르별 총 재생 횟수를 갱신
num.put(genres[i], num.getOrDefault(genres[i], 0) + plays[i]);
// 장르별 노래 리스트에 현재 노래 추가
if (!music.containsKey(genres[i])) {
music.put(genres[i], new ArrayList<>()); // 해당 장르가 처음 등장하면 리스트 생성
}
music.get(genres[i]).add(new Song(i, plays[i])); // 노래 추가
}
// 장르별 총 재생 횟수를 기준으로 장르를 내림차순 정렬
List<String> keySet = new ArrayList<>(num.keySet());
Collections.sort(keySet, (s1, s2) -> num.get(s2) - num.get(s1));
for (String key : keySet) {
List<Song> songs = music.get(key); // 현재 장르의 노래 리스트 가져오기
Collections.sort(songs); // 노래 리스트를 재생 횟수 기준으로 정렬
// 정렬된 리스트에서 상위 두 개의 노래 인덱스 추가
answer.add(songs.get(0).index);
if (songs.size() > 1) {
answer.add(songs.get(1).index);
}
}
// ArrayList를 int 배열로 변환하여 반환
return answer.stream().mapToInt(i -> i).toArray();
}
}
📚 풀이 보완
처음 문제를 풀었을 때와는 다른 방법으로 사용했다.
노래의 인덱스와 재생 횟수를 정의하는 Song 클래스를 정의하였고 Comparable<Song> 인터페이스를 사용하여 정렬 기준을 정의하여 재생 횟수 내림차순으로 정렬하고, 재생 횟수가 같은 경우 인덱스 오름차순으로 정렬하는 로직으로 변경하였다.
변경 전과 변경 후는 차이가 크지는 않았지만 Comparable 인터페이스를 구현하여 정렬을 더 직관적이고 효율적으로 수행할 수 있게 되었고 HashMap<String, List<Song>> 구조로 변경하여 장르별 노래 리스트를 보다 효율적으로 관리하고 정렬하는 코드로 작성할 수 있었다.
📍 Plus+
문제 상세 풀이 및 이모저모 ~..~
- Song 클래스 정의
public static class Song implements Comparable<Song> {
int index; // 노래의 인덱스
int plays; // 노래의 재생 횟수
public Song(int index, int plays) {
this.index = index; // 인덱스 초기화
this.plays = plays; // 재생 횟수 초기화
}
@Override
public int compareTo(Song other) {
// 재생 횟수가 같으면 인덱스 오름차순 정렬
if (this.plays == other.plays) {
return this.index - other.index;
}
// 재생 횟수가 다르면 재생 횟수 내림차순 정렬
return other.plays - this.plays;
}
}
- Comparable 인터페이스는 객체의 자연 정렬 순서를 정의하기 위해 사용, 구현 클래스는 compareTo 메서드를 오버라이드하여 두 객체의 비교 방법을 지정
- 위의 Song 클래스에서 compareTo 메서드는 재생 횟수가 같을 경우 인덱스를 기준으로 오름차순 정렬하고, 재생 횟수가 다를 경우 재생 횟수를 기준으로 내림차순 정렬합니다.
- Collections.sort
Collections.sort 메서드는 리스트를 정렬
리스트에 있는 객체들이 Comparable 인터페이스를 구현하고 있으면, compareTo 메서드에 정의된 기준에 따라 정렬 - HashMap과 getOrDefault
HashMap 클래스의 getOrDefault 메서드는 키가 존재하면 해당 키의 값을 반환하고, 존재하지 않으면 기본값을 반환하는 메서드
위의 코드에서 "classic" 키가 genrePlays 맵에 없으므로 getOrDefault는 0을 반환합니다.
- For문과 getOrDefault를 사용한 총 재생 횟수 계산 총 재생 횟수를 계산하기 위해 for 문과 getOrDefault 메서드를 사용할 수 있습니다. 각 장르의 총 재생 횟수를 계산할 때 유용합니다.
위의 코드는 각 장르별 총 재생 횟수를 genreTotalPlays 맵에 저장합니다. getOrDefault 메서드를 사용하여 기본값 0을 설정하고, 현재 재생 횟수를 더합니다.String[] genres = {"classic", "pop", "classic", "classic", "pop"}; int[] plays = {500, 600, 150, 800, 2500}; HashMap<String, Integer> genreTotalPlays = new HashMap<>(); for (int i = 0; i < genres.length; i++) { genreTotalPlays.put(genres[i], genreTotalPlays.getOrDefault(genres[i], 0) + plays[i]); }
'알고리즘 & 자료구조 > 코딩테스트 준비' 카테고리의 다른 글
[프로그래머스][JAVA] 이중 우선순위 큐 (0) | 2024.05.26 |
---|---|
[프로그래머스][JAVA] 디스크 컨트롤러 (0) | 2024.05.26 |
[프로그래머스][JAVA] 주식 가격 (0) | 2024.05.23 |
[프로그래머스][JAVA] 다리를 지나는 트럭 (0) | 2024.05.23 |
[백준][JAVA] 비슷한 단어 (0) | 2024.05.22 |