하루하루 꾸준히, 인생은 되는대로

알고리즘

프로그래머스 베스트 앨범

긤효중 2023. 6. 7. 17:40

 

문제를 보면 주어진 요구사항은 크게 4가지로 나뉜다.

  1. 장르별로 최대 2개씩까지만 앨범에 들어간다.
  2. 속한 노래가 많이 재생된 장르를 먼저 수록한다.
  3. 장르 내에서 많이 재생된 노래를 먼저 수록한다.
  4. 장르 내에서 재생횟수가 같은 노래끼리는 고유번호가 낮은 노래를 수록한다.

장르별로 구분을 해야하기 떄문에 Map을 사용할 수 있다. map의 키로 장르를 선택하고 값으로 해당 장르의 재생횟수, 고유번호 리스트를 선택할 수 있다.

 

입출력 예

문제에서 예를 들어보자.

  1. 처음 클래식이라는 장르가 보인다. map에 {클래식,{500번,처음 고유번호인0}}을 저장한다.
  2. 다음으로 Pop이라는 또 다른 새로운 장르가 보인다. 마찬가지로 map에 {팝,{600번,고유번호 1}}을 저장한다.
  3. 다음으로 클래식이라는 이미 존재하는 장르가 나왔다.  
    1. 이미 존재하는 장르가 나왔다면 기존 재생횟수 + 현재 재생횟수를 더하고 고유번호를 덮어 씌운다.
    2. 이미 존재하는 값을 가져와 갱신한다. 위의 경우 기존 재생횟수 500 + 현재 재생횟수 150을 새 재생횟수로 갱신하고 고유번호 3을 1과 덮어씌운다.
    3. 기존 장르를 map에서 지우고 , 새 값으로 교체한다.
    4. 결국 map에 {클래식,{650번,[1,3]}}이 최종적으로 들어간다.

 

이러한 과정을 반복하면 각 장르별로 map에는 {장르별 누적 재생횟수, 장르별 고유변호 배열}이 값으로 들어가 있게 된다.

 

이 과정 이후, 기존 요구사항인 속한 노래가 많이 재생된 장르를 구하기 위해 정렬을 해준다.

map에서는 정렬 메서드가 없으므로, 스프레드 연산자를 통해 정렬을 사용할 수 있다.

 

  1. 객체나 배열의 내용을 새로운 객체나 배열로 복사하기 위해 사용할 수 있다.
  2. 이를 통해 기존 객체나 배열을 변경하지 않고, 불변성을 유지하면서 원하는 작업을 수행할 수 있다.
  3. 배열 전개를 통해 배열에 포함된 각 항목을 개별적으로 다룰 수 있다.
  4. 배열 병합을 통해 여러 배열을 합쳐 하나의 배열로 만들 수 있다.

스프레드 연산자는 위의 역할을 할 수 있는데, map 객체의 내용을 배열로 복사하기 위해 사용할 것이다.

그렇게 map을 스프레드 연산자로 정렬을 하면 다음과 같이 변신할 수 있다!

 

보면 배열이 있고, play(재생 횟수)별로 카테고리가 정렬되었음을 확인할 수 있다.

이렇게 하게 되면, 고유번호를 알 수 있는데, 고유번호가 몇 번 재생되었는가?plays[고유변호]로 알 수 있다.

 

따라서 먼저 빈 배열을 만든다.

그 후 이 배열에 [고유변호,plays[고유번호]]형태로 push를 해준다. (고유변호와 해당 고유번호가 몇 번 재생되었는지?)

 

보면 [고유번호,plays[고유변호]]의 형태로 카테고리별로 배열이 만들어지는데, 주어진 요구사항은 

 

  1. 장르 내에서 많이 재생된 노래를 먼저 수록한다.
  2. 장르 내에서 재생횟수가 같은 노래끼리는 고유번호가 낮은 노래를 수록한다.

이다. 따라서 이 배열 내에서는 장르 내에서를 특정 지을 수 있고, 정렬을 통해 많이 재생된 횟수 -> 고유번호가 낮은 횟수 순으로 나열할 수 있다.

 

같은 장르 내에서 많은 재생횟수->고유번호 낮은 순으로 정렬된 모습

장르별 최대 2개까지 수록가능하니깐 이 배열을 돌면서 최대 2개까지 담으면 된다.

 

//가장 많이 재생된 노래를 두개씩 모아서 앨범을 만든다

//속한 노래가 많이 재생된 장르 먼저 수록
//장르 내에서 많이 재생된 노래
//고유번호 낮은 노래 수록

//클래식 3 팝 2

function solution(genres, plays) {
  var answer = [];
  let map = new Map();

  //{장르,횟수,고유번호}
  for (let i = 0; i < genres.length; i++) {
    //처음 등록되는 경우
    let getCategorie = map.get(genres[i]);
    if (typeof getCategorie === 'undefined') {
      map.set(genres[i], { play: plays[i], ownNumber: [i] });
    }

    //이미 맵에 존재하는 경우
    else {
      const playTime = Number(getCategorie.play) + plays[i];
      const ownNumberLists = getCategorie.ownNumber;

      map.delete(genres[i]);
      map.set(genres[i], { play: playTime, ownNumber: [...ownNumberLists, i] });
    }
  }

  //재생 횟수 기준 정렬
  const sortByPlayTime = [...map].sort(function (a, b) {
    return b[1].play - a[1].play;
  });

  //카테고리별 2개까지 앨범에 존재
  sortByPlayTime.forEach((data) => {
    const eachPlayTime = [];

    //고유번호와 해당 고유번호의 재생횟수를 배열에 넣고
    [...data[1].ownNumber].map((number) =>
      eachPlayTime.push([number, plays[number]])
    );

    //재생횟수 -> 고유번호 순으로 정렬한다.
    eachPlayTime.sort((a, b) => {
      if (a[1] !== b[1]) {
        return b[1] - a[1];
      } else if (a[1] === b[1]) {
        return a[0] - b[0];
      }
    });

    //최대 2개까지 담는다.
    for (let i = 0; i < eachPlayTime.length; i++) {
      if (i === 2) {
        break;
      }
      answer.push(eachPlayTime[i][0]);
    }
  });

  return answer;
}

'알고리즘' 카테고리의 다른 글

위상정렬(Topological sort)  (0) 2023.05.29
이분 그래프와 판별법  (2) 2023.03.15
프로그래머스 덧칠하기 C++  (0) 2023.03.03
프로그래머스 뒤에 있는 큰 수 찾기 C++  (0) 2023.01.31
프로그래머스 튜플 C++  (0) 2023.01.29