알고리즘 문제 풀이

[LeetCode] 954. Array of Doubled Pairs

_OB1N 2021. 8. 12. 13:41

문제 출처: https://leetcode.com/problems/array-of-doubled-pairs/

 

Array of Doubled Pairs - LeetCode

Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.

leetcode.com


문제

짝수 길이의 정수 배열 arr 이 주어지면, 모든 0 <= i < len(arr) / 2 i 에 대해서 arr[2 * i + 1] = 2 * arr[2 * i] 의 형태로 재정렬할 수 있다면 true 를 반환하고, 그렇지 않다면 false 를 반환하라.

 

예제

Input: arr = [3,1,3,6]
Output: false
Input: arr = [4,-2,2,-4]
Output: true

Explanation: We can take two groups, [-2,-4] and [2,4] to form [-2,-4,2,4] or [2,4,-2,-4].
Input: arr = [1,2,4,16,8,4]
Output: false

풀이

재정렬된 배열의 조건을 나열하면 다음과 같다.

 

arr[1] = 2 * arr[0]

arr[3] = 2 * arr[2]

arr[5] = 2 * arr[4]

arr[7] = 2 * arr[6]

...

 

배열(arr)에서 가장 작은 값은 위와 같이 표현했을 때, 등호의 오른쪽에 있을까 아니면 왼쪽에 있을까?

 

배열에서 가장 작은 값의 경우, 자신보다 작은 값이 배열 내에 없기 때문에 등호의 왼쪽에 존재할 수가 없다. 즉, 오른쪽에 위치하여 본인보다 2배 큰 값과 매칭 될 것이다. 그렇다면, 두 번째로 작은 값은 어떠할까? 이미 가장 작은 값이 매칭 되었기 때문에 두 번째로 작은 값 역시 자신의 2배 큰 값과 바로 매칭 시킬 수 있을 것이다.

 

이처럼 가장 작은 값부터 시작하여 탐욕적(Greedy)인 방식으로 문제를 접근해 볼 수 있다.

 

우선 배열 내 각 값이 몇 번 등장하는지 카운트한다. 이 카운트 값은, 이후 과정에서 어떤 값이 이미 다른 값과 매칭이 되어있는지 여부를 체크하기 위함이다.

const numCounter = {};

arr.forEach(num => {
    if (!numCounter[num]) {
        numCounter[num] = 0;
    }
    numCounter[num] += 1;
});

Greedy하게 접근하기 위해서 주어진 arr을 오름차순으로 정렬할 차례이다. 이때 한 가지 생각해볼 것은 arr 안에 존재하는 중복된 값을 고려해야 하는가 이다.

 

결론은 중복된 값은 하나로 처리하는 것이 효율적이다. 예를 들어 arr = [2, 1, 1, 2] 인 경우를 생각해보자.

arr = [2, 1, 1, 2]

1. 중복을 허용할 경우
  sortedArr = [1, 1, 2, 2]
  - 중복 숫자의 구분을 위해 다음과 같이 표시: sortedArr = [1(1), 1(2), 2(1), 2(2)]
  - 정렬된 배열을 순차적으로 돌면서 숫자들을 매칭해 나감
      1) 1(1) => 2(1) 매칭
      2) 1(2) => 2(2) 매칭
  - 총 2번의 매칭 과정이 수행

2. 중복 값을 없앤 경우
  sortdArr = [1, 2]
  - 정렬된 배열을 순차적으로 돌면서 숫자들을 매칭해 나감
      1) 1 => 2 매칭
  - 총 1번의 매칭 과정이 수행

이러한 예시처럼 중복 값을 처리하면 반복 횟수를 줄여 실행 시간을 조금이라도 단축시킬 수 있다.

 

숫자들을 매칭해 나가기 전에 마지막으로 한 가지 케이스만 더 고려해보자. Greedy하게 접근하기 위해서 위 예시처럼 정렬시킨 배열을 사용할 수 있는데, 배열에 음수가 포함되어 있는 경우를 생각해야 한다.

 

만약 arr = [-2, -4] 라고 하면, 이 arr을 정렬하면 [-4, -2]가 된다. 문제는 이때 맨 앞의 원소를 선택하여 x2를 하게 되면 -8이 된다는 것이다. 양수와 달리 음수는 절댓값의 결과가 작을수록 실제 값이 크기 때문이다. 이를 고려해서 만약 값이 음수라면 x2가 아니라 /2를 취하여 매칭 시키도록 해야 한다.

 

위 내용과 더불어 몇 가지 종료 조건을 추가하면 다음과 같은 코드를 통해서 탐욕적(Greedy)하게 숫자들을 매칭 시킬 수 있다.

let answer = true;

const sortednum = new Set(arr.sort((a, b) => a - b));
for (let num of sortednum) {
    if (!numCounter[num]) { continue; }

    const isMinus = num < 0;
    if (isMinus && num % 2) { 
        answer = false; 
        break; 
    }

    let next = isMinus ? num / 2 : num * 2;
    if (!numCounter[next]) { 
        answer = false; 
        break; 
    }
	
    numCounter[num] = 0;
    numCounter[next] -= numCounter[num];
    if (numCounter[next] < 0) { 
        answer = false; 
        break; 
    }
}

 

 

Github: 954.js

 

GitHub - opwe37/Algorithm-Study

Contribute to opwe37/Algorithm-Study development by creating an account on GitHub.

github.com