ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [CodeKata] wecode 4주차 코드카타
    Algorithm 2021. 1. 3. 21:24
    반응형

    🤔 서론

    코드카타도 벌써 2주차에 접어들었다. 알고리즘 문제를 반복하면서 중요하다고 느낀 2가지이다. 로직화와 예외처리.

    중복되는 구문의 반복문 처리와 함수 메서드를 통한 심플한 로직화, 다양한 예외를 처리하기 위한 방법고민을 명심하며,

    이번주에 새로 배정된 승완님과 팀업하여 코드카타를 한주간 진행하였다.


    👊 1.3(월) / 반복 조건문 + 조건에 객체활용

    📜 문제 : 로마자에서 숫자로 바꾸기

     

    1~3999 사이의 로마자 s를 인자로 주면 그에 해당하는 숫자를 반환해주세요. 로마 숫자를 숫자로 표기하면 다음과 같습니다.

    - 로마자를 숫자로 읽는 방법은 로마자를 왼쪽부터 차례대로 더하면 됩니다. III = 3 / XII = 12 / XXVII = 27 입니다.

    - 그런데 4를 표현할 때는 IIII가 아니라 IV 입니다. 뒤의 숫자에서 앞의 숫자를 빼주면 됩니다. 9는 IX입니다.

      (I는 V와 X앞에 와서 4, 9 / X는 L, C앞에 와서 40, 90 / C는 D, M앞에 와서 400, 900)

     

    🧮 풀이

    function romanToNum(s) {
      let result = 0;
      let strArr = s.split('');
    
      for (let i = 0 ; i<strArr.length ; i++) {
        const str = strArr[i]
        if (str === 'M') {
          result += 1000;
        }
        else if (str === 'D') {
          result += 500;
        }
        else if (str === 'C') {
          result += (strArr[i+1]==='D' || strArr[i+1]==='M' ? -100 : 100)
        }
        else if (str === 'L') {
          result += 50;
        }
        else if (str === 'X') {
          result += (strArr[i+1]==='L' || strArr[i+1]==='C' ? -10 : 10)
        }
        else if (str === 'V') {
          result += 5;
        }
        else if (str === 'I') {
          result += (strArr[i+1]==='V' || strArr[i+1]==='X' ? -1 : 1)
        }
      }
      
      return result;
    }

     

    🖇 리뷰

     

    모범답안이다. 내가 고민했던 글자들의 좌표화(객체키), 예외처리 모두 하나의 로직으로 간단하게 구현한 좋은 풀이이다. 

    function romanToNum(s) {
      let matching = {
          I: 1,
          V: 5,
          X: 10,
          L: 50,
          C: 100,
          D: 500,
          M: 1000
      }
      
      let strArr = s.split('');
      let numArr = strArr.map(el => matching[el]);
      let sum = 0;
      
      for (let i = 0; i < numArr.length; i++) {
          if (numArr[i] < numArr[i+1]) {
              sum += (numArr[i+1] - numArr[i]);
              i++;
          } else {
              sum += numArr[i];
          }
      }
      
      return sum;
    }
    1. 우선, 각 로마자에 매칭되는 숫자값들을 가진 객체(matching)을 선언하고, strArr를 이와 매칭한 numArr로 변환(map)
    2. 마찬가지로, sum(result)를 0으로 설정하고 모든 numArr을 반복문으로 순환하며 이를 추가한다.
    3. 예외처리는, 쉽게 numArr의 다음값과 비교하여 현재값이 작으면 4 혹은 9의 경우가 되므로 numArr[i]는 빼준다.
    4. 위처럼 예외처리를 진행한 뒤, numArr[i+1]까지 더해주고 i++를 한 번 더 진행하여 해당 케이스를 완료하였다.

    👊 1.4(화) / 반복 조건문 + 객체 누적

    📜 문제

     

    숫자로 이루어진 배열인 nums를 인자로 전달합니다. 숫자중에서 과반수(majority)가 넘은 숫자를 반환해주세요.

    예를 들어,

    nums = [3,2,3] => return 3

    nums = [2,2,1,1,1,2,2] => return 2

     

    🧮 풀이

    function moreThanHalf(nums) {
      let obj = {};
      for (let val of nums) {
        obj[val] = !obj[val] ? 1 : obj[val]+=1 
      }
      for (let key in obj) {
        if (obj[key] >= nums.length/2) {
          return Number(key)
        }
      }
    }
    1. nums 숫자 배열의 각 값들을 key로 가지는 객체(obj)를 만든다. 배열을 한 번 순회하며 여기에 개수를 누적한다.
    2. 다시, 이 obj 객체를 순회하며, 객체의 키값(=갯수)이 과반수 이상인 key를 return 해주면 된다. (String -> Number)

     

    🖇 리뷰

     

    배열을 순회 혹은 이중순회하며 count를 누적하고 과반수와 비교하는 알고리즘도 있으나, 객체에 저장시킨 내 방법이 맘에 들었다.

    만약 위 방법을 쓴다면, 이중순회보다는 배열을 sort() 한 뒤 1번의 순회로 누적비교 하는게 효율적일 것 같다.


    👊 1.5(수) / 반복 조건문 + String 처음-끝 비교

    📜 문제

     

    s는 여러 괄호들로 이루어진 String 인자입니다. s가 유효한 표현인지 아닌지 true/false로 반환해주세요.

    종류는 '(', ')', '[', ']', '{', '}' 으로 총 6개 있습니다. 아래의 경우 유효합니다.

     

    한 번 괄호를 시작했으면, 같은 괄호로 끝내야 한다. 괄호 순서가 맞아야 한다. 예를 들어 아래와 같습니다.

    s = "()" => return true

    s = "()[]{}" => return true

    s = "(]" => return false

    s = "([)]" => return false

    s = "{[]}" => return true

     

    🧮 풀이

    function isValid(s) {
      let sArr = s.split('');
      let len = sArr.length;
      let conArr = ['[', '{', '(', ')', '}', ']'];
      
      for (let i = 0 ; i < len ; i++) {
        if (conArr.indexOf(sArr[i]) + conArr.indexOf(sArr[i+1]) === 5) {
          sArr.splice(i,2)
        }
        if (conArr.indexOf(sArr[len-i-1]) + conArr.indexOf(sArr[len-i-2]) === 5) {
          sArr.splice(len-i-2, 2)
        }
      }
      return !sArr.length ? true : false
    }
    1. 우선, s(괄호 문자열)를 배열로 split 처리했다. len은 이 길이를 간략하게 사용하기 위해 선언한 변수다.
    2. 비교로직은 conArr를 기반으로 한다. 둘의 index 합이 5면 괄호 페어링이 정상이라는 비교문을 사용했다.
    3. sArr 배열을 순회하는데, 처음과 끝 양쪽에서 순회하며 바로 옆의 괄호와 비교한다. 페어링이 정상이면 이 쌍을 배열에서 splice 한다.
    4. 겹괄호( [{()}] ) 는 정방향에서 제일 안쪽을 splice 하면, 그 다음 페어부턴 역방향에서 순차적으로 splice가 가능하다.

    🖇 리뷰

     

    정석풀이는 아래처럼 순회하며, 여는 괄호는 check 배열에 누적(push) / 닫는 괄호가 나오면 check 배열 제일 끝의 걸 뺀 뒤 비교(pop)

    function isValid(s) {
      let check = [];
      let pars = {
        '(': ')',
        '[': ']',
        '{': '}'
      }
      for (i = 0; i < s.length; i++) {
        let char = s[i];
        if (char === '(' || char === '[' || char === '{') {
          check.push(pars[char]);
        } else {
          let last = check.pop();
          if (char !== last) return false;
        }
      }
     return check.length === 0 ? true : false;
    

    👊 1.6(목) / 객체 누적 + Array 메서드(sort, map, slice)

    📜 문제

     

    nums는 숫자로 이루어진 배열입니다. 가장 자주 등장한 숫자를 k 개수만큼 return해주세요.

     

    nums = [1,1,1,2,2,3] , k = 2 => return [1,2]

    nums = [1], k = 1 => return [1]

     

    🧮 풀이

    function topK(nums, k) {
      let countObj = {};
      
      for (let val of nums) {
        !countObj[val] ? countObj[val] = 1 : countObj[val]++;
      }
      let countArr = Object.entries(countObj).sort((a,b) => b[1] - a[1]).map(ele => Number(ele[0]))
      return countArr.slice(0, k)
    }
    1. 이전 문제와 유사하다. 우선 for-of 순회를 하면서 각 숫자(key)의 개수(value)를 저장한다.
    2. 이를, 배열화(entries), 내림차순 정렬(sort), 숫자(key)로만 재구성(map), 필요한 길이만큼 자르기(slice) 메서드들을 적용했다. 

    🖇 리뷰

     

    알고리즘에 대해 자부심이 생긴 하루였다. 모든 풀이들 중 내 풀이가 가장 간결한 것 같아 매우 뿌듯했다.


    👊 1.7(금) / 이중순회(순열)

    📜 문제

     

    인자인 height는 숫자로 이루어진 배열입니다. 그래프로 생각한다면 y축의 값이고, 높이 값을 갖고 있습니다.

    아래의 그래프라면 height 배열은 [1, 8, 6, 2, 5, 4, 8, 3, 7] 입니다.

    저 그래프에 물을 담는다고 생각하고, 물을 담을 수 있는 가장 넓은 면적의 값을 반환해주세요.

    🧮 풀이

    function getMaxArea(height) {
      let maxArea = 0;
      for (let i = 0 ; i < height.length ; i++) {
        for (let j = i+1 ; j < height.length ; j++) {
          const area = Math.min(height[i], height[j]) * (j-i)
          if (area > maxArea) {
            maxArea = area
          }
        }
      }
      return maxArea;
    }
    1. 이중순회를 하며, 각 경우의 넓이를 area로 계산했다. (이중순회 시, 위처럼 전후중복이 안되면 순열, 중복되면 조합으로 칭하겠다.)
    2. 높이는 두 값중 작은 값을 Math.min() 으로 뽑는다.(그림처럼 담겨야하므로) 너비는 인덱스 값 차이로 계산했다.(j-i)
    3. maxArea와 비교하면서, 클 경우 이를 최신화하는 조건문으로 각 이중순회가 마무리된다.

    🖇 리뷰

     

    오늘은 상대적으로 문제가 어렵진 않았고, 또 Math.min() 메서드를 활용해 코드를 효율적으로 줄여 뿌듯하다.

    점점 알고리즘 사고가 증진된다는 착각과도 같은 자부심이 생기고 있당! 😃😃

    반응형
Designed by Tistory.