파워 J인 나는 Leetcode, 프로그래머스, 백준, SWEA 등등의 플랫폼에서 푼 알고리즘 문제들을 모두 올려두는 Github repository가 있다. 단순히 사이트 IDE에서만 풀고 풀이들을 흘려보내는 것 보다는, 내 풀이도 기록하고 나중에 다시 풀어봤을 때 달라지는 나의 풀이를 보는 재미도 있기 때문에 작년 말부터 꾸준히 알고리즘 풀이들을 올려두고 있다. 원래 블로그에 풀이를 올리곤 했었는데 본격적인 코테 준비도 시작하며 문제 갯수가 많아지니 아무래도 포스트 하나하나로 올리기엔 한계가 있었다. 그래서 본격적으로 깃헙으로 이사를 갔던 것 같다. (그리고 깃헙 잔디 관리에도 좋다 ㅎ)
Repository에 문제들을 올리며 자연스레 리드미도 어떻게 관리할 지 고민을 하기 시작했었는데.. 아카이빙에 집착하는 나로써는 어떤 방식을 택해도 만족스럽지가 않았다. 특히나 문제를 하나하나 풀 때마다 리드미를 수동으로 업뎃해야 한다는게 너무너무 귀찮았고 문제 기록이 밀리기 시작한 나머지 리드미를 아예 삭제해버렸다 (급발진). 최근에는 미국 코테를 준비하며 Leetcode에서 푼 문제를 다시 풀어봐야되는 일들이 많이 생겼기에 리드미를 다시 부활시켜야할 필요성을 느꼈고 실행 한 번에 내가 Repository에 추가한 풀이를 리드미에 작성해주는 파이썬 스크립트를 작성해보았다. 프로젝트라고 하기엔 거창한게 없지만 내 삶의 질을 수직상승 시켜준 프로그램이기에 블로그에 작성해두려한다.
위의 스샷에서 볼 수 있듯이 나는 Leetcode 문제를 풀 때마다 문제번호_문제이름.py 파일을 만들어서 문제 링크, 내 코드, 설명, 그리고 time complexity, space complexity를 기록해둔다. 파일 이름이 일정한 형식을 유지하고 있기 때문에 우선 Leetcode 디렉토리 안의 파이썬 파일들을 불러오고, 문제 번호 순서대로 정렬해서 딕셔너리에 저장해두었다.
def __init__(self):
self.attempt = []
self.questions_list = []
def get_file_names(self):
# Get python files
python_files = [x for x in os.listdir("./leetcode") if x.endswith(".py")]
# Sort by question num
master = collections.defaultdict(str)
for i in python_files:
num = int(i.split('_')[0])
master[num] += i
sorted_questions = dict(sorted(master.items(), key=lambda item: item[0]))
self.questions_list = list(sorted_questions.values())
return self.questions_list
해당 문제를 몇 번째 풀어보는 것인지에 대한 기록도 하고 싶어서 각 파일내에 "class "라는 string이 몇 번 등장하는지 확인한 후, 시도 횟수는 리스트에 저장했다. 나는 문제를 다시 풀어보면 "class Solution" 부분부터 다 가져와서 기존의 코드 밑에 복붙하기 때문에 해당 방법으로 시도 횟수를 카운트 했다. 간혹 "class ListNode", "class Tree" 같은 자료구조 이름을 사용하는 클래스들도 있기 때문에 우선 "class " string을 사용하였다.
def check_attempt(self, q_list):
# Check if nth attempt for each question
for name in q_list:
f = open("./leetcode/{}".format(name), "r")
# read file content
readfile = f.read()
occurrences = readfile.count("class ")
if occurrences > 3:
occurrences = 3
self.attempt.append(occurrences)
# closing a file
f.close()
문제들을 마크다운 형식으로 변환하는 단계이다. 테이블을 만들고 싶었기 때문에 4개의 column을 만들고 문제들의 이름과 시도 횟수를 각 row에 저장하였다. 문제의 제목에 풀이의 relative path도 연결하였다.
def convert_format(self, q_list):
# Link relative path
q_list = ['[' + i + ']' + '({})'.format(i) for i in q_list]
checked = 'x'
unchecked = ''
# Attempt check
checkbox = unchecked + '|' + unchecked + '|' + unchecked + '|'
for i, num in enumerate(self.attempt):
if num == 1:
checkbox = checked + '|' + unchecked + '|' + unchecked + '|'
elif num == 2:
checkbox = checked + '|' + checked + '|' + unchecked + '|'
elif num == 3:
checkbox = checked + '|' + checked + '|' + checked + '|'
# Add to table and default checkbox
self.questions_list[i] = '|' + q_list[i] + '|' + checkbox + '\n'
checkbox = unchecked + '|' + unchecked + '|' + unchecked + '|'
return self.questions_list
def write_to_markdown(self, q_list):
# Export it to markdown, convert to table
markdown = open('./leetcode/readme.md', 'w')
markdown.write("## Leetcode Python Questions Solved\n")
markdown.write("|Question| Attempt 1| Attempt 2|Attempt 3|")
markdown.write("\n")
markdown.write("|---|---|---|---|")
markdown.write("\n")
markdown.writelines(q_list)
markdown.write("\n")
markdown.write("Total: {} questions".format(len(q_list)))
print('Markdown export success')
스크립트를 실행시키면 추가 된 문제들이 반영된 예쁜 테이블이 완성된다!
코드는 하단의 링크에서 확인할 수 있다:
개선할 점 + 추가할 점
- 로컬에서 커밋할 때마다 자동으로 해당 스크립트가 실행 됐으면 좋겠는데 어떻게 하는지 잘 모르겠다. 찾아보니 git pre-commit이라는 기능이 있는 것 같은데 더 찾아봐야할 것 같다.
- 기본적인 기능에만 충실한 코드라 개선할 부분이 많을 것 같다.
- 예외 처리 + 프로그램파일 이름 바꾸자. 지금은 귀찮아서 숫자_이름.py 형식으로 만들어뒀는데 잇츠 어글리.
- 각 문제별 난이도, 태그와 자료구조 종류까지 가져올 수 있다면 그걸로 시각화를 해보고 싶은데 내가 아는 한 Leetcode API가 없어서 내가 직접 파일 안에 해당 정보를 적어야한다. 그러기엔 이미 있는 파일들이 너무 많아서 고민 중.
알고리즘 풀기 싫어서 갑자기 구현해본 기능치곤 평소에도 꾸준히 쓸 것 같다!
+ 업데이트: Leetcode api가 있었다.. ! 당연히 없을 줄 알았는데.. 다음엔 이걸 사용해보는걸루..
'Projects' 카테고리의 다른 글
딥러닝(BERT) 기반 멜론 음원 사재기 판별 시스템 구축 프로젝트 (0) | 2021.04.01 |
---|---|
[EDA Project] 제주도 재난지원금 EDA 프로젝트 (0) | 2021.03.13 |