본문 바로가기

Algorithm

[Udemy] 코딩테스트 초보자가 문제 해결을 위해 거쳐야 할 필수적인 5가지 단계

 

오늘은 코딩테스트와 실무에서 문제 해결을 위한 필수적인 단계에 대해 이야기하려고 한다.

이를 적용하기 위해 예시 문제를 가져왔다.

문제

문자열을 인수로 받아서 문자열에 포함된 모든 숫자, 알파벳의 갯수를 포함하는 객체를 반환하는 함수를 구현해라.

  • 갯수를 셀 때, 대소문자 구분이 없이 센다.
  • 특수문자, 공백은 포함하지 않는다.
function charCount(str) {
 // 구현하시오 
}

문제 해결 계획 수립을 위한 필수적인 단계

1. 문제 이해 하기

문제를 해결하기 위해 우선 문제를 제대로 이해하는 것이 중요하다.
문제를 제대로 이해하기 위한 단계는 다음과 같다.

1. 문제를 나만의 방식대로 생각해보기

  • 문제를 읽고 나만의 언어 혹은 내가 생각하고 이해한대로 직접 말해보거나 글로 작성해본다.

어떤 문자열이 주어지고 문자열에 포함된 모든 알파벳과 숫자의 갯수를 세야하는구나!
특수문자와 공백은 포함하지 않는구나!
대소문자 구분을 하지 않으니 대소문자 통일해서 처리를 해야겠구나!

2. 문제가 어떤 입력값을 가지고 있는가?

  • 문제에서 주어진 입력값이 어떤 입력값이고 제한되는 부분과 허용되는 부분을 생각해본다.

입력값은 모든 문자열이구나!

3. 문제가 어떤 결과값을 반환해야 하는가?

  • 문제가 반환해야하는 결과값을 정확히 이해한다.

결과값은 각 문자열의 갯수를 포함한 객체이구나!

4. 입력값을 통해 결과값을 결정할 수 있는가?

  • 주어진 입력값을 통해 결과값을 도출할 수 있는지 생각해본다.
  • 그 과정에서 어떤 힌트들이 주어졌는지 확인한다.

문자열을 객체로 반환해야하므로 객체를 만들어서 문자열이 객체에 존재하는지 확인하여 결과값을 도출할 수 있구나!

2. 구체적 예제 만들기

문제를 제대로 이해했다면 구체적인 예제를 만들어본다.

  • 간단한 예제
    • input: 'hello'
    • output: {h:1, e:1, l:2, o:1}
  • 복잡한 예제
    • input: 'Hi There!@'
    • output: {h:2, i:1, t:1, e:2, r:1}
  • 빈 입력값 예제
    • input: ''
    • output: {}
  • 유효하지 않은 입력값 예제 (문자열이 아닌 입력값이 주어졌을 경우)
    • input: [], {}, false, 1000
    • output: {}

3. 세분화 하기

예제를 바탕으로 문제 해결을 위한 단계를 세분화 한다.

function charCount(str) {
  // 결과를 담을 객체를 만든다.

  // 문자열을 순회하면서 문자를 하나씩 확인한다.
    // 문자를 소문자로 바꾼다.

    // 문자가 a-z, 0-9라면 객체에 추가한다.
          // 문자가 객체에 있다면 1을 추가한다.
        // 문자가 객체에 없다면 1을 할당한다.

  // 문자열 순회가 끝나면 객체를 반환한다.
}

4. 해결하기 및 단순화 하기

function charCount(str) {
  // 결과를 담을 객체를 만든다.
  const result = {};

  // 문자열을 순회하면서 문자를 하나씩 확인한다.
  for (let i = 0; i < str.length; i++) {
    // 문자를 소문자로 바꾼다.
      const char = str[i].toLowerCase();

    // 문자가 a-z, 0-9라면 객체에 추가한다.
    if (/[a-z0-9]/.test(char)) {
      if (result[char]) {
          // 문자가 객체에 있다면 1을 추가한다.
        result[char]++
      } else {
        // 문자가 객체에 없다면 1을 할당한다.
        result[char] = 1
      }
    }
  }
  // 문자열 순회가 끝나면 객체를 반환한다.
  return result
}

해결이 가능하다면 해결하고, 해결이 어렵다면 단순화한다.

예를들어, 대소문자 구분하는 방법을 내가 모른다면 본질에 집중하기 위해 잠시 무시하고 넘어간다.

5. 리팩터링 질문하기 ⭐️

코드가 제대로 동작하는 것만 확인하는 것만으로는 좋은 프로그래머가 될 수 없다.
리팩터링을 생각해보는 것 만으로도 다른 개발자와 차별성을 두는 장점이 될 수 있다.

리팩터링 질문은 다음과 같다.

  • 코드를 한줄씩 읽어가며 더 나은 방법(성능)으로 구현 가능한지? 더 가독성있게 작성 가능한지? 질문하기
  • 결과를 다른 방법으로 도출할 수 있는가?
  • 한눈에 보고 이해할 수 있을만큼 직관적인가?
  • 해결책 작성 시 이전 문제들과의 연관성 혹은 유사성이 있는가?
function charCount(str) {
    const result = {}

    // for문 대신 for...of 문으로 수정하여 가독성 높임
    for (let char of str) {
      // 성능이 낮은 정규표현식 대신 charCodeAt 메서드 사용 + 해당 로직 함수로 추출하여 가독성 향상
        if (isAlphaNumeric(char)) {
            char = char.toLowerCase();
            result[char] = ++result[char] || 1
        }
    }

    return result;
}

// 인수로 전달받은 문자가 알파벳인지 숫자인지 체크하는 함수
function isAlphaNumeric(char) {
    let code = char.charCodeAt(0);

    if (!(code > 47 && code < 58) && // 0-9
        !(code > 64 && code < 91) && // A-Z
        !(code > 96 && code < 123) // a-z
    ) {
        return false
    }
    return true
}

이렇게 리팩터링까지 마치고 나면 문제에 대한 이해도와 여러가지 상황에 대한 적절한 해결책을 배울 수 있다.
추후에 새로운 문제에 맞닥뜨렸을 때, 이전 문제들의 경험을 토대로 문제 해결을 위한 직관력을 높일 수 있으므로 위 단계를 거치면서 문제를 푸는 것을 훈련하자.

 

다음 시간에는 문제해결력 향상을 위한 또 다른 방법에 대해 이야기해보겠다.

참조