[Leetcode] Last Stone Weight / Find Lucky Integer in an Array

dosilv·2021년 5월 5일
0
post-thumbnail

🥑 Last Stone Weight

We have a collection of stones, each stone has a positive integer weight.

Each turn, we choose the two heaviest stones and smash them together. Suppose the stones have weights x and y with x <= y. The result of this smash is:

If x == y, both stones are totally destroyed;
If x != y, the stone of weight x is totally destroyed, and the stone of weight y has new weight y-x.

At the end, there is at most 1 stone left. Return the weight of this stone (or 0 if there are no stones left.)

돌맹이들 중에 무거운 돌맹이 두개를 뿌수고... 거기서 부러진 돌맹이+남은 돌맹이들 중에 또 무거운 돌 두 개를 뿌수고.... 암튼 반복해서 마지막 남은 돌의 무게를 구하는ㅠ 이상한 문제


정답 🥳

var lastStoneWeight = function(stones) {
    for (let i=0; stones.length>0; i++) {
        if (stones.length === 1) {
            return stones[0];
        }
        stones = stones.sort((a, b) => {
            if (a>b) return -1;
        });
        if (stones[0] === stones[1]) {
            stones.splice(0,2);
        } else {
            const [a, b] = stones.splice(0, 2);
            stones.push(a-b)
        }
    }
    return 0;
};

먼저 sort를 이용해 돌맹이들을 무거운 순으로 정렬해주고, stones[0]과 stone[1]의 무게가 같은 경우/다른 경우로 나누어서 코드를 작성했다. 같으면 둘 다 그냥 없어지니까 splice로 빼버리고, 다르면 splice로 뺀 걸 a, b라는 변수에 담아서 그 차이를 stones에 push했다. 돌이 안 남을 때까지 반복... 먼가 엄청 지저분하게 푼 기분😟


다른 solution 😮

var lastStoneWeight = function(stones) {
    let size = stones.length;
    while(size >= 2){
        stones.sort((a,b)=> { return a-b })
        x = stones.pop(); 
        y = stones.pop();
        if(x == y) {
            size -= 2;
        } else {
            size -= 1; 
            stones.unshift(x - y); 
        }
    }
    return stones;
};

나는 내림차순으로 정렬을 했는데, 여기선 오름차순으로 정렬을 해서 pop을 이용했다. pop을 잘 쓰면 요소 비교 후 굳이 splice를 따로 하지 않아도 배열에서 이미 제거가 된 상태니까 유용한 것 같음!

근데 왜 stones.length를 굳이 size라는 변수에 담아서 직접 -2, -1을 한 거지? 싶어서 size대신 stones.length를 넣고, size -= 2, size -= 1 부분 코드를 지우고 실행해 봤다. 결과는 똑같이 통과인데 코드는 더 짧아졌지만 runtime은 더 늘어났다. 왤까....? while문을 돌 때마다 stones.length를 체크하는 시간이 길어서 그런가🤔 알 수 없는 알고리즘의 세계...

그리고 이 코드를 보고 내 답을 보니 나도 반복을 위해 for문을 쓰긴 했지만 변수 i를 아예 사용하지 않고 있다는 걸 깨달았다. 나도 while문으로 바꾸는 게 좋겠당.

그래서 정리한 후의 코드!

var lastStoneWeight = function(stones) {
    while(stones.length >= 2){
        stones.sort((a,b)=> { return a-b })      
        x = stones.pop(); 
        y = stones.pop();   
        if(x !== y) {
            stones.unshift(x - y);
        }
    }
    return stones;
};

배운 것 💪✨

🍒 숫자 배열에 sort 적용하기

배열의 요소가 모두 숫자일 경우 sort 메서드를 쓰면 자동적으로 오름차순 정렬이 되는 줄 알았다. 왜냐면...

const nums = [6, 1, 0, 3, 2, 4];
nums.sort();
console.log(nums);
//결과는 [ 0, 1, 2, 3, 4, 6 ]

이렇게 하면 잘 되니까! 근데 그게 아니었다. nums Array에 10, 20을 추가하고 다시해 보면...

const nums = [6, 1, 0, 3, 2, 4, 10, 20];
nums.sort();
console.log(nums);
//결과는 [ 0, 1, 10, 2, 20, 3, 4, 6 ] 엥....?!

저렇게 뒤죽박죽 나온다.
알고보니 숫자를 string처럼 인식해서 유니코드 같은 값을 기준으로...? 정렬하는 것 같다. 그래서 첫 번째 숫자가 1인 10이 2보다 더 앞에, 2인 20이 3보다 더 먼저 나오는 것. 결국 자릿수가 다르면 원하는 결과를 얻기 힘들다는 뜻!

결국 sort의 인자로 콜백함수를 줘야 하는데,
Array.sort((a,b) => { return a-b })
이렇게 하면 간단히 오름차순 정렬, 반대로 return값을 b-a로 주면 내림차순으로 정렬시킬 수 있다!

🍒 Array의 '==' & '==='

또 알게 된 새롭고 재밌는 사실. 분명 남은 stone의 weight(=number)를 리턴하라고 했는데, stones(=Array)를 그대로 리턴했는데도 정답처리가 되었다. 왜지? 하고 이것저것 찍어 봤는데 type 비교를 하지 않는 ==의 특성 때문인 듯!

'3' == 3;	//얘가 true인 것처럼,
[3] == 3;	//얘도 true다.
[] == 0;	//심지어 얘도 true! (false == false니까)

물론 == 대신 ===로 비교하면 모두 false가 나온다.

근데 더 신기한 건!

[1] == [1];
['a', 'b'] == ['a', 'b'];

위의 조건들은 다 false로 나옴😱😱😱 안에 요소도 같고 type도 같은데.... 이건 Array의 참조(reference) 특성와 관련된 것 같다. 모양은 같지만 각 Array의 메모리 주소가 다르기 때문에, 배열끼리 비교할 경우 그 주소를 비교해서 true/false를 판단하나 보다. 신기신기~~



🥑 Find Lucky Integer in an Array

Given an array of integers arr, a lucky integer is an integer which has a frequency in the array equal to its value.

Return a lucky integer in the array. If there are multiple lucky integers return the largest of them. If there is no lucky integer return -1.

Example 1:
  Input: arr = [2,2,3,4]
  Output: 2
  Explanation: The only lucky number in the array is 2 because frequency[2] == 2.

숫자로 이루어진 배열의 요소 중 요소의 실제 값과, 해당 배열에 들어있는 개수가 동일한 숫자를 찾아 리턴하는 문제.

정답 🥳

var findLucky = function(arr) {
    let obj = {};
    ans = [];
    for (i of arr) {
        obj[i]? obj[i] += 1 : obj[i] = 1;
        }
    for ([n, m] of Object.entries(obj)) {
        if(n == m) ans.push(m);
    }
    return ans.length? Math.max(...ans) : -1;
};

우선 요소를 key로, Array내 요소의 개수를 value로 하는 오브젝트를 만들었다. 그리고 Object.entries()를 이용해 key와 value를 n, m에 구조분해할당해주고, n과 m이 같은 요소를 찾아냈다.
처음에는 그걸 바로 리턴시켰었는데, 만약 그런 요소가 2개 이상일 경우 그 중 큰 수를 리턴하라는 조건이 있어서 모든 ans를 배열에 담고, Math.max()로 최댓값을 리턴하도록 했다.

배운 것 💪✨

🍒 Object.entries()

object의 각 요소를 [key, value] 형태로 담은 배열을 반환한다.
예를 들어,

const obj = {a:1, b:2, c:3};

이런 오브젝트가 있을 때

Object.entries(obj);

얘의 결과는

[ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]

가 된다. (obj가 바뀌는 게 아니라 저 코드 자체가)

그래서 for-of문 & 구조분해할당을 이용해서

for [x, y] of Object.entries(분해할 오브젝트) {
  ...
}

이런 형태로 유용하게 쓸 수 있음!

profile
DevelOpErUN 성장일기🌈

0개의 댓글