본문 바로가기

Problem Solving/나의 생각

LeetCode 알고리즘 PS에 대한 생각

릿코드(리트코드, LeetCode)에도 많은 문제들이 있고, 특히 해외에서는 상당히 보편적으로 사용하는 플랫폼으로 알고 있다. 릿코드의 시스템에도 장단점이 있는데 몇가지만 짚어보면,

 

  • 문제를 틀렸을 경우, 어떤 테케에서 틀렸는지 공개된다.

이 점은 시간단축의 효과(장점)가 있다. 어디서 잘못했는지 금방 알 수 있다. 때때로 정말 작은 실수때문에 며칠이 걸리기도 한다. 그런 실수, 그런 코너케이스까지 생각해내야 하는 것도 맞지만 그것으로 너무 많은 시간을 뺏기는 것도 문제가 있다. 실제로 개발자들이 코드리뷰, 페어 프로그래밍 등의 방식을 고안하고 적용하는 것도 이러한 문제를 해결하기 위함이 아닌가?

하지만 동시에 이것은 단점도 된다. 스스로 생각해내는 기회를 앗아가기 때문이다.

필자도 (요새 이 '필자'라는 표현말고 다른 좋은 표현이 없을까라고 고민중이다... 원래 블로그 처음 오픈했을 때, 존대말로 쓰다가 자꾸 문장들이 반말로 바뀌어서 반말로 통일하였다. 근데 나를 표현할 때 그냥 '나'라고 하기도 조금 그래서 '필자'라고 했는데 이것도 조금 ...) 가장 처음 IT업계로 발을 들이기 위해 준비를 시작했을 때, 알고리즘 자료구조, 그리고 PS 공부를 하다가 별거 아닌 문제를 며칠씩 고민하기도 하였다. 사실 필자가 푼 백준의 가장 최초의 문제는 (상당히 어려운 문제이기도 하지만) 거의 보름 정도를 붙잡고 있었던 것 같다. 하지만 당시 그렇게 한 문제 한 문제를 한땀한땀 고민했기에 스스로도 많은 깨달음을 가질 수 있었다. 어쨌든 요지는 고민하고 고심하는 시간, 그리고 그것을 할 수 있는 기회부터가 중요하다.

 

  • 문제 해결을 위한 자료구조가 제공된다.

이진트리 관련 문제를 보면 다음과 같이 자료구조가 제공된다.

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */

이는 선택사항이 아니다. 문제의 입력부터 위의 구조로 주어지기 때문이다. 릿코드 문제를 처음 풀었을 때, 저걸 모르고 이진트리 자료구조를 직접 구현했다가 맞왜틀 시전을 계속했던 적이 있다. 여기에도 장단점이 있다.

장점은 당연히 자료구조 구현법을 배울 수 있다는 것이다. 이진트리, 링크드리스트 등을 어떻게 구현하고 어떻게 사용하면 편리하고 효율적인지 배울 수 있다는 것이다. 실제로 필자도 뭔가 정형화된 방식을 외우고 다니거나, 습관처럼 나오는 방식이 있는 게 없어서 매번 처음부터 고민하고 작성하는 식이다. 하지만 저런 기본적인 방식은 익혀둘 만하다. 문제를 풀 때, 저것을 잘 활용하면 매우 좋다. 아니 어떻게 보면, 저것을 활용할 수 있도록 문제 풀이 방식을 설계한다고 봐야할지도 모르겠다.

단점은 한 가지 고정된 구현 틀에 갖히게 되고, 그 틀을 활용한 풀이만 생각하게 될 수도 있다는 것이 된다. 이진트리 자료구조를 만들거나 관련 문제를 해결하는 방식은 당연히 무궁무진하게 많을 것이다. 어떤 한 가지의 자료구조 구현 방식이 절대로 최선일 수는 없다. 경우에 따라서, 문제에 따라서, 문제에서 주어지는 조건에 따라서 여러가지 서로 다른 방식이 있을 수 있다. 이렇게 되면 중요한 것은, 문제를 정확히 파악하고 매번 그에 맞는 자료구조, 알고리즘 등을 고안할 수 있는 능력이라는 점을 알 수 있다. 하지만 고정된 틀을 강제적으로 사용해야 한다면 그런 능력을 배양할 기회를 잃어 아쉬울 수 있는 것이다.

 

  • 테스트케이스를 파일이 아닌 함수가 수행, 전역변수

사실 이 것은 장단점에 대해 논하려고 하는 것이 아니다. 그저 릿코드는 이렇게 돌아간다는 것을 짚어보기 위함이다.

백준같은 플랫폼에서는 자신이 제출한 파일을 매번 돌려서 여러 테스트케이스(테케)를 풀어낸다. 테스트케이스안에 여러 케이스가 있는 것을 말하는게 아니다. 하지만 릿코드에서는 작성한 함수를 매번 돌린다. 그래서 가끔씩, 문제에서 작성하라고 주어지는 함수 외부에 다른 변수(전역변수 느낌)를 정의하여 풀이할 때, 이전 테케를 푸느라 사용한 값들이 다음 테케에 내려와서 문제되는 경우가 생긴다. 그래서 함수 내부에 그 변수에 들어가도록 설계를 바꾸거나, 외부 전역변수를 매번 초기 리셋하는 식으로 수정했었다. 아마 릿코드 문제를 처음 푸는 사람은 주의해야 할 것이다.

 

보통 전역변수를 함부로 사용하지 말라고 한다. 프로젝트 및 프로그램의 규모가 커질수록 여러개의 스코프가 존재하고 서로 관계를 맺는 상황에서 분명히 최상단 스코프는 존재하기 마련이고, 거기에도 분명 뭔가가 있다. 그 곳에 있는 것이 전역변수 아니겠는가? 실제 코드를 짤때도 common폴더 같은 곳에 공통 모듈, 공통 로직을 구현해 두지 않는가? 요새는 모노 리포(레포), mono repository를 통해 모든 서브 패키지들이 외부 모듈을 한 곳에서 공용하는 식의 패키지 구성도 많은 것으로 알고 있다. 즉, 전역변수는 공통되는 부분을 모음하는 곳이고, 나름의 필요가 있다. 내가 쓰는 PS코드에서는 내 판단하에 나름대로 전역변수들을 구성할 수 있다. 다만 백준과 달리 릿코드는 파일을 한번만 열고, 그 내부 함수를 계속 돌려서 문제를 푸는 방식이라고 생각되기에 전역변수가 있다면 그에 대한 처리에 주의를 가져야 한다.

 

어쨌든 알고리즘 부분에 한하여 개인적인 생각을 조금 적어봤으며, 릿코드는 의심할 여지없이 좋은 플랫폼이고 이미 많은 사람들이 사용중이다. 필자도 앞으로 해당 플랫폼을 많이 활용하려고 한다.