코딩/Javascript

[JS] 이중 반복문 성능 개선 방법

junhub 2023. 3. 17. 17:20
function checkSame(arr1, arr2) {
  let counter1 = {};
  let counter2 = {};
    
  for (let item of arr1) {
    counter1[item] = (counter1[item] || 0) + 1;
  }
    
  for (let item of arr2) {
    counter2[item] = (counter2[item] || 0) + 1;
  }

  for (let key in counter1) {
    if (!(key in counter2)) {
      return false;
    }
    if (counter2[key] !== counter1[key]) {
      return false;
    }
  }
  return true;
}

console.log(checkSame([3, 5, 7, 8, 9], [3, 7, 9, 8, 5])); // true
console.log(checkSame([1, 2, 3, 4], [4, 3, 2, 1])); // true
console.log(checkSame([1, 2, 3, 4], [4, 3, 2, 1, 1])); // false

 

이중 반복문의 성능 개선을 위한 코드이다. 위와 같은 코드를 사용하면 이중 반복문 사용 시 예를 들어 10000번 * 10000번 = 100000000번 반복할 코드를 10000번 + 10000번 = 20000번만 코드를 돌려 결과를 출력해낼 수 있다. 

 

변수 counter1 과 counter2에 각각 빈 객체를 할당하고, 반복문을 사용해 arr1의 배열을 item으로 차례로 할당하면서 해당 item이 counter1 객체에 들어있는지 확인한다. 만약 등록되어있지 않다면, 새로운 키 'item'을 counter1 객체에 추가하고 그 값을 1로 초기화한다.

 

만약 arr1 에 들어온 값이 ['a', 'b', 'a'] 라면 counter1['a']  => { a :   }의 형태가 되고 우변의 괄호 안의 식인 counter1['a'] || 0 이 실행된다. 이때 a 에는 value 값이 없으므로 or 연산자에 의해 0이 되고 괄호 밖에 있는 + 1 에 의해 { a : 1 } 의 형태가 된다 

마찬가지로 conuter1['b'] => { b :  }의 형태가 되고 우변의 counter['b'] || 0 의 식이 실행된다. 이때 b에도 value 값이 없으므로 or 연산자에 의해 0이 되고 괄호 밖 +1에 의해 { b : 1 } 의 형태가 된다.

마지막으로 counter1['a'] 가 들어오게 되면 우변의 식 counter1[ 'a' ]는 => { a : 1 } 의 값이 이미 counter1 안에 있다. 

따라서 counter1['a'] 는 true이기 때문에 괄호 안 논리연산자의 결과는 1이 되고 괄호 밖 + 1 에 의해 2가 된다. 

추가적으로 만약 counter1 객체 안에 { a : 10 } 이라는 값이 이미 존재하고 또 다시 a가 item으로 들어오게 된다면 

우변의 counter['a'] 는 { a : 10 } 즉 , 10을 의미하므로 10 + 1 = 11이 된다. 

이와 같은 원리로 두 배열의 중복 요소를 카운트하는 것이다. 

 

여기까지 진행된 후 counter1 = { '3' : 1, '5' : 1, '7' : 1, '8' : 1, '9' : 1 }   

                             counter2 = { '3' : 1, '5' : 1, '7' : 1, '8' : 1, '9' : 1 }   가 된다. 

for (let key in counter1) {
    if (!(key in counter2)) {
      return false;
    }

그 후 이러한 코드가 나오는데 counter1에서 key라는 변수로 값을 넘겨받아 만약 counter2에서 값을 넘겨받은 key와 비교했을 때 

다르다면 false를 리턴한다. 이때 key 값을 console을 찍어보게 되면 3, 5, 7, 8, 9 가 나오게 된다. 즉, key 값만 넘겨받은 것이다.

 

if (counter2[key] !== counter1[key]) {
      return false;
    }

 또한번의 조건문을 활용해서 비교하게 되는데 이땐 counter2[key] 즉, value 값을 불러와서 비교하는 것이다. 

console을 찍어보면 1, 1, 1, 1, 1 이 나오게 된다. 

 

모두 통과하게되면 true를 리턴하게 된다. 

 


function sameString(first, second) {
  if (first.length !== second.length) {
    return false;
  }

  const lookup = {};

  for (let i = 0; i < first.length; i++) {
    let letter = first[i];
    lookup[letter] ? (lookup[letter] += 1) : (lookup[letter] = 1);
  }

  for (let i = 0; i < second.length; i++) {
    let letter = second[i];
    if (!lookup[letter]) {
      return false;   
    } else {
      lookup[letter] -= 1;
    }
  }

  return true;
}

console.log(sameString("anagram", "nagaram"));

먼저 이 코드는 하나의 변수에만 객체를 할당해서 문제를 해결하는 코드이다. 

1차적으로 각 매개변수의 길이를 확인하여 일치여부를 확인한다. 그 후 반복문을 돌면서 letter에 a n a g r a m 이 차례로 할당된다.

lookup[a] ? (lookup[a] += 1) : (lookup[a] = 1) 

이 식은 lookup[a]가 true 라면 (lookup[a] += 1) 실행, 값이 없다면 (lookup[a] = 1)이 실행된다. 

만약 a가 처음 들어가게 되면 lookup[a]는 값이 없으므로 (lookup[a] = 1)이 실행된다. 

그 후 a가 다시 들어가게 되면 lookup[a]은 true가 되므로  + 1이 된다. 이러한 원리로 카운트가 진행된다. 

 

두번째 반복문에서는 변수 letter에 second[i]가 들어가게 된다. 그 후 조건문을 활용하여 lookup[letter] 이 false 라면 false를 리턴하고 조건에 충족하면 true를 리턴한다.