해시(Hash) - 폰켓몬/마라톤/전화번호 목록/의상/베스트 앨범
해시(Hash) - 폰켓몬/마라톤/전화번호 목록/의상/베스트 앨범
1. 폰켓몬
최대 N/2 마리의 폰켓몬을 선택하면서, 폰켓몬 종류의 최대 개수를 구하는 문제
풀이 전략
- 중복을 제거한 폰켓몬 종류의 개수를 구한다. (
set
을 사용하여 중복 제거). - 가능한 최대 폰켓몬 종류 수는 N/2입니다. 즉,
nums
에서 고를 수 있는 폰켓몬의 종류는N/2
마리입니다. 하지만 폰켓몬의 종류가N/2
보다 적으면 그 종류만큼 선택할 수 있습니다. - 따라서 최종적으로 고를 수 있는 폰켓몬 종류의 수는
Math.min(중복을 제거한 폰켓몬 종류의 수, N/2)
이다.
알고리즘 풀이
1
2
3
4
5
6
7
function solution(nums) {
// 폰켓몬의 종류를 중복 없이 셈
const uniqueTypes = new Set(nums);
// 선택할 수 있는 최대 폰켓몬 종류의 수는 N/2이므로, 그 값과 uniqueTypes의 크기 중 작은 값이 답
return Math.min(uniqueTypes.size, nums.length / 2);
}
2. 마라톤
해시 테이블(또는 객체)을 사용하여, 마라톤에 참여한 선수와 완주한 선수 명단을 비교함으로써 완주하지 못한 선수를 찾는 문제
풀이 전략
- 참가자 배열에서 각 선수의 이름을 해시 테이블에 저장
- 각 이름을 키로 하고, 그 출현 횟수를 값으로 설정한다.
- 완주자 배열에서 각 선수의 이름을 해시 테이블에서 찾아 카운트를 하나씩 감소:
- 완주한 선수는 해시 테이블에서 그 출현 횟수를 감소한다.
- 완주하지 못한 선수는 카운트가 1인 선수
알고리즘 풀이
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function solution(participant, completion) {
const map = new Map(); // 1. map 객체 생성
// 2. 배열 순회
for (let i = 0; i < participant.length; i++) {
let a = participant[i],
b = completion[i];
map.set(a, (map.get(a) || 0) + 1); // 참가자는 + 1
map.set(b, (map.get(b) || 0) - 1); // 완주자는 - 1
}
// 3. 카운트가 1인 선수 찾기
for (let [k, v] of map) {
if (v > 0) return k;
}
return "nothing";
}
3. 전화번호 목록
전화번호부에 있는 전화번호 중 하나가 다른 전화번호의 접두어인지 확인하는 문제
풀이 전략
- 전화번호를 해시맵에 저장
- 전화번호부의 모든 번호를 해시맵에 저장한다. 여기서 전화번호는 키로 사용되며, 값은
true
로 설정한다.
- 전화번호부의 모든 번호를 해시맵에 저장한다. 여기서 전화번호는 키로 사용되며, 값은
- 각 번호의 접두어 체크
- 각 번호에 대해 접두어가 존재하는지 확인한다. 예를 들어, 번호
"12345"
가 있으면"1"
,"12"
,"123"
과 같은 접두어가 다른 번호에 존재하는지 확인한다. - 이때,
slice(0, i)
를 사용하여 접두어를 추출하고, 해시맵에서 이 접두어가 존재하는지 확인합니다. 존재하면 접두어 관계가 성립되므로false
를 반환합니다.
- 각 번호에 대해 접두어가 존재하는지 확인한다. 예를 들어, 번호
- 접두어가 존재하지 않으면
- 모든 번호에 대해 접두어를 체크한 후, 접두어가 발견되지 않으면
true
를 반환한다.
- 모든 번호에 대해 접두어를 체크한 후, 접두어가 발견되지 않으면
알고리즘 풀이
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function solution(phone_book) {
let phoneMap = new Map();
// 1. 모든 번호 해시맵에 저장
for (const number of phone_book) {
phoneMap.set(number, true);
}
// 2. 접두어 검사
for (const number of phone_book) {
for (let i = 1; i < number.length; i++) {
const prefix = number.slice(0, i);
if (phoneMap.has(prefix)) return false; // 접두어 존재할 경우
}
}
return true; // 3. 접두어가 없을 경우
}
4. 의상
주어진 의상 목록에서 가능한 옷의 조합 수를 계산하는 문제
풀이 전략
- 의상 종류별 의상 개수 세기
categoryCount
객체를 해시맵으로 사용한다.
- 각 의상 종류에서 선택 가능한 방법 계산
- 각 의상 종류에 대해 의상을 선택하는 방법은
개수 + 1
이다. 의상을 입지 않거나 해당 종류의 의상 중 하나를 고를 수 있기 때문이다.
- 각 의상 종류에 대해 의상을 선택하는 방법은
- 최종 가능한 조합 수 계산
- 모든 의상 종류에 대해 가능한 조합 수를 곱해주고, 최소 1개의 의상은 입어야 하므로 마지막에 1을 빼
알고리즘 풀이
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function solution(clothes) {
// 의상의 종류별로 개수를 저장할 Map 생성
const clothesMap = new Map();
// 1. 의상 종류별 개수 세기
for (const [name, type] of clothes) {
if (clothesMap.has(type)) {
clothesMap.set(type, clothesMap.get(type) + 1); // 해당 카테고리 의상 수 증가
} else clothesMap.set(type, 1); // 해당 카테고리가 없으면 1로 초기화
}
// 가능한 조합 수 계산
let answer = 1;
for (const count of clothesMap.values()) {
answer *= count + 1; // 입지 않는 경우 포함
}
return answer - 1; // 아무 의상도 입지 않는 경우 제외
}
5. 베스트 앨범
장르별로 가장 많이 재생된 노래를 두 개씩 선택하여 뽑는 문제
풀이 전략
- 장르별로 곡 데이터 분류
genreMap
이라는Map
을 사용하여 각 장르에 속하는 곡들의 데이터를 저장한다.genreMap
의 값은{ total: 0, songs: [] }
형태로,total
은 해당 장르의 총 재생 횟수이고,songs
는 해당 장르에 속한 곡들의 정보(곡 ID와 재생 횟수)를 담고 있는 배열이다.
- 장르별 총 재생 횟수 기준으로 정렬
genreMap
의entries()
메서드를 사용하여 장르별로Map
객체를 배열로 변환한 후, 각 장르의 총 재생 횟수를 기준으로 내림차순 정렬한다.
- 각 장르에서 최대 2곡씩 선택
- 각 장르에 대해, 해당 장르에 속한 곡들을 재생 횟수 내림차순으로 정렬하고, 재생 횟수가 같은 곡들에 대해서는 고유 번호가 낮은 곡을 우선적으로 선택한다.
sortedSongs.slice(0, 2)
를 사용하여 각 장르에서 가장 많이 재생된 최대 2곡을 선택한다.
알고리즘 풀이
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function solution(genres, plays) {
const answer = [];
const genreMap = new Map();
// 장르별로 곡 데이터 분류
genres.forEach((genre, i) => {
if (!genreMap.has(genre)) {
genreMap.set(genre, { total: 0, songs: [] });
}
genreMap.get(genre).total += plays[i];
genreMap.get(genre).songs.push({ id: i, play: plays[i] });
});
// 장르별 총 재생 횟수 기준으로 정렬
const sortedGenres = [...genreMap.entries()].sort(
(a, b) => b[1].total - a[1].total
);
// 각 장르에서 최대 2곡씩 선택
for (const [genre, { songs }] of sortedGenres) {
const sortedSongs = songs.sort((a, b) => {
if (b.play === a.play) {
return a.id - b.id;
}
return b.play - a.play;
});
// 최대 두 곡 선택
sortedSongs.slice(0, 2).forEach((song) => answer.push(song.id));
}
return answer;
}
This post is licensed under CC BY 4.0 by the author.