ABOUT ME

Today
Yesterday
Total
  • [LeetCode] 954. Array of Doubled Pairs
    알고리즘 문제 풀이 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

     

    댓글

Designed by Tistory.