WIL WEEK2

정범진·2026년 3월 12일

파이썬 알고리즘 풀이 하면서 느낀점
1. C와 다르게 내부 동작 과정이 많이 생략돼있는 것 같음 -> input output 값에 더 집중해야 할 것 같음
2. 자료형이 표시돼있지 않음 -> 이것 때문인지는 모르겠는데 소스코드가 잘 안읽힘
3. 슬라이싱, range() 엄청 햇갈림 특히 for 문 돌릴 때 시작 값, 종료 값 잘못 작성할 때 많음
4. 각 자료형마다 사용할 수 있는 메서드를 잘 알아둬야 할 것 같음

-> GPT의 답변

  1. C와 다르게 내부 동작 과정이 많이 생략돼있는 것 같음

    • 맞는 느낌임
    • 파이썬은 저수준 동작을 덜 드러내고, “무엇을 할지”를 더 빨리 쓰게 해주는 언어임
    • 그래서 알고리즘 풀이에서는 메모리 구조나 구현 디테일보다는 입력을 어떻게 해석하고, 출력을 어떻게 만들지에 더 집중하게 됨
    • 대신 그만큼 “내가 지금 자료를 어떤 형태로 가지고 있는지”를 머릿속에서 더 선명하게 관리해야 함
  2. 자료형이 표시돼있지 않음

    • 이것도 파이썬 입문 때 코드가 안 읽히는 가장 큰 이유 중 하나임
    • C는 선언만 봐도 int, char, int[] 같이 감이 오는데, 파이썬은 a, tmp, result만 있으면 바로 안 보임
    • 그래서 파이썬 코드는 읽을 때 변수 이름, 현재 값의 예시, 자료형 추론을 같이 해야 해서 처음엔 더 추상적으로 느껴짐
    • 이 문제는 타입 힌트보다도, 알고리즘 풀이에서는 변수명을 더 의미 있게 짓는 습관이 훨씬 도움 됨
      • 예: a보다 nums, cnt, left, right, visited가 읽기 쉬움
  3. 슬라이싱, range() 엄청 헷갈림

    • 이건 거의 모두가 헷갈리는 부분임
    • 특히 파이썬은 끝값이 포함되지 않는 규칙이 많아서 실수하기 쉬움
    • range(start, end)도 end 미포함, 슬라이싱 arr[a:b]도 b 미포함이라 규칙은 일관적이지만, 익숙해지기 전엔 자꾸 틀림
    • 오히려 이건 감각 문제가 아니라 규칙을 몸에 익히는 반복 문제에 가까움
    • 실전에서는
      • “몇 번 도는가”
      • “마지막으로 포함되는 인덱스가 어디인가”
      • “길이가 몇 개인가”
        이 3개를 같이 확인하는 습관 들이면 실수 많이 줄어듦
  4. 각 자료형마다 사용할 수 있는 메서드를 잘 알아둬야 할 것 같음

    • 이건 진짜 중요함
    • 파이썬 알고리즘 실력은 단순히 알고리즘 지식만이 아니라
      list / dict / set / string / deque / heapq를 얼마나 자연스럽게 다루는지에 크게 좌우됨
    • 같은 문제도 메서드와 자료구조를 잘 알면 짧고 명확하게 풀리고, 모르고 있으면 괜히 복잡하게 돌아가게 됨
    • 그래서 문법 공부보다 자료형별 핵심 연산 정리가 훨씬 효율적임

for문 사용시 배열 인덱스

-> GPT의 팁
인덱스가 헷갈릴 때는 숫자를 따라가지 말고, 그 숫자가 가리키는 역할을 따라가면 됨

왜 헷갈리는가

1. 변수 이름이 역할을 설명하지 않음

i, j는 그냥 숫자임
i+1을 보면 “그래서 이게 뭐지?”가 됨

예를 들면

for i in range(n):
    if arr[i] < arr[i+1]:
        ...

이걸 읽을 때 머릿속에서는 사실 이렇게 해석해야 함

  • i = 현재 위치
  • i+1 = 다음 위치

근데 코드에는 “현재”, “다음”이라는 의미가 안 적혀 있으니까
매번 숫자를 계산하면서 읽게 됨
그래서 피곤함


2. 인덱스와 값이 섞여서 생각됨

초보 때 가장 흔한 혼동이 이거임

  • i는 인덱스
  • arr[i]는 값

근데 읽다 보면 i 자체를 값처럼 느끼거나
arr[i+1]를 볼 때 “다음 인덱스”와 “다음 값”이 한꺼번에 떠올라서 꼬임

예:

for i in range(len(arr)-1):
    print(i, arr[i], arr[i+1])

여기서 동시에 관리해야 하는 게 3개임

  • i
  • 현재 값 arr[i]
  • 다음 값 arr[i+1]

그래서 뇌 부담이 커짐


3. 머릿속으로만 추적하려고 해서

인덱스 관련 실수는 머릿속 계산만으로 버티면 거의 틀리기 쉬움
특히 반복문 안에서 조건까지 있으면 더 그럼

예:

for i in range(1, n-1):
    if arr[i-1] < arr[i] < arr[i+1]:
        ...

이건 사실

  • 왼쪽
  • 현재
  • 오른쪽

을 비교하는 건데
코드상으로는 i-1, i, i+1이라 숫자 계산처럼 보여서 어려운 거임


덜 헷갈리는 팁

1. 숫자로 보지 말고 역할로 읽기

이게 제일 중요함

arr[i]
arr[i+1]

이걸

  • 현재 값
  • 다음 값

으로 번역해서 읽는 습관 들이면 좋음

예:

for i in range(len(arr)-1):
    if arr[i] > arr[i+1]:
        ...

이걸 그냥

  • i번째 값
  • i+1번째 값

으로 보지 말고

  • 현재 값
  • 바로 다음 값

으로 읽기

그러면 훨씬 덜 꼬임


2. 범위를 먼저 해석하고 들어가기

반복문 볼 때 바로 본문부터 보지 말고
먼저 range()가 어디까지 도는지 확인해야 함

for i in range(len(arr)-1):

이 뜻은

  • i0부터 len(arr)-2까지 돈다
  • 그래서 i+1은 최대 len(arr)-1까지 가능하다
  • 즉, arr[i+1]까지 안전하게 접근하려고 -1 한 거다

이렇게 먼저 이해하면 본문이 쉬워짐

즉, 반복문 볼 때는 항상 먼저 확인:

  • 시작 인덱스가 어디인지
  • 마지막 인덱스가 어디인지
  • 왜 저 범위로 돌리는지

3. i+1이 나오면 “왜 +1이 필요한지” 먼저 보기

+1, -1은 대부분 이유가 정해져 있음

대표 패턴

다음 원소 보기

arr[i+1]

→ 현재 기준으로 다음 칸 확인

이전 원소 보기

arr[i-1]

→ 현재 기준으로 이전 칸 확인

1-based 출력 맞추기

print(i+1)

→ 내부 인덱스는 0부터 시작하지만, 사람에게는 1번부터 보여주려고 함

2중 반복문에서 뒤쪽만 보기

for j in range(i+1, n):

i 다음 위치부터 비교하려는 것

+1은 뜬금없는 계산이 아니라
대부분 다음 것, 중복 제거, 출력 보정 중 하나임


4. 작은 예시를 직접 써보기

헷갈리는 문제는 5개짜리 배열로 직접 써보는 게 제일 빠름


5. 디버그 출력 적극적으로 쓰기

실전에서 제일 강력한 방법임

for i in range(len(arr)-1):
    print("i =", i, "현재 =", arr[i], "다음 =", arr[i+1])

이렇게 출력하면
인덱스와 값이 어떻게 움직이는지 눈으로 보임


6. 2중 반복문은 표로 생각하기

i, j가 동시에 나오면 머릿속에서 선형으로 추적하기 어려움
이때는 표나 좌표처럼 생각하는 게 좋음

예:

for i in range(n):
    for j in range(i+1, n):
        ...

이건 보통

  • i보다 뒤에 있는 것만 비교
  • 자기 자신 제외
  • 중복 비교 제거

의미임

n=4면 실제로는

  • i=0j=1,2,3
  • i=1j=2,3
  • i=2j=3
  • i=3 → 없음

이렇게 됨

(0,1), (0,2), (0,3), (1,2), (1,3), (2,3)만 보는 구조임
이걸 표로 한번 그려보면 훨씬 쉬움


7. 인덱스 대신 이름을 붙일 수 있으면 붙이기

가능하면 숫자 대신 의미를 드러내는 이름으로 바꾸는 게 좋음

예:

for i in range(len(arr)-1):
    current = arr[i]
    nxt = arr[i+1]
    if current > nxt:
        ...

이렇게 하면

  • arr[i]
  • arr[i+1]

를 계속 읽지 않아도 됨
가독성이 확 좋아짐

2포인터도 비슷함

left, right = 0, len(arr)-1
while left < right:
    ...

이건 i, j보다 훨씬 덜 헷갈림
왜냐하면 역할이 이름에 들어있기 때문임


8. “값 추적”보다 “불변 규칙”을 잡기

숙련되면 매 순간 값을 다 계산하지 않음
대신 규칙을 잡음

예:

for i in range(len(arr)-1):

여기서는

  • i는 현재 위치
  • i+1은 항상 다음 위치
  • 마지막 원소는 현재 위치가 될 수 없음
    왜냐하면 다음 위치가 없으니까

이 규칙만 잡으면 매번 계산 안 해도 됨

2중 반복문도 마찬가지

for i in range(n):
    for j in range(i+1, n):

규칙:

  • j는 항상 i보다 큼
  • 같은 쌍 두 번 안 봄
  • 자기 자신과 비교 안 함

이런 규칙을 이해하면 숫자 따라가는 부담이 줄어듦


4. 사람 기준 번호 출력

for i in range(n):
    print(i+1, arr[i])

해석:

  • 내부 인덱스는 0부터
  • 출력은 1번부터 보여주기 위해 보정

실수 줄이는 습관

습관 1

반복문 보면 먼저 한글로 바꾸기

for i in range(len(arr)-1):

→ “0부터 마지막 바로 전까지”

for j in range(i+1, n):

→ “i 다음 칸부터 끝까지”

이렇게 번역해두면 좋음

습관 2

본문 읽을 때 역할로 바꾸기

  • arr[i] → 현재
  • arr[i+1] → 다음
  • arr[i-1] → 이전

습관 3

헷갈리면 4~5개짜리 예시 대입하기

습관 4

출력 찍어서 실제 값 보기


=, .copy(), copy.deepcopy() 차이점

=

a = [1, 2]
b = a

b[0] = 3
print(a) ---> [3, 2]

.copy()

a = [1, 2]
b = a.copy()
b[0] = 3
print(a) ---> [1, 2]

# 주의 이중 리스트인 경우는 다름. 겉 리스트만 복사 리스트 내부는 참조
a = [[1, 2], [3, 4]]
b = a.copy()
b[0][0] = 5
print(a) ---> [[5, 2], [3, 4]]

copy.deepcopy()

a = [[1, 2], [3, 4]]
b = copy.deepcopy(a)

b[0][0] = 5
print(a) ---> [[1, 2], [3, 4]]

깊은 복사 얕은 복사 확인 방법 -> id() 사용

print(id(a)) ---> id값을 비교해서 같은지 다른지 확인

= 인 경우 → id(a) == id(b)
.copy() 인 경우 → id(a) != id(b)
하지만 얕은 복사는 겉만 다르고 속은 같을 수 있음

인덱스로 내부 값도 비교 가능

print(id(a[0]), id(b[0]))

0개의 댓글