본문 바로가기
코딩 생활/코딩테스트

[프로그래머스 | JS] 레벨 0. 특이한 정렬 (feat. sort())

by everyhahaha 2023. 4. 14.

❓ 문제: 특이한 정렬

 

정수 n을 기준으로 n과 가까운 수부터 정렬하려고 합니다. 이때 n으로부터의 거리가 같다면 더 큰 수를 앞에 오도록 
배치합니다. 정수가 담긴 배열 numlist와 정수 n이 주어질 때 numlist의 원소를 n으로부터 가까운 순서대로 정렬한 
배열을 return하도록 solution 함수를 완성해주세요.

📝 문제 풀이

배열의 메서드와 삼항연산자를 이용해서 바로 결과값을 리턴하도록 풀었다.

인자로 넘어오는 numlist가 오름차순으로 정렬된 상태가 아니여서

맨 위의 sort을 안하면 테스트 2번에서 통과가 안돼서 sort를 두 번이나 쓴 게 과연 최선일까 고민했었다.

(그리고 점수도 1점밖에 못 받아서.... 그닥 휼륭한 풀이라고는......)

 

// 나의 풀이
function solution(numlist, n) {
    return numlist
            .sort((a, b) => a - b)
            .map(value => [value, Math.abs(n - value)])
            .sort((a, b) => (a[1] === b[1] ? (a[0] > b[0] ? b[0] - a[0]: a[0] - b[0]) : a[1] - b[1]))
            .flatMap(x => x[0])
}

 

더 아름답고 효율적인 코드를 기대하며 설레는 마음으로 다른 풀이를 살펴봤다.

 

function solution(numlist, n) {
  return numlist.sort((a, b) => Math.abs(a - n) - Math.abs(b - n) || b - a);
}

와......나도 Math.abs까지는 생각했는데....

나름 sort 메서드에 대해서 잘 다룬다고 생각했는데

이번 기회에 더 깊게 알아보고자 한다.


🔍 더 파고들기 - sort()

 

흔히   sort((a, b) => a - b)  는 오름차순,  sort((a, b) => b - a) 는 내림차순으로 사용했다.

이 때 동작하는 원리를 더 깊이 살펴보면 저 코드에 대해 이해할 수 있다.

mdn 사이트에서 sort 메서드에 대해 더 차근히 알아보자


1. 형태

전달인자가 비교함수밖에 없어 비교적(?) 간단한 메서드이다.

정렬한 배열을 반환하는데, 복사본이 만들어지는 게 아닌 원본 배열도 정렬되는 점 참고하자.

 

arr.sort([compareFunction])
  • compareFunction / 비교함수 (선택사항)
    정렬 순서를 정의하는 함수.
    생략하면 배열은 각 요소의 문자열 변환에 따라 각 문자의 유니 코드 코드 포인트 값에 따라 정렬됩니다.
    외부에 별도로 함수를 정의해서 호출하는 방식도 있지만 애로우 함수로 괄호 안에 쓰면 보기 더 편하다.

 

여기서 재밌는 점은, 비교함수의 매개변수 a는 배열의 홀수번째 요소를 전달받는다.

예를 들어  [4, 2, 1, 7].sort((a, b) => a - b)  에서 a는 첫번째 요소로 2를 전달받는다.

 

[4, 2, 1, 7].sort((a, b) => console.log(a, b))
// 2 4
// 1 2
// 7 1

 

 

예전부터 궁금해서 여기저기 알아본 결과 성능을 위해서 이렇게 동작한다고 한다.

 

sort 메서드는 내부적으로 정렬 알고리즘을 사용하는데,

비교함수를 가지고 배열의 2개의 요소를 비교하고 교환하는 과정을 반복하면서 배열을 정렬한다.

비교함수가 배열의 첫번째 요소부터 전달받는다면 중복 호출되는 경우가 생기는데

만약 배열의 요소가 많아질수록 비교함수가 호출 횟수도 같이 늘어나 비효율이 증가한다.

 

그래서 비교함수의 호출을 최소화하기 위해 첫 번째 요소와 두 번째 요소를 비교한 뒤에는 

첫 비교 결과를 고정하고 두 번째 요소부터 비교하는 식으로 동작한다. 

요소의 순서가 정렬에 영향을 주진 않지만 내가 생각하는 방향으로 코드가 실행되지 않을 때 도움을 주는 경우가 있다. 

 

형태를 살펴봤으니 이제 어떤 방식으로 동작하는지 알아보자.

비교함수를 기준으로 크게 2가지 방식으로 작동된다.


2. 작동방식

 

① sort()는 비교함수 없이 실행시키면 문자열(유니코드)로 변환해서 정렬시킨다.

숫자로 비교하는 게 아니라 문자열로 판단하기 때문에 앞자리의 숫자만 비교해서 정렬한다.

[10, 3, 20, 1].sort();      // [1, 10, 20, 3]

 

② sort()는 비교함수를 전달하면 함수의 반환값을 기준으로 정렬시킨다.

sort((a, b) => a - b)처럼 비교함수로 (a, b) => a - b를 전달했다고 가정해보자. 

그리고 비교함수의 결과값에 따라 3가지 방식으로 정렬된다. 

 

  •  비교함수의 반환값이 '음수' 경우  → a가 앞에
    a가 b보다 작다고 판단해 a를 b보다 앞에 위치시킨다 

  •  비교함수의 반환값이 '양수'인 경우b가 앞에
    b가 a보다 작다고 판단해 b를 a보다 앞에 위치시킨다.

  •  비교함수의 반환값이 '0'인 경우 순서변경 X
    a와 b의 값이 같다고 판단해 순서가 바뀌지 않는다.

 

mdn 사이트에도 설명이 있는데 저는 바로 이해가 안돼서 풀어서 설명해봤어요 https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/sort


그럼 다시 그 아름다운 코드로 돌아가보자.

function solution(numlist, n) {
  return numlist
  		.sort((a, b) => 
        
        	// (a - b)이기 때문에 오름차순으로 정렬한다.
            	// 각 요소에서 n을 뺀 절대값을 기준으로 오름차순으로 정렬
        	Math.abs(a - n) - Math.abs(b - n) || 
            
            	// or을 썼기 때문에 앞의 비교문이 0이면
                // 즉, 두 요소가 n과의 거리가 같다면 두 요소끼리 비교해서
            	// 내림차순(b - a)으로 정렬한다.
        	b - a
        );
}

 

작동원리를 알고보니 이제야 이해가 된다. 너무 후련하다.

이해가 어렵다면  (a, b) => a - b 오름차순이니까  (a, b) => b - a 는 순서를 뒤바꿨으니 내림차순이라고 외우고

차근차근 알아가는 것도 좋은 것 같다.

(제가 그 케이스라...)


현재 랭킹 20,017위...계속 가보자!

모두 즐코하세요-!

 

참고자료

댓글