포스트

JS 빈도수 세기 패턴

jslogo

빈도수 세기 패턴

  • Object 또는 Set을 사용하여 특정 문자열, 배열의 값 또는 값의 빈도를 수집해서 푸는 패턴
  • 이 패턴은 때로, 중첩 루프 또는 O(N^2) 의 배열 / 연산 을 우회할 수 있다.

문제1

  • same 이라는 함수를 작성해라
  • 이 함수는 배열 2개를 인자로 받는다.
  • 1번째 배열의 요소의 제곱이 2번째 배열의 요소로 존재하면서, 그 빈도까지 같다면 True를 반환
  • 그것이 아닐 경우에는 false 를 반환

테스트 케이스

1
2
3
same([1, 2, 3], [4, 1, 9]); // true
same([1, 2, 3], [1, 9]); // false
same([1, 2, 1], [4, 4, 1]); // false, 이경우 1의 제곱이 두번 들어가 있지 않기 때문에 false

나의 해답 - 오답

1
2
3
4
5
6
7
8
9
10
11
function same(arr1, arr2) {
  arr2.sort((a, b) => a - b);
  const squareArr = arr1.map((x) => Math.pow(x, 2)).sort((a, b) => a - b);
  squareArr.forEach((i, idx) => {
    if (i !== arr2[idx]) {
      console.log(false);
      return;
    }
    console.log(true);
  });
}

해답 1 - O(n^2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function same_solution(arr1, arr2) {
  // 갯수가 일치하지 않으면 아예 false
  if (arr1.length !== arr2.length) {
    return false;
  }

  // 1번째 배열을 순회하면서 조회
  for (let i = 0; i < arr2.length; i++) {
    // N
    // 1번째 배열의 제곱 값을 가진 arr2의 인덱스를 찾는다.
    let correctIndex = arr2.indexOf(Math.pow(arr1[i], 2)); //N
    // 반환된 인덱스가 -1 일때 false
    if (correctIndex === -1) {
      return false;
    }
    // 정상 인덱스 반환시 인덱스 기준으로 값을 하나씩 제거 한다.
    arr2.splice(correctIndex, 1);
  }
  // 최종 true 반환
  return true;
}

해답 2 - O(n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function same(arr1, arr2) {
  if (arr1.length !== arr2.length) {
    return false;
  }
  let frequencyCounter1 = {};
  let frequencyCounter2 = {};
  for (let val of arr1) {
    frequencyCounter1[val] = (frequencyCounter1[val] || 0) + 1;
  }
  for (let val of arr2) {
    frequencyCounter2[val] = (frequencyCounter2[val] || 0) + 1;
  }
  console.log(frequencyCounter1);
  console.log(frequencyCounter2);
  for (let key in frequencyCounter1) {
    if (!(key ** 2 in frequencyCounter2)) {
      return false;
    }
    if (frequencyCounter2[key ** 2] !== frequencyCounter1[key]) {
      return false;
    }
  }
  return true;
}

문제 2

두 개의 문자열이 주어졌을 때, 두 번째 문자열이 첫 번째 문자열의 애너그램인지 확인하는 함수를 작성합니다. 애너그램은 다른 글자의 글자를 재배열하여 형성된 단어, 구 또는 이름입니다. (예시: cinema -> iceman)

테스트 케이스

1
2
3
4
5
6
7
8
validAnagram('', '') // true
validAnagram('aaz', 'zza') // false
validAnagram('anagram', 'nagaram') // true
validAnagram("rat","car") // false) // false
validAnagram('awesome', 'awesom') // false
validAnagram('amanaplanacanalpanama', 'acanalmanplanpamana') // false
validAnagram('qwerty', 'qeywrt') // true
validAnagram('texttwisttime', 'timetwisttext') // true

나의 해답 - O(N)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function validAnagram(str1, str2){
    // if str1 === str2 return true
    if(str1 === str2) {
        return true;
    }
    // change lower case string
    str1 = str1.toLowerCase();
    str2 = str2.toLowerCase();
    
    // create frequency counter each string arguments
    let freqCounter1 = {};
    let freqCounter2 = {};
    // Get Count From Each String
    
    // count str1's char
    for (let char1 of str1) {
        freqCounter1[char1] = (freqCounter1[char1] || 0) + 1;
    }
    
    // count str2's char
    for (let char2 of str2) {
        freqCounter2[char2] = (freqCounter2[char2] || 0) + 1;
    }
    
    // compare str1's keys in str2
    for (let key in freqCounter1) {
        // str1's key not in freqCounter 2 return false
        if(!(key in freqCounter2)) {
            return false;
        }
        
        // freqCounter1[key]'s value not Equals freqCounter2[key]'s value return false
        if(freqCounter1[key] !== freqCounter2[key]) {
            return false;
        }
    }
    
  return true;
}

해답 - O(N)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function validAnagram(first, second) {
  if (first.length !== second.length) {
    return false;
  }

  const lookup = {};

  for (let i = 0; i < first.length; i++) {
    let letter = first[i];
    // if letter exists, increment, otherwise set to 1
    lookup[letter] ? lookup[letter] += 1 : lookup[letter] = 1;
  }
  console.log(lookup)

  for (let i = 0; i < second.length; i++) {
    let letter = second[i];
    // can't find letter or letter is zero then it's not an anagram
    if (!lookup[letter]) {
      return false;
    } else {
      lookup[letter] -= 1;
    }
  }

  return true;
}

배운점

  • 빈도수를 세는 과제에서는 객체하나를 두고 그안에 추가하여 Counting 하는것이 확실히 이상적인것 같다.

강의

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.