[LV0] 최빈값 구하기
갈수록 문제들이 어려워진다. 문제 푸느라 블로그를 못써서 하루에 몰아 써버리기
최빈값은 주어진 값 중에서 가장 자주 나오는 값을 의미합니다. 정수 배열 array가 매개변수로 주어질 때, 최빈값을 return 하도록 solution 함수를 완성해보세요. 최빈값이 여러 개면 -1을 return 합니다.
제한사항
- 0 < array의 길이 < 100
- 0 ≤ array의 원소 < 1000
입출력 예arrayresult
[1, 2, 3, 3, 3, 4] | 3 |
[1, 1, 2, 2] | -1 |
[1] | 1 |
입출력 예 설명
입출력 예 #1
- [1, 2, 3, 3, 3, 4]에서 1은 1개 2는 1개 3은 3개 4는 1개로 최빈값은 3입니다.
입출력 예 #2
- [1, 1, 2, 2]에서 1은 2개 2는 2개로 최빈값이 1, 2입니다. 최빈값이 여러 개이므로 -1을 return 합니다.
입출력 예 #3
- [1]에는 1만 있으므로 최빈값은 1입니다.
가장 많이 나온 값. 최빈값을 구하는 문제였다.
코딩테스트 공부를 시작했을 때, 매일 4문제씩 풀어보라는 말에 1,2일차를 후다닥 풀고 바로 3일차 문제까지 도전 했었다.
위의 두 문제까지는 쉬운 문제들이어서 다음도 쉽겠지~ 하고 들어갔는데....
오메? 이게 입문자 3일차에 풀 수 있는 내용이냐~~~~~ 어이없을무
그 이후에도 틈틈히 들어가서 풀어 보려 했지만 번번히 실패한 나...울고 있는 나에 모습?
여튼, 이 문제를 드디어 다시 만나게 되었다. 정답률 60%대 문제로 들어오니 코드도 매우 길어지고 조건도 까다로워져서 푸는데 시간이 오래 걸리기 시작했다. 이게....0단계?
처음 문제를 보았을 때 접근 방식은 다음과 같았다.
- 주어진 배열 array의 요소들을 추려 새로운 배열을 만듬
- 새로운 배열과 array를 비교하여 각 숫자가 몇번 나왔는지를 count
- count의 숫자가 가장 높은 요소를 찾고, 해당 요소를 return
먼저 array의 요소들은 Set() 객체를 통해 추려낸 뒤 setArr라는 배열 안에 저장했다.
let setArr = [...new Set(array)] |
- array = [1, 2, 3, 3, 3, 4]
- setArr = [1, 2, 3, 4]
이제 array와 setArr를 비교해서 setArr의 요소를 array가 포함하고 있으면 count가 올라가도록 만들어야 한다.
만약 setArr의 요소 1을 array가 가지고 있다면 1의 count가 하나 올라가야 하는 것이다.
그러기 위해서는 배열의 요소간의 비교가 필요했다. 이를 해결하기 위해 filter() 함수를 가져왔다.
filter() 함수는 인자로 함수를 받는다. 그리고 해당 함수의 조건을 통과하는 요소들을 모아 새로운 배열을 만든다.
예를 들어
let number = [1,2,3,4,5,6,7,8,9] let even = number.filter(a=>a%2 == 0) |
라고 했을 때, even에는 number의 요소들 중 함수 'n % 2 == 0'을 만족하는 요소들이 저장된다. 따라서 even은 [2,4,6,8]이라는 새로운 배열이 되는 것이다.
이 filter() 함수를 통해 인자간의 요소를 비교해 주기로 했다.
먼저 count라는 빈 배열을 만든다. 이 count 배열은 각 요소가 몇번 나왔는지 저장하는 배열이다.
count 배열 안에는 다음 조건을 만족하는 요소들이 들어가야 한다
array의 n번째 요소가 setArr의 m번째 요소와 일치하는가?
setArr의 모든 요소를 따져봐야 하므로 for문을 이용하고, setArr[i]와 일치하는 새로운 배열을 만들어야 하므로
filter() 안에는 a => a == setArr[i]라는 함수를 넣어준다.
그렇다면 setArr[i]와 일치하는 요소들로 이루어진 새로운 배열이 생성될 것이다.
- [1]
- [2]
- [3,3,3]
- [4]
새롭게 생성된 배열의 길이 = 요소가 등장한 횟수이므로, length를 이용해 등장 횟수로 변환 뒤 count 배열에 차례대로 넣어준다.
for(let i = 0; i < setArr.length; i++){ count.push(array.filter(a => a == setArr[i]).length) } |
이 과정까지 마치면 count 배열의 n번째 요소는 setArr의 n번째 요소가 array 안에 등장한 횟수가 된다.
- array = [1, 2, 3, 3, 3, 4]
- setArr = [1, 2, 3, 4]
- count = [1, 1, 3, 1]
이제 이 count 배열의 요소끼리 비교하여 최빈값을 찾아주면 된다.
가장 간단한 방법은 indexOf()와 Math.max`를 사용하는 것.
최빈값은 setArr[count 배열의 최댓값의 인덱스]이다. max라는 변수에 Math.max를 이용해 count 배열의 최댓값을 넣어주고, indexOf(max)를 이용해 최빈값의 인덱스를 찾아주면 된다.
let max = Math.max(...count) answer = setArr[count.indexOf(max)] |
하지만 이 방법은 아주 큰 결함이 하나 있었는데...
바로 최빈값이 2개 이상인 경우를 판별할 수 없다는 것이었다. 허!
예를들어
- array = [1, 2, 3, 4, 1, 2, 3, 4]
- setArr = [1, 2, 3, 4]
- count = [2, 2, 2, 2]
이런 경우에는 <최빈값이 여러 개면 -1을 return 합니다.> 라는 조건에 의해 -1이 리턴되어야 하지만, answer은 맨 앞의 1이 되고 만다.
이를 해결하기 위해 아까 썼던 filter()를 다시 가지고 왔다.
count 배열의 최댓값이 최빈값의 등장 빈도인데, 만약 이 최댓값이 여러개라면 최빈값도 여러개라는 말이 된다.
따라서 count 배열의 요소들과 count 배열의 최댓값을 비교하여 같은 값이 2개 이상이라면 -1을 리턴해야 하는 것이다.
filter()를 이용해 count 배열에서 최댓값과 같은 요소들을 따로 모아 새로운 배열을 만들고, 새로운 배열의 길이가 2 이상이라면 -1. 아니라면 처음 구했던 답을 리턴한다.
let check = count.filter(n => n == max) if(check.length > 1){ answer = -1 } else if(check.length == 1){ answer = setArr[count.indexOf(max)] } |
마지막으로. array 배열의 요소가 하나인 배열은 최빈값이 자기 자신이므로 array[0]로 처리해준다.
function solution(array) { var answer = 0; let setArr = [...new Set(array)] let count = [] if(array.length == 1){ answer = array[0] } else { for(let i = 0; i < setArr.length; i++){ count.push(array.filter(a => a == setArr[i]).length) } let max = Math.max(...count) let check = count.filter(n => n == max) if(check.length > 1){ answer = -1 } else if(check.length == 1){ answer = setArr[count.indexOf(max)] } } return answer; } |
사실 처음에는 더 복잡하게 풀었었는데, 글 쓰다가 문득 생각이 나서 이 방법으로 해봤더니 아주 잘 됐다. 나이스~