[코테] 가장 큰 수 (정렬)

ekil·2026년 4월 18일

코딩테스트

목록 보기
12/15

가장 큰 수 (정렬)

2026.4.17. & 2026.4.18.

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

핵심 개념

  • 기본 sort()로는 엣지케이스가 존재하는 상황에서 직접 정렬 로직 구현하기
    [3, 30, 34]가 주어질 때, 일반적인 내림차순 정렬하면 [34, 30, 3]이 되어 '34303'을 리턴하게 되는데, '34330'이 더 큰 숫자임!
  • 위와 같은 비교를 위해 sort()에 커스텀 콜백을 넘기는데, 문자열로 만들어 비교하는 방법을 선택했음
  • 주의할 점: 내림차순 정렬할 때 sort((a, b) => b - a) 이렇게 많이 쓰다보니, 콜백에서 비교하고 a : b 를 리턴하게 했는데, 그렇게 하면 정확한 정렬이 안됨! 왜? sort는 콜백 반환값이 음수/양수/0 중 무엇인지만 판단하기 때문임. 3과 30을 비교해서 30을 리턴하든 3을 리턴하든 똑같이 양수이므로 (a, b)b가 앞에 배치된다. 문제에서 주어지는 배열은 0 또는 양의 정수로 구성되므로 결국 어떤 경우든 항상 0 또는 양수를 리턴하니 정렬이 제대로 안되는 것이다~~!! => 명확하게 음수/양수/0 케이스로 구별하여 리턴해야 한다.
  • 엣지케이스 (테스트 케이스 11번) 떠올리기: 문제 조건에 따르면 모든 요소가 0일 수도 있음. 그럴 땐 '000' 같은 형태가 아닌 '0'을 리턴하도록 처리가 필요함.

내 풀이

function solution(numbers) {
    if (numbers.every(n => n === 0)) return '0';
    
    const answer = [...numbers].sort((a, b) => `${a}${b}` < `${b}${a}` ? 1 : -1);
    
    return answer.join('');
}

개선된 풀이

function solution(numbers) {
    const answer = [...numbers].sort((a, b) => `${a}${b}` < `${b}${a}` ? 1 : -1).join('');
    
    return answer[0] === '0' ? '0' : answer;
}

핵심 차이

배열의 모든 요소가 0인 경우를 체크하는 방법의 차이

  • 얼리 리턴이 가독성도 좋고 더 효율적이라 생각해서 코드 제일 앞에서 그 경우를 체크하게 했다. 그런데 다른 사람의 풀이를 보니까 연산 수행 후 제일 앞 요소가 '0'인지를 체크하고 있었다.
  • 정렬 후 제일 앞 요소가 '0'이라면 당연히 그 뒤의 모든 값은 0이겠지.
  • 근데 모든 요소가 0일 때는 sort 연산을 하지 않는 게 더 효율적이지 않을까? 하며 얼리 리턴을 고집해봤다. 대신 모든 요소를 순회하지 않게, some()으로 변경하고, 0이 아닌 요소가 하나라도 발견되면 기존대로 answer을 구하는 연산을 수행하게 하고, 아니면 0을 리턴하게도 작성해봤다.
  • 3가지 케이스를 모두 실행하고 실행 속도를 비교했다.

비교 결과
A. [얼리 리턴] every를 사용한 경우 - 전반적인 연산 속도가 가장 오래 걸림.
B. some을 사용해 조건을 체크한 경우 - 모든 요소가 0인 케이스 연산만 C보다 0.02ms 빠르고, 그 외의 속도는 C가 근소하게 빠름.
C. 조건 없이 코드를 실행하고, 마지막에 제일 첫 요소가 '0'인지 체크한 경우 - 전반적으로 가장 빠름!

왜 이런 결과가 나왔는지 생각해봤다.
얼리 리턴, 조건 체크를 추가하면, 어떤 인풋이 들어오든 if문을 한번 실행한다. 그것을 만족하는 케이스가 많다면 더 효율적일 수도 있다. 그러나 이번 문제에서는 11번 테스트 케이스를 제외하고는 그런 경우가 없다.
이 접근법은 (every를 쓰든 some을 쓰든), 한가지 경우를 위해 모든 다른 경우에 불필요한 if문과 비교 연산이 실행되는 것이다. => 오히려 이것이 비효율!

정렬 연산 수행 후 그 문자열의 제일 앞 요소가 '0'과 같은지 비교하는 것이 오히려 가벼운 것인가보다.

막혔던 포인트

  • 어떻게 접근할 것인가? 일반적인 정렬은 안된다. 문제에서 설명한 접근법대로 문자열을 구성하고 그것을 비교하는 함수를 sort의 콜백으로 넘기자. 까지는 그래도 금방 생각해냈는데, 거기서 콜백이 리턴하는 값을 1, -1이 아닌 그 숫자 자체로 작성하는 바람에 오답을..
  • 그래서 그 접근법이 아닌가 하고 다른 식으로도 고민했다.
  • 극단적으로 "b" - "a" 이런 것도 적어봤음. 참고로 저걸 실행하면 NaN임..
  • sort 콜백 구성할 때, 서로 같은 값이면 0을 리턴하게 3가지로 나누어 작성했던 기억이 나서 그것도 추가해봄. 그러다가 아, 비교 후 1, -1을 리턴해서 정렬이 이루어지도록 해야하지? 떠올랐다.
  • 수정 후 11번 테케만 계속 실패해서 힌트 요청함. 나는 맨 처음 값이 0인 경우에서 실패하는 걸까? 라고만 생각했는데, 비교 연산을 하는데 0이 맨 앞에 놓일 순 없지!라고 생각하고 넘겼었음.
  • 힌트: numbers = [0, 0, 0] 일 때 이 코드의 결과는 무엇인가?
  • 아하리 모든 게 0일 때는 '0'이 나와야 하는구나 -> 얼리 리턴 처리

풀면서 찾은 개념

  • 문자열 비교는 localeCompare (그냥 compare는 존재하지 않음)
  • 배열의 length = 1 일때 sort는 어떻게 동작하는가? => 정렬을 수행하지 않고 리스트를 그대로 반환한다고 함
  • string from array: 주어진 배열을 정렬했으니 이제 그대로 문자열로 구성하면 되는데, 그런 메서드가 분명 존재하겠지? 라는 생각으로 찾음. join('')하면 배열의 모든 요소를 빈 문자열로 연결해 하나의 문자열이 구성됨

다음에 비슷한 문제 만나면

  1. sort의 콜백을 구성할 때는 -1, 0, 1을 일관되게 리턴하게 하자. sort는 콜백 반환값이 음수인지 양수인지 0인지 판단하기 때문!!
  2. 발생 확률이 극히 낮은 엣지케이스를 대비할 때는 얼리 리턴 코드가 오히려 비효율적일 수 있다. 모든 정상 케이스에서도 그 조건문을 한번 체크해야 하니까.
  3. 문자열의 제일 앞 요소에 대한 단순 비교 연산은 가볍다.
  4. 엣지케이스가 떠오르지 않을 땐 문제의 제약 조건들을 다시 살펴보고, 모든 요소가 0 같은 경우도 의심해보자.
profile
좋아하는 일을 잘함으로써 먹고살고 싶은 프론트엔드 개발자입니다.

0개의 댓글