HeoBrain AI · DEV · GROWTH

HEO BRAIN · DEV LAB

배운 것을 구조화하고,
실제로 작동하게 만듭니다.

AI, 코딩, 영어, 포트폴리오를 직접 공부하고 만들며 얻은 지식을 누구나 다시 써먹을 수 있게 정리합니다.

heobrain.workflow LIVE
01 collect(experience) 02 structure(knowledge) 03 ship(something useful)

LATEST NOTES

최근에 정리한 글

모든 글 보기

Day20)프로그래머: 함수(메서드)

이 글의 목차 펼치기

Python 코딩테스트 학습 기록

3주 코딩 레벨업 플랜 | Lv.0에서 다음 문제로 넘어가기 위한 Python 학습 기록

문제를 많이 푸는 것보다, 한 문제에서 실수한 이유와 더 나은 풀이를 끝까지 확인하는 연습을 남긴다. 이번 기록에서는 3주 학습 계획과 배열·딕셔너리·정렬 문제를 풀며 정리한 핵심을 함께 적었다.

먼저 세운 3주 학습 방향

짧은 기간에 점수를 올리는 일은 결과를 약속할 수 있는 일이 아니다. 다만 현재 푸는 문제의 난이도와 실수 패턴을 정확히 기록하면, 다음 단계로 넘어갈 가능성은 분명히 높일 수 있다. 처음에는 하루 5문제를 빠르게 넘기는 방식보다 하루 1~2문제를 끝까지 복기하는 방식으로 바꾸기로 했다.

1
1주 차: 해시와 기본 자료구조
딕셔너리, 빈도수 세기, 스택·큐의 동작을 익힌다. 같은 값을 몇 번 만났는지 기록하는 문제를 중심으로 푼다.
2
2주 차: 완전탐색과 DFS/BFS
작은 입력에서는 직접 모든 경우를 확인하는 기준을 세우고, 그래프·격자 문제에서 방문 처리를 연습한다.
3
3주 차: 투 포인터·슬라이딩 윈도우·그리디
한 번 훑으며 답을 갱신하는 O(N) 사고를 익히고, 실제 시험 형식 문제로 시간 배분을 점검한다.

복기 기준: 제출 후에는 정답 여부만 보지 않는다. `왜 이 변수로 시작했는지`, `반복문이 몇 번 도는지`, `같은 계산을 반복하지는 않는지`를 짧게 적는다. 이 기록이 다음 문제의 힌트가 된다.

1. 배열의 길이를 2의 거듭제곱으로 만들기

프로그래머스 공식 문제 링크: 배열의 길이를 2의 거듭제곱으로 만들기

배열 뒤에 0을 최소한으로 붙여 길이를 1, 2, 4, 8, 16처럼 만들면 된다. 핵심은 현재 길이보다 작지 않은 가장 가까운 2의 거듭제곱을 찾는 것이다.

처음 시도에서 주의할 점

answer = [2 ** i for i in range(len(arr))]
for i in answer:
    while len(arr) < i:
        arr.append(0)

이 방식은 필요한 길이를 찾는 데 지나치게 큰 수를 많이 만들 수 있다. 또 whilefor 안에 넣으면, 아직 목표를 정하기 전인데 배열 길이만 계속 바꾸는 흐름이 되기 쉽다. 목표 길이를 하나 정한 뒤, 그 차이만큼 0을 붙이는 편이 읽기 쉽다.

내 풀이를 다듬은 기본 풀이

def solution(arr):
    target = 1

    while target < len(arr):
        target *= 2

    return arr + [0] * (target - len(arr))

target은 1에서 시작한다. 현재 배열 길이보다 작을 동안만 2배로 키우면, 반복이 끝났을 때 target은 필요한 최소 길이이다. 예를 들어 길이가 6이면 1 → 2 → 4 → 8이 되어 0을 2개 붙인다.

제한사항이 길이 1,000 이하임을 이용해 2의 거듭제곱 후보를 미리 만들 수도 있고, math.log2()도 사용할 수 있다. 하지만 이 풀이처럼 목표를 직접 키우면 별도 모듈이나 하드코딩한 반복 횟수 없이 조건이 잘 보인다.

2. 배열 비교하기

프로그래머스 공식 문제 링크: 배열 비교하기

이 문제는 일반적인 리스트의 대소 비교가 아니라, 길이 우선, 길이가 같을 때만 원소 합을 비교한다.

def solution(arr1, arr2):
    if len(arr1) != len(arr2):
        return (len(arr1) > len(arr2)) - (len(arr1) < len(arr2))

    sum1, sum2 = sum(arr1), sum(arr2)
    return (sum1 > sum2) - (sum1 < sum2)

파이썬에서 비교식은 True 또는 False이고, 숫자로 계산하면 각각 1과 0처럼 쓸 수 있다. 그래서 (A > B) - (A < B)는 A가 크면 1, 같으면 0, 작으면 -1이 된다.

한 줄 풀이를 읽을 때의 포인트

def solution(arr1, arr2):
    return ((len(arr1) > len(arr2)) - (len(arr1) < len(arr2))
            or (sum(arr1) > sum(arr2)) - (sum(arr1) < sum(arr2)))

or는 왼쪽 값이 1 또는 -1이면 그 값을 바로 반환한다. 길이가 같아 왼쪽 결과가 0일 때만 오른쪽의 합 비교를 계산한다. 짧지만 처음에는 기본 풀이를 먼저 이해하는 편이 좋다.

3. 문자열 묶기: 정답에서 시간 초과로 넘어가는 지점

프로그래머스 공식 문제 링크: 문자열 묶기

문자열을 길이별로 묶었을 때 가장 큰 그룹의 개수를 구하는 문제다. 가장 긴 문자열의 길이를 찾는 것이 아니라, 같은 길이를 가진 문자열이 몇 개인지 세는 문제라는 점이 중요하다.

처음 풀이와 실패 원인

def solution(strArr):
    answer = 0
    box = []

    for char in strArr:
        box.append(len(char))

    for length in box:
        answer = max(answer, box.count(length))

    return answer

답은 맞지만 box.count(length)가 실행될 때마다 리스트 전체를 다시 센다. 문자열이 최대 100,000개면 전체 탐색을 다시 전체 탐색 안에서 반복하는 O(N²) 흐름이 되어 시간 초과가 날 수 있다.

딕셔너리로 한 번만 세기

def solution(strArr):
    count_dict = {}

    for char in strArr:
        length = len(char)
        if length in count_dict:
            count_dict[length] += 1
        else:
            count_dict[length] = 1

    return max(count_dict.values())

count_dict[length] = 1은 키와 값을 함께 만든다. 예를 들어 길이가 3인 문자열을 처음 만나면 count_dict[3] = 1이 실행되어 {3: 1}이 된다. 다음에 길이 3을 만나면 이미 키가 있으므로 += 1로 그 값만 올린다.

values()의 최댓값인가? 키는 문자열 길이이고, 값은 그 길이를 가진 문자열의 개수다. 문제는 가장 큰 길이가 아니라 가장 많은 그룹의 크기를 묻기 때문에 max(count_dict.values())가 정답이다.

같은 뜻의 간결한 풀이

def solution(strArr):
    count_dict = {}
    for text in strArr:
        length = len(text)
        count_dict[length] = count_dict.get(length, 0) + 1
    return max(count_dict.values())

get(length, 0)은 키가 있으면 기존 값을, 없으면 0을 가져온다. 그래서 처음에는 0 + 1로 새 키가 만들어지고, 다음부터는 기존 값 + 1로 갱신된다.

제한사항을 활용한 빈도수 배열

def solution(strArr):
    counts = [0] * 31
    for text in strArr:
        counts[len(text)] += 1
    return max(counts)

이 문제에서는 문자열 길이가 1부터 30까지라서 인덱스 0~30을 가진 리스트를 장부처럼 쓸 수 있다. 값의 범위가 작고 정수로 정해져 있을 때만 가능한 방식이다. 범위가 넓거나 문자열처럼 인덱스가 될 수 없는 값이면 딕셔너리가 더 일반적인 선택이다.

4. 배열의 길이에 따라 다른 연산하기

프로그래머스 공식 문제 링크: 배열의 길이에 따라 다른 연산하기

배열 길이가 홀수면 짝수 인덱스에, 길이가 짝수면 홀수 인덱스에 n을 더한다. 인덱스는 0부터 시작한다는 점을 먼저 확인해야 한다.

def solution(arr, n):
    length = len(arr)

    if length % 2:
        for i in range(0, length, 2):
            arr[i] += n
    else:
        for i in range(1, length, 2):
            arr[i] += n

    return arr

range(0, length, 2)는 0, 2, 4처럼 두 칸씩 이동하고, range(1, length, 2)는 1, 3, 5처럼 이동한다. 대상 인덱스만 직접 방문하므로 반복 중에 다시 홀짝을 검사하지 않아도 된다.

if length % 2:는 나머지가 1일 때 참으로 동작한다. 초보자라면 if length % 2 == 1:로 먼저 써도 충분히 명확하다. 또한 파이썬의 len()은 길이를 다시 세는 함수가 아니라 보통 O(1)로 길이를 돌려준다. 여기서 length에 저장한 이유는 속도보다 조건의 의미를 읽기 쉽게 하기 위해서다.

새 리스트를 만드는 풀이

def solution(arr, n):
    return [value + n if len(arr) % 2 != i % 2 else value
            for i, value in enumerate(arr)]

이 한 줄은 배열 길이의 홀짝과 인덱스의 홀짝이 다를 때만 값을 바꾼다. 다만 원리를 익히는 단계에서는 위의 range 풀이를 먼저 읽는 편이 낫다.

5. 정렬 후 가장 작은 5개 고르기: if와 while의 차이

프로그래머스 공식 문제 링크: 뒤에서 5등 위로

def solution(num_list):
    ordered = sorted(num_list)
    result = []

    for number in ordered:
        if len(result) < 5:
            result.append(number)

    return result

이 풀이에서 if는 반복마다 한 번만 검사한다. 따라서 정렬된 수를 하나씩 넣어 앞의 5개를 만든다. 여기서 while len(result) < 5:로 바꾸면 첫 번째 숫자를 잡은 상태에서 조건이 거짓이 될 때까지 같은 숫자만 5번 넣는다. while은 조건이 참인 동안 같은 자리를 계속 반복하기 때문이다.

def solution(num_list):
    return sorted(num_list)[:5]

sorted(num_list)로 오름차순 정렬한 뒤 [:5]로 앞의 다섯 원소만 가져온다. solution = lambda values: sorted(values)[:5]도 동작하지만, 프로그래머스의 기본 형식과 초보자용 설명에서는 함수 이름과 매개변수가 드러나는 def solution(...)이 더 읽기 좋다.

이번 복기에서 남길 것

1
답이 맞아도 입력 크기를 확인한다. 리스트 안에서 다시 count()로 전체를 세면 시간 초과가 될 수 있다.
2
딕셔너리는 dict[key] = value 한 줄로 키와 값을 함께 만든다. 문제에서 묻는 값이 키인지 값인지 먼저 문장으로 확인한다.
3
for는 대상의 다음 원소로 넘어가고, while은 같은 상태에서 조건이 거짓이 될 때까지 머문다. 둘을 바꾸기 전에는 반복 변수와 상태 변화가 있는지 확인한다.
4
한 줄 풀이를 발견해도 기본 풀이를 먼저 남긴다. 짧은 코드보다 다음날 다시 읽었을 때 이해되는 코드가 우선이다.
이 글은 문제를 푼 뒤의 코드와 질문을 정리한 학습 기록입니다. 다음 복기에서는 해시 빈도수, 반복 범위, 시간 복잡도를 먼저 점검해 보려고 합니다.
RSS 구독
블로그 검색