
코딩테스트를 준비하는 과정을 따로 Velog에 글로 다루지 않고 백준에서 문제만 풀고 있었는데, 이 과정들을 글로 정리해보면서 되짚어보는 것도 좋을 거 같다.
공부할 때 참고한 영상은 거의 바이블처럼 여겨지는
였으며, 이번에는 다시 해당 영상 시리즈의 처음부터 끝까지 깃허브에 올려보기도 해보며 내가 많이 사용하는 알고리즘 코드를 라이브러리화 하는 일명 "팀 노트" 를 관리해보고자 한다.

(출처: https://youtu.be/SwqsX8bb-gw?si=C0btl5n5Vg8eikUW)
이는 위 출처의 영상인, 유튜브 개발남노씨 님의 2025년 코테 기출 32문제 분석 영상 중, 2025년 상반기 코딩테스트 트렌드를 분석한 그래프다.
알고리즘의 성능을 나타내기 위해서는 복잡도라는 개념이 사용된다.
이 복잡도의 표기법에는 여러 가지가 있는데, 빅오(Big-O) 표기법이 효과적이다.
이는 가장 빠르게 증가하는 항만을 고려하며, 함수의 상한만을 나타낸다.
연산 횟수가 인 알고리즘에 있다고 가정하면, 빅-O 표기법에서는 차수가 가장 큰 항만 남기므로 으로 표현되며, 계수는 무시한다.
⭐ 여기서 만약
이 정말 커서 "1억" 정도가 된다고 가정한다면, 을 제외한 나머지 항들 이에 비해 작은 수가 된다.
그렇기 때문에 이 굉장히 큰 값이 될 수 있다고 가정했을 때, 이 함수의 상한 만을 고려한다고 하더라도 충분히 그 함수의 성능이 어느정도 나올지 가늠 할 수 있는 것이다.
📌 참고
- 항만
- 알고리즘의 시간 복잡도를 나타내는 함수에서 가장 영향력이 큰 항을 의미함
- 시간 복잡도 함수가 과 같을 때, n이 커짐에 따라 의 값에 가장 큰 영향을 미치는 항은 이므로, 이 항을 항만 이라고 함
- 빅-O에서는 이러한 가장 빠르게 증가하는 항만을 고려하여 함수의 상한을 표현함
- 상한
- 알고리즘의 시간 복잡도 함수에서 최악의 경우, 즉 가장 오래 걸리는 실행 시간을 나타냄
- 빅-O는 함수의 상한을 표현하는 표기법으로, 어떤 알고리즘의 시간 복잡도가 이라는 것은 최악의 경우에도 입력 크기 n에 비례하는 시간 안에 실행됨을 의미하며, 다시 말해, 입력 크기가 커짐에 따라 실행 시간이 에 비례하여 증가할 수 있다는 뜻

알고리즘은 이와 같이 상수 시간, 로그 시간, 선형 시간 등의 표기법을 붙여서 해당 알고리즘의 성능을 나타낼 수 있으며, 좋음(Better) 쪽으로 갈수록 복잡도가 낮은 것이다.
즉 여기서 상수 시간은 몇번의 연산만 거치면 수행이 완료되는 경우, 로그 시간은 에 비례하는 만큼 수행 되는 것을 말하는 것이다.
예시 프로그램을 보면 다음과 같다.
array=[3,5,1,2,4] # N=5
total=0
for x in array: total += x
print(total)
이의 수행 시간은 데이터의 개수(N) 에 비례하기 때문에 시간 복잡도는 이 된다.
이런 점을 고려해야한다.
일반적으로 CPU 기반의 개인 PC나 채점용 PC에서 연산 횟수가 5억을 넘어가는 경우에는 python을 기준으로 통상 5~15초 가량이 소요된다.
그리고 채점 서버가 PyPy를 제공한다면 경우에 따라 C언어(통상 1~3초)보다 빠르게 동작하기도 하기에 Python으로 제출했을때 시간초과가 나왔을때 PyPy로 제출하는 방법도 있다.
그런데 아예 반대로 PyPy보다 Python이 빠른 경우도 있으니 그 반대의 방안도 생각해 두는 것이 좋다.
그리고 코딩 테스트 문제에서 시간제한은 통상 1~5초 가량이라는 점을 유의해야 한다.
명시되지 않았다면 대략 5초 정도라고 생각하는 것이 좋다.
따라서 문제에서 가장 먼저 시간제한(수행시간 요구사항)을 확인해야 한다.
시간제한이 1초라면, 일반적으로 다음과 같을 때 풀 수 있다.
import time
start_time=time.time()
# ( 프로그램 소스코드 )
end_time=time.time()
print("time:", end_time-start_time)
a=5.
print(a) # 5.0
a=-.7
print(a) #-0.7
지수 표현
a=1e9
print(a) # 1000000000.0
a=123e-3
print(a) # 0.123
IEEE754 표준
a=0.1
a+=0.2
print(a) # 0.30000000000000004
round() 함수를 이용해서 처리 a=0.1
a+=0.2
print(round(a,2)) # 0.3
연산
/): 실수형 반환%): 홀수/짝수 체크용//)**): a=3; print(a**0.5) # 1.7320508075688772[]와 , 사용n=5
a=[0]*n # [0, 0, 0, 0, 0]
lst=[i for i in range(10) if i%2==1]
print(lst) # [1, 3, 5, 7, 9]
lst=[i*i for i in range(1,10)]
print(lst) # [1, 4, 9, 16, 25, 36, 49, 64, 81]
⭐ 2차원 리스트를 초기화할 때 좋음
크기의 2차원 리스트 한 번에 초기화 할 때,
즉 N번 반복할 때마다, 길이가 m인 리스트를 초기화하는 방식은
lst=[[0]*m for _ in range(n)]
⭐ 그런데 주의할 점이 있음
만약 2차원 리스트를 위가 아닌 다음과 같이 초기화하면 문제가 발생함
lst=[[0]*m]*n
보기에는 문제가 없어 보이지만, 파이썬은 기본적으로 리스트 자료형으로 변수값을 할당하면, 내부적으로 그 리스트는 객체형태로 처리되고, 별도로 주소 값을 가지게 됨
따라서 이와 같이 리스트를 n번 곱하게 되면, 길이가 m인 리스트를 n번 만큼 참조 값을 복사하는 것과 같아짐
즉 내부적으로 포함되어 있는 [[0]*m] 이 리스트가, 모두 동일한, 같은 객체로 인식하게 되어버림
그렇게되면 내부 리스트에서 특정 위치의 하나의 값만 바꾸더라도, 모든 리스트에 동일하게 변경사항이 적용되어 버림
n, m=3, 3
lst=[[0]*m]*n
print(lst)
# [[0, 0, 0],
# [0, 0, 0],
# [0, 0, 0]]
lst[1][1]=1
print(lst)
# [[0, 1, 0],
# [0, 1, 0],
# [0, 1, 0]]
_ 사용파이썬에서 반복을 수행할 때 반복을 위한 변수의 값을 무시하고자 할 때, 또는 변수를 사용하지 않을때 _를 사용
total=0
for i in range(1,10):
total += i
print(total) # 45
for _ in range(3):
print("Hello", end=" ") # Hello Hello Hello
또 다른 자주 사용되는 메서드
lst.append(): 가장 뒤에 원소 하나 삽입 # 시간복잡도:lst.sort(): 오름차순 정렬 # 시간복잡도:lst.sort(reverse=True): 내림차순 정렬 # 시간복잡도:lst.reverse(): 원소 순서 뒤집기 # 시간복잡도:lst.insert(삽입할 위치 인덱스, 삽입할 값): 특정 인덱스 위치에 원소 삽입 후 한 칸씩 뒤로 미룸 # 시간복잡도:lst.count(특정 값): 특정한 값을 가지는 원소 개수 count # 시간복잡도:lst.remove(특정 값): 특정한 값을 갖는 원소를 제거하는데, 값을 가진 원소가 여러 개면 하나만 제거 # 시간복잡도:
- ⭐ 특정 값 모두 제거할 때는
lst=[1,2,2,3,4,5] set_for_remove={2,3} # 집합 자료형 (특정한 원소의 존재 유무만 체크할 때 효과적으로 사용됨) # set_for_remove에 포함 안되는 값만 골라서 result에 저장 lst_after_remove=[i for i in lst if i not in set_for_remove] print(lst_after_remove) # [1, 4, 5]
+를 이용해서 문자열을 연결할 수 있음a="hello"
b=' world'
print(a+b) # hello world
() 이용dict() 함수를 이용해서 초기화 가능data=dict()
data["사과"]="Apple"
data["바나나"]=Banana"
print(data) # {'사과': 'Apple', '바나나': 'Banana'}
if "사과" in data:
print("'사과'를 키로 가지는 데이터가 존재한다.") # '사과'를 키로 가지는 데이터가 존재한다.
keys()와 values() 라는 함수로 키만, 값만 뽑을 수 있는데, data={
"사과": "Apple",
"바나나": "Banana"
}
print(data) # {'사과': 'Apple', '바나나': 'Banana'}
key_lst=data.keys()
print(key_lst) # dict_keys(['사과', '바나나'])
이때, keys()나, values()는 dict_keys나, dict_values라는 하나의 객체로 반환되기 때문에, 리스트와 같이 반환하고 싶다면 형변환을 해주면 됨
data={
"사과": "Apple",
"바나나": "Banana"
}
print(data) # {'사과': 'Apple', '바나나': 'Banana'}
key_lst=data.keys()
print(key_lst) # dict_keys(['사과', '바나나'])
value_lst=data.values()
print(value_lst) # dict_values(['Apple', 'Banana'])
print(list(value_lst)) # ['Apple', 'Banana']
set() 함수 이용 또는 {} 안에 각 원소를 ,를 기준으로 구분하여 삽입data=set([1,2,2,3,3,4,5]) # {1, 2, 3, 4, 5}data={1,1,2,3,4,5,5} # {1, 2, 3, 4, 5}s1=set([1,2,3,4,5])
s2={3,4,5,6,7}
# 합집합(|)
print(s1|s2) # {1, 2, 3, 4, 5, 6, 7}
# 교집합(&)
print(s1&s2) # {3, 4, 5}
# 차집합(-)
print(s1-s2) {1, 2}
data=set([1,2,3])
data.add(4)
print(data) # {1, 2, 3, 4}
data.update([5,6])
print(data) # {1, 2, 3, 4, 5, 6}
data.remove(3)
print(data) # {1, 2, 4, 5, 6}
⭐ 자료형 정리
리스트나 튜플은 순서가 있기 떄문에 인덱싱을 통해 자료형 값을 얻을 수 있지만,
딕셔너리나 집합은 순서가 없기 때문에 인덱싱으로 값을 얻을 수 없음
사전의 키나 집합의 원소를 이용해 의 시간 복잡도로 조회함
프로그램 동작의 첫 번째 단계는 데이터를 입력 받거나 생성하는 것이다.
일반적으로 코딩테스트 문제들은 특정한 양식으로 입력이 주어진다고 문제에 정해지고, 실제로 문제에서 정해놓은 규칙에 따라서 프로그램의 입력이 주어진다.
예를 들자면, 학생의 성적 데이터가 주어지고 이를 내림차순으로 정렬한 결과를 출력하는 프로그램을 작성하라는 식으로 나온다.
5
60 70 30 90 85
90 85 70 60 30
알고리즘 문제 및 코딩테스트에서 가장 많이 표준 입력 방법은 다음과 같다.
input(): 한 줄의 문자열 입력 받음map(): 리스트의 모든 원소에 각각 특정한 함수를 적용할 때 사용list(map(int, input().split()))n=int(input())
data=input()
print(f"입력받은 n: {n}")
print(f"입력받은 data: {data}")
글이 길어져서 나머지는 다음 글에서 적겠다!