이것이 취업을 위한 코딩 테스트다 with 파이썬 (나동빈 저) 의 책과 강의를 보고 정리한 글입니다.
강의 출처 : https://www.youtube.com/channel/UChflhu32f5EUHlY7_SetNWw

알고리즘의 성능을 나타내는 척도
시간 복잡도와 공간 복잡도가 더 낮을 수록 좋다.
가장 빠르게 증하가는 항만을 고려하는 표기법으로 함수의 상한만을 나타내게 된다.

출처 : 이것이 취업을 위한 코딩 테스트다 with 파이썬 (나동빈 저)
array = [3, 5, 1, 2, 4] # 5개의 데이터 (N=5)
summary = 0 # 합계를 저장할 변수
# 모든 데이터를 하나씩 확인하며 합계를 계산
for x in array:
summary += x
# 결과를 출력
print(summary)
수행 시간은 데이터의 개수 N에 비례할 것임을 예측할 수 있다.
array = [3, 5, 1, 2, 4] # 5개의 데이터(N=5)
for i in array:
for j in array:
temp = i * j
print(temp)
일반적으로 CPU 기반의 개인 컴퓨터가 연산 횟수가 5억을 넘어가는 경우:
C 기준 통산 1~3초 Python 기준 5~15초 가량 소요
PyPy의 경우 때때로 C보다 빠를수도?
O(N^3)의 알고리즘의 경우 N의 값이 5000이 넘는다면 엄청난 시간이 걸린다. 코딩 테스트 문제는 대부분 1~5초 제한이 있으므로 유의하자
문제에서 가장 먼저 확인해야 할 내용은 시간제한(수행시간 요구사항)이다.
시간제한이 1초인 문제를 만났을 때, 일반적인 기준
import time
start_time = time.time() # 측정 시작
# 프로그램 소스코드
end_time = time.time() # 측정 종료
print("time:", end_time - start_time) # 수행 시간 출력
파이썬에서는 e나 E를 이용한 지수 표현 방식을 이용할 수 있다.

최단 경로 알고리즘에서는 도달할 수 없는 노드에 대하여 최단 거리를 무한(INF)로 설정하곤 한다. 이때 가능한 최댓값이 10억 미만이라면 무한(INF)의 값으로 1e9를 이용할 수 있다.
지수 표현 방식은 기본적으로 실수 데이터로 들어가기 때문에 출력을 해보면 실수형 데이터로 출력된다.
컴퓨터 시스템은 실수 정보를 표현하는 정확도에 한계를 가진다. 10진수에서는 0.9를 표현할 수 있지만 컴퓨터에서는 0.9와 가까운 수로 미세한 오차를 낸다.
따라서 round()함수를 이용하여 반올림해주는 것이 좋다.
파이썬에서 나누기 연산자(/)는 나눠진 결과를 실수형으로 반환하는 것에 주의하자.
# 직접 데이터를 넣어 초기화
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(a)
# 크기가 N이고, 모든 값이 0인 1차원 리스트 초기화
n = 10
a = [0] * n
print(a)

# 0부터 9까지의 수를 포함하는 리스트
array = [i for i in range(10) if i % 2 == 1]
print(array)
좋은 예)
# N번 반복할 때마다 길이가 m인 리스트를 새롭게 초기화하는 리스트
array = [[0] * m for _ in range(n)]

array = [[0] * m] * n

위 코드는 전체 리스트 안에 포함된 각 리스트가 모두 같은 객체로 인식된다.

a = [1, 2, 3, 4, 5, 5, 5]
remove_set = {3, 5} # 집합 자료형
# remove_list에 포함되지 않은 값만을 저장
result = [i for i in a if i not in remove_set]
print(result)
서로 다른 성질의 데이터를 묶어서 관리해야 할 때 - 최단 경로 알고리즘에서는 (비용, 노드 번호)의 형태로 튜플 자료형을 자주 사용한다.
데이터의 나열을 해싱(Hashing)의 키 값으로 사용해야 할 때 (사전 자료형, 집합 자료형 등에서 해싱의 키 값으로 데이터의 묶음, 나열을 사용해야 할 때) - 튜플은 변경이 불가능하므로 리스트와 다르게 키 값으로 사용될 수 있다.
리스트보다 메모리를 효율적으로 사용해야 할 때
참고) 튜플 자료형은 리스트와 유사하지만 한 번 선언된 값을 변경할 수 없고, 리스트에 비해 상대적으로 공간 효율적이다.
키와 값의 쌍으로 데이터를 가진다. - 리스트나 튜플이 순차적으로 저장하는 것과 대비
자료의 순서가 없기 때문에 인덱싱으로 값을 얻을 수 없다.
원하는 '변경 불가능한(immutable) 자료형'을 키로 사용할 수 있다.
파이썬의 사전 자료형은 해시 테이블(Hash Table)을 이용하므로 데이터의 조회 및 수정에 있어서 O(1)의 시간에 처리할 수 있다.
중복을 허용하지 않고, 순서가 없기 때문에 데이터의 존재 여부를 확인할 때 유용하다.
자료의 순서가 없기 때문에 인덱싱으로 값을 얻을 수 없다.
리스트 혹은 문자열을 이용하여 초기화할 수 있다. - set() 함수 이용
데이터의 조회 및 수정에 있어서 O(1)의 시간에 처리할 수 있다.
input() : 한줄의 문자열을 입력
map() : 리스트의 모든 원소에 각각 특정한 함수를 적용할 때 사용
예시) 공백을 기준으로 구분된 데이터를 입력 받을 경우
list(map(int, input().split()))
예시) 공백을 기준으로 구분된 데이터가 무조건 3개 입력된다고 한다면 다음과 같이 할 수 있다.
a, b, c = map(int, input().split()))
# 데이터의 개수 입력
n = int(input())
# 각 데이터를 공백을 기준으로 구분하여 입력
data = list(map(int, input().split()))
data.sort(reverse=True)
print(data)
사용자로부터 입력을 최대한 빠르게 받아야 하는 경우가 있다.
파이썬의 경우 sys 라이브러리에 정의되어 있는 sys.stdin.readline() 메서드를 이용한다. 단, 입력 후 엔터가 줄 바꿈 기호로 입력되므로 rstrip() 메서드를 함께 사용한다.
입력의 개수가 매우 많은 문제는 입력을 받는 것만으로 많은 시간이 소요되어 문제가 생길 수 있는데 readline()으로 해결한다.
실제로 이진탐색, 정렬, 그래프 관련 문제에서 자주 사용되는 테크닉이다.
import sys
# 문자열 입력 받기
data = sys.stdin.readline().rstrip()
print(data)
파이썬에서 기본 출력은 print() 함수를 이용한다. 각 변수를 ,를 이용하여 띄어쓰기로 구분하여 출력할 수 있다.
print()는 기본적으로 출력 이후에 줄 바꿈을 수행한다. 줄 바꿈을 원치 않는 경우 'end' 속성을 이용할 수 있다.
코딩 테스트에서 무한 루프를 구현할 일은 거의 없으니 유의
반복문을 작성한 뒤에는 항상 반복문을 탈출할 수 있는지 확인
def operator(a, b):
add = a+b
sub = a-b
mul = a*b
div = a/b
return add, sub, mul, div
data = list(operator(7, 3))
print(data)
print((lambda a, b: a+b)(3, 7))
함수 자체를 입력으로 받는 또 다른 함수가 존재할 때 유용하게 사용할 수 있다.
내장함수 특히 sorted와 같은 정렬 함수를 사용할 때 람다 함수를 사용한다.
array = [('홍길동', 50), ('이순신', 32), ('아무개', 74)]
def my_key(x):
return x[1]
print(sorted(array, key=my_key))
print(sorted(array, key=lambda x: x[1]))
list1 = [1, 2, 3, 4, 5]
list2 = [6, 7, 8, 9, 10]
result = map(lambda a, b: a+b, list1, list2)
print(list(result))
itertools : 파이썬에서 반복되는 형태의 데이터를 처리하기 위한 유용한 기능 제공 - 특히 순열과 조합 라이브러리는 코딩 테스트에서 자주 사용
heapq: 힙(Heap) 자료구조를 제공 - 우선순위 큐 기능을 구현하기 위해 사용
bisect: 이진 탐색(Binary Search) 기능을 제공합니다.
collections: 덱(deque), 카운터(Counter) 등의 유용한 자료구조 포함
math: 필수적인 수학적 기능 제공 - 펙토리얼, 제곱근, 최대공약수, 삼각함수, 파이(pi) 등
sum(), min(), max()
eval() : 사람의 입장에서 작성된 식을 실제 수 형태로 계산
# eval()
result = eval("(+5)*7")
print(result)
모든 경우의 수를 고려해야 할 때 어떤 라이브러리를 효과적으로 사용할 수 있을까?
순열 : 서로 다른 n개에서 서로 다른 r개를 선택하여 일렬로 나열하는 것
{a,b,c} 에서 3개를 선택하여 나열하는 경우 : abc, acb, bac, bca, cab, cba
조합 : 서로 다른 n개에서 순서에 상관 없이 서로 다른 r개를 선택하는 것
{a,b,c} 에서 순서를 고려하지 않고 두 개를 뽑는 경우 : ab, ac, bc

from itertools import permutations
data = ['a', 'b', 'c']
# 모든 순열 구하기
result = list(permutations(data, 3))
print(result)
from itertools import product
data = ['a', 'b', 'c']
# 2개를 뽑는 모든 순열 구하기 (중복 허용)
result = list(product(data, repeat=2))
print(result)
from itertools import combinations_with_replacement
data = ['a', 'b', 'c']
# 2개를 뽑는 모든 조합 구하기 (중복 허용)
result = list(combinations_with_replacement(data, 2))
print(result)
from collections import Counter
counter = Counter(['red', 'blue', 'red', 'green', 'blue', 'blue'])
print(counter['blue'])
print(counter['red'])
print(dict(counter)) # 사전 자료형으로 반환
import math
# 최소 공배수(LCM)를 구하는 ㅎ마수
def lcm(a, b):
return a * b // math.gcd(a, b)
a = 21
b = 14
print(math.gcd(a, b)) # 최대 공약수(GCD) 계산
print(lcm(a, b)) # 최소 공배수(LCM) 계산