Latest commit

History

165 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

CodingTest with Python

Python 문법 및 알고리즘 정리 블로그링크


코딩테스트 시간복잡도

제한시간 1초(n초) = 10^8번 연산 (100,000,000번)
따라서 입력값 n의 크기에 따라 시간복잡도를 고려하여 10^8번 연산 이내로 풀릴 수 있어야 한다.

n <= 20

O(n!), O(2^n)

  • 어떤 알고리즘이든 가능

n <= 100

O(n^3)

  • 3중 for문
  • 플로이드 워셜 알고리즘
  • 완전탐색

n <= 1000

O(n^2)

  • 2중 for문
  • 벨만 포드 알고리즘
  • 선택 정렬
  • 삽입 정렬

n <= 10000 ⭐️

O(n)

  • 동적 프로그래밍(DP)
  • 계수 정렬
  • 유니언 파인드
  • 위상 정렬

O(n log n)

  • 이진 탐색
  • 파라메트릭 서치
  • 다익스트라 알고리즘
  • 세그먼트 트리
  • 투 포인터
  • 퀵 정렬
  • 크루스칼 알고리즘

n <= 10^8

O(log n)

  • 수학적 기믹이 필요
  • 유클리드 호제법

파이썬 라이브러리

itertools (순열, 조합) ⭐️⭐️

# 순열fromitertoolsimportpermutationforcaseinpermutations(array, len(array)):
...
# 조합fromitertoolsimportcombinationsforcaseincombinations(array, len(array)):
...

functools (reduce)

# reducefromfunctoolsimportreduceresult=reduce(lambdax, y: x*y, array)

collections (deque(queue)) ⭐️

fromcollectionsimportdequequeue=deque() # queue 생성queue.append('a') # pushqueue[0] # peak 'a'queue.popleft() # pop 'a'queue.extend(['b', 'c', 'd']) # 배열 push

heapq (priorityQueue) ⭐️

importheapqpq= [] # priorityQueue 생성heapq.heappush(pq, (0, 1)) # pushpq[0] # peak (0, 1)heapq.heappop(pq) # pop (0, 1)array= [b, c, a]
heapq.heapify(array) # array를 priorityQueue로 생성heapq.heappop(array) # pop 'a'

math (gcd)

importmathvalue=math.gcd(3, 6)

copy

importcopyarray2=copy.deepcopy(array)

sys (readline)

importsysinput=sys.stdin.readline()

Python 기본 자료구조

Stack

stack= [] # stack 생성stack.append('a') # pushstack[0] # peakstack.pop() # pop 'a'

Set

s=set() # set 생성s1=set(['a', 'b', 'c']) # array를 set으로 생성s2=set(['b', 'c', 'd'])
s1.add('c') # pushs1.remove('c') # removes1.update(['a', 'b', 'c']) # 배열 pushs1&s2# 교집합 {'b', 'c'}s1|s2# 합집합 {'a', 'b', 'c', 'd'}s1-s2# 교집합을 제거한 s1 {'a'}
{ xforxin'abcde' } # list comprehension {'a', 'b', 'c', 'd', 'e'}'a'ins1# Truelist(s1) # set to list ['a', 'b', 'c']

Dictionary

d= {} # dictionary 생성d= {'a': 1, 'b': 2}
d=dict([('a', 1), ('b', 2)]) # touble array로 생성d_from_list= { x: ord(x) forxin'abc' } # dictionary comprehension {'a': 97, 'b': 98, 'c': 99}d['a'] # value 접근 1d['a'] =3# value updatedeld['a'] # value delete'a'ind# Truelist(d) # ['a', 'b']d.items(): # [(key, value)] 반환 [('b': 2)]d.keys() # ['b']d.values() # [2]forkey, valueinitems(): # key, value 순차접근print(key, value)

List

array= [] # list 생성array.append('a') # pusharray.remove('a') # deletearray=list(range(0, 5)) # [0, 1, 2, 3, 4]array.index(3) # index 3array.count(3) # 1array[-1] # 4array[:-1] # return [0, 1, 2, 3]array[1:-1] # return [1, 2, 3]array[::2] # return [0, 2, 4]array[-1::-2] # return [4, 2, 0]array[1:-1][::-1] # return [3, 2, 1]temp=array+ [5, 6] # return [1, 2, 3, 4, 5, 6]array+= [5, 6] # update [1, 2, 3, 4, 5, 6]# 2차원배열n, m=2, 3graph= [[0]*mfor_inrange(n)]
# [[0, 0, 0],# [0, 0, 0]]graph[n-1][m-1] =1# [[0, 0, 0],# [0, 0, 1]]

Tuple

tuple= ('a', 1) # tuple 생성tuple[0] # 'a'tuple[1] # 1

String

str(1) # '1'str(1)+str(2) # '12'string='abc'string.swapcase() # return 'ABC'string.isupper() # return Falsestring.islower() # return Truestring.upper() # return 'ABC'string.lower() # return 'abc'string='123'string.isdecimal() # return Truestring=' abcd 'string.strip() # return 'abcd'

range

list(range(0, 5)) # [0, 1, 2, 3, 4]foriinrange(0, 5):
print(i, end=", ") # 0, 1, 2, 3, 4,

파이썬 기능들

sort

# list.sort()array= [1, 4, 5, 2, 3]
array.sort() # update [1, 2, 3, 4, 5]array.sort(key=lambdax: -x) # update [5, 4, 3, 2, 1]array.sort(key=lambdax: abs(x-3)) # update [3, 4, 2, 5, 1]array.sort(reverse=True) # update [5, 4, 3, 2, 1]

sorted

# sorted(array)array= [1, 4, 5, 2, 3]
sorted(array) # return [1, 2, 3, 4, 5]sorted(array, key=lambdax: -x) # return [5, 4, 3, 2, 1]sorted(array, reverse=True) # return [5, 4, 3, 2, 1]sorted({1: 'a', 2: 'b'}) # dictionary: return [1, 2]sorted([('a', 3, 10), ('b', 1, 9), ('c', 2, 9)], key=lambdax: (x[2], x[1])) # tuple: return [('b', 1, 9), ('c', 2, 9), ('a', 3, 10)]

filter

array= [1, 2, 3, 4, 5]
list(filter(lambdax: x>2, array)) # [3, 4, 5]

enumerate

array [1, 2, 3]
foridx, valueinenumerate(array):
print(idx, value, end=",") # 0 1,1 2,2 3,

sum

# 총합total=sum([1, 2, 3]) # 6# 1차원 list화array=sum([[1, 2], [3, 4]], []) # [1, 2, 3, 4]

join

# string.join()''.join(['a', 'b', 'c']) # return 'abc'

replace

# string.replace()'abcde'.replace('a', 'A') # return 'Abcde'

ord

counts= [0]*26forcharin'abcde':
counts[ord(char)-ord('a')] +=1

abs

abs(-4) # 4

map

a, b=map(int, input().split())

Infinity

INF=int(1e9)

3항연산자

num=5odd=Trueifnum%2==1elseFalse# True

코딩테스트 알고리즘

그리디

가장 큰 순서대로, 가장 작은 순서대로 와 같은 기준 (정렬 알고리즘과 한쌍)
큰 단위가 작은 단위의 배수형태여야 한다. 아닌 경우 다이나믹 프로그래밍으로 접근

  • while문 대신 몫을 계산하는 식으로 접근

구현 ⭐️

완전탐색 or 시뮬레이션

move= [(0, 1), (1, 0), (0, -1), (-1, 0)]
ny, nx=y+move[direction][0], x+move[direction][1]
ifny<0orny>=nornx<0ornx>=n:
direction= (direction+1)%4y, x=y+move[direction][0], x+move[direction][1]
dict= {'left': (-1, 0), 'right': (1, 0), 'up': (0, 1), 'down': (0, -1)}
nx, ny=x+dict[key][0], y+dict[key][1]
ifnx<-nornx>n:

그래프

노드와 노드 사이에 연결된 간선 정보를 가지고 있는 자료구조

인접행렬

n, m=map(int, input().split())
graph= []
forrowinrange(n):
line= [int(x) forxinlist(input())]
graph.append(line)

인접 리스트

n, m=map(int, input().split())
graph= [[] for_inrange(n)]
graph[0].append((1, 7)) #노드 1, cost 7graph[1].append((0, 7)) #노드 0, cost 7

DFS/BFS

스택, 큐(deque: from collections) 자료구조 필요
재귀: 스택구조, 종료 조건이 필요

DFS: 스택(재귀)

  • 방문하지 않은 경우 재귀를 통해 새로운 노드 방문
defdfs(graph, v, visited):
visited[v] =Trueforiingraph[v]:
ifnotvisited[i]:
dfs(graph, i, visited)

BFS: 큐(deque)

  • 방문하지 않은 경우 queue 에 추가, queue 순서에 맞게 노드 방문
defbfs(graph, start, visited):
queue=deque([start])
visited[start] =Truewhilequeue:
v=queue.popleft()
foriingraph[v]:
ifnotvisited[i]:
queue.append(i)
visited[i] =True

선택 정렬

O(n^2)

defsort(array):
foriinrange(len(array)):
min_index=i;
forjinrange(i+1, len(array)):
ifarray[min_index] >array[j]:
min_index=j;
array[i], array[min_index] =array[min_index], array[i]
returnarray

삽입 정렬

O(n^2) but 데이터가 거의 정렬되어 있을 때 효율적
정렬된 상태 + 적절한 위치 찾아 삽입하여 정렬

defsort(array):
foriinrange(1, len(array)):
forjinrange(i, 0, -1): # i to 1 까지 -1씩 감소ifarray[j] <array[j-1]:
array[j], array[j-1] =array[j-1], array[j]
else:
breakreturnarray

퀵 정렬 ⭐️

O(n log n) but 데이터가 거의 정렬되어 있을 때 비효율적
pivot 을 기준으로 작은, 데이터 좌우 분리, 분리된 좌, 우 역시 동일 방식으로 정렬

좌 → 우: pivot 보다 큰 데이터의 위치(left), 좌 ← 우: pivot 보다 작은 데이터의 위치(right)를 찾아 위치를 서로 변경
작은 데이터 위치(left)가 큰 데이터 위치(right)보다 크거나 같은 경우: pivot, 작은 데이터 switch, 종료

재귀 종료조건: 리스트가 1개인 경우 정렬된 상태이므로 반환
pivot 선택기준: 첫번째 데이터 (호어 분할 방식)

defsort(array):
quick_sort(array, 0, len(array)-1)
defquick_sort(array, start, end):
ifstart>=end: # 원소가 1개인 경우 종료returnpivot=startleft=start+1right=endwhileleft<=right:
whileleft<=endandarray[left] <=array[pivot]:
left+=1# find 큰값 indexwhileright>startandarray[right] >=aray[pivot]:
right-=1# find 작은값 indexifleft>right: # 엇갈린 경우: pivot, 작은값 switcharray[right], array[pivot] =array[pivot], array[right]
pivot=rightelse: # 큰값, 작은값 switch, 이후 while 통해 다음 switch 진행array[left], array[right] =array[right], array[left]
# pivot 보다 작은 값들 | pivot | pivot 보다 큰 값들quick_sort(array, start, pivot-1)
quick_sort(array, pivot+1, end)
defsort(array):
iflen(array) <=1:
returnarraypivot=array[0]
tail=array[1:]
left_side= [xforxintailifx<=pivot]
right_side= [xforxintailifx>pivot]
returnsort(left_side) + [pivot] +sort(right_side)

병합 정렬

O(n log n) 분할 정복(divide and conqure) 방법을 통해 크가 같은 두 리스트로 분할 후 각각 정렬한 결과를 합병
분할하는 횟수는 절반식 줄어들기에 O(logN) 시간이 소모된다.
병합할 때 모든 값들을 비교해야 하기 때문에 O(N) 시간이 소모된다.
퀵정렬보다는 느리지만 최악의 경우도 O(n log n)보장

defmerge_sort(arr):
iflen(arr) <2:
returnarrmid=len(arr) //2low_arr=merge_sort(arr[:mid])
high_arr=merge_sort(arr[mid:])
merged_arr= []
l=h=0whilel<len(low_arr) andh<len(high_arr):
# 좌측이 더 작은 경우 -> 좌측 merged에 추가iflow_arr[l] <high_arr[h]:
merged_arr.append(low_arr[l])
l+=1# 우측이 더 작은 경우 -> 우측 merged에 추가else:
merged_arr.append(high_arr[h])
h+=1merged_arr+=low_arr[l:]
merged_arr+=high_arr[h:]
returnmerged_arr

계수 정렬

O(n + k) (k: 가장큰 수)
범위가 정해진 정수, 중복이 많은 경우 효율적
위치변경 X, 리스트 정보를 통한 정렬

defsort(array):
counts= [0]*(max(array)+1)
foriinrange(len(array)):
counts[array[i]] +=1sorted= []
foriinrange(len(counts)):
sorted+= [i] *counts[i]
returnsorted

순차 탐색

O(n) 앞에서부터 차례로 확인

defsearch(array, target):
foriinrange(len(array)):
ifarray[i] ==target:
returnireturn-1

이진 탐색 ⭐️

O(log n) 정렬된 배열 내에서 중간점 위치 데이터와 비교하며 반복적으로 절반씩 좁혀가며 찾는다
재귀 형식과 반복문 형식이 있는데, stack에 쌓이지 않는 반복문 방식이 더 좋다.

defbinary_search(array, target, start, end):
whilestart<=end:
mid= (start+end) //2ifarray[mid] ==target:
returnmideliftarget<array[mid]:
end=mid-1else:
start=mid+1return-1

파라메트릭 서치 (Parametric Search) ⭐️

주어진 범위 내에서 원하는 값 또는 원하는 조건에 가장 일치하는 값을 찾아내는 알고리즘
최적화 문제(최댓값 x = ?) -> 결정문제(x일 경우 최댓값?)로 바꾸어 풀 수 있다.

  • 범위의 중간값과 조건을 비교하며 범위를 수정해나간다
  • 이진 탐색과 차이점은 값 비교 -> 조건 비교, 반환값 (mid, -1 -> result (현재까지의 최적값))이 다르다는 점.
# 최적화문제: 24시간을 7시간마다 배고파지는 사람이 최소 몇끼니를 먹어야 배부르게 지낼수있는가?# 결정문제: 7시간마다 배고파지는 사람이 x끼니를 먹으면 24시간을 배부르게 지낼 수 있다.defparametricSearch(start, end) {
result=0while (start<=end):
mid= (start+end) /2if (24/mid) <7:
end=mid-0.1else:
start=mid+0.1result=midreturnresult
}

파라메트릭 서치 블로그

이진 탐색 트리

이진 탐색이 동작할 수 있도록 고안된 트리 자료구조
child-left < parent < child-right 식

다이나믹 프로그래밍

큰 문제를 작은 문제로 나눌 수 있는 경우 동일문제를 중복되지 않게 계산하는 방법
Bottom up(반복문), Top down(재귀) 방식
메모이제이션: 계산된 결과들을 메모하는 기법 (캐싱)
O(n)

d= [0] *100defpibo(x):
d[1] =1d[2] =1foriinrange(3, x+1):
d[i] =d[i-1] +d[i-2]

다익스트라

시작 노드로부터 각 노드까지의 최단거리 알고리즘 (음의 간선이 없는 경우)
반복적으로 최소 최단거리의 노드를 선택하며 테이블을 갱신 (heapq 사용)
O(E log V)

  • V: 노드수, E: 간선수
  • visited: [False] * (n+1)
  • distance: [INF] * (n+1)
defdijkstra(graph, startNode):
distance= [INF]*(len(graph)+1)
distance[startNode] =0q= []
# q: (distance, node), distance 가 적은 node 순으로 정렬된다heapq.heappush(q, (0, startNode))
whileq:
currentDistance, currentNode=heapq.heappop(q)
# currentNode 의 distance 가 갱신된적이 있는 경우 무시ifdistance[currentNode] <currentDistance:
continue# currentNode 의 인접노드들 확인fornodeingraph[currentNode]:
nodeNum=node[0]
nodeCost=node[1]
newDistance=currentDistance+nodeCostifnewDistance<distance[nodeNum]:
distance[nodeNum] =newDistanceheapq.heappush(q, (newDistance, nodeNum))
returndistance
importsysimportheapqinput=sys.stdin.readlineINF=int(1e9)
n, m=map(int, input().split())
startNode=int(input())
graph= [[] foriinrange(n+1)]
for_inrange(m):
from, to, cost=map(int, input().split())
graph[from].append((to, cost))
distance=dijkstra(graph, startNode)
foriinrange(1, n+1):
ifdistance[i] ==INF:
print("INF")
else:
print(distance[i])

플로이드 워셜

여러개의 from, to 노드간 최단거리 구하기 알고리즘
점화식: AtoB = min(AtoB, AtoK + KtoB)
k, a, b 순차적으로 3중 for 구조
O(n^3)

deffloydWarshall(graph):
forkinrange(1, n+1):
forainrange(1, n+1):
forbinrange(1, n+1):
graph[a][b] =min(graph[a][b], graph[a][k] +graph[k][b])
graph[b][a] =graph[a][b]
importsysinput=sys.stdin.readline()
INF=int(1e9)
n, m=map(int, input().split())
graph= [[]]*(n+1) for_inrange(n+1)]
forainrange(1, n+1):
forbinrange(1, n+1):
ifa==b:
graph[a][b] =0graph[b][a] =0for_inrange(m):
a, b, cost=map(int, input().split())
graph[a][b] =costgraph[b][a] =costfloydWarshall(graph)
forainrange(1, n+1):
forbinrange(1, n+1):
val=graph[a][b] ifgraph[a][b] !=INFelse"INF"print(val, end=" ")
print()

유니언 파인드 ⭐️

공통 원소가 없는 집합이 필요한 경우
하나의 집합으로 합치는 union 과 원소가 속한 집합을 알려주는 find 연산이 필요
union 의 경우 작은 노드값이 집합의 값이 된다.

# 노드값과 집합값이 같을때까지 재귀적으로 호출deffindParent(parents, x):
ifparents[x] !=x:
# 경로 압축parents[x] =findParent(parents, parants[x])
returnparents[x]
defunionParent(parents, a, b):
setA=findParent(parents, a)
setB=findParent(parents, b)
ifsetA<setB:
parents[b] =setAelse:
parents[a] =setB
v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
unionParent(parents, a, b)
foriinrange(1, v+1):
print(findParent(parents, i), end=" ")
print()

사이클 판별

유향 그래프: DFS 알고리즘
무향 그래프: 유니언 파인드 집합 알고리즘

# 무향그래프에서 사이클 판별v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
cycle=Falseforiinrange(e):
a, b=map(int, input().split())
iffindParent(parents, a) ==findParent(parents, b):
cycle=Truebreakelse:
unionParent(parents, a, b)
ifcycle:
print("사이클 발생")
else:
print("사이클 비발생")

신장 트리

모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프
모든 도시를 연결할 때 최소 비용과 관련된 크루스칼 알고리즘의 경우 최소 신장 트리를 활용한 알고리즘

최소 신장 트리를 구하기 위한 알고리즘

  • 신장 트리(Spanning tree): 모든 정점을 포함, 정점간 서로 연결, 싸이클이 존재하지 않는 그래프
  • 최소 신장 트리(Minimum Spanning Tree): 신장 트리 중 가중치 합이 최소인 그래프
  • 싸이클 여부: 서로소 집합(Disjoint Set)Union & Find 활용 크루스칼 알고리즘 블로그

크루스칼 알고리즘

경로를 최소한의 비용으로 연결하는 알고리즘
그래프 내 모든 정점들을 가장 적은 비용으로 연결하기 위해 사용 (사이클이 없는, 가중치의 합 최소가 되는 상태)
모든 간선에 대해 비용값 기준 정렬, 사이클이 발생하지 않는 간선부터 집합에 포함
무향 그래프이므로 find, union 을 통해 사이클 여부 판별 후 간선비용 총합
O(E log E)

v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
edges= [(a, b, cost) for_inrange(e) a, b, cost=map(int, iniput().split())]
result=0edges.sort()
foredgeinedges:
cost, a, b=edgeiffindParent(parents, a) !=findParent(parents, b):
unionParent(parents, a, b)
result+=costprint(result)

위상정렬

우선순위(선후관계)에 따라 방향성에 거스르지 않도록 순서대로 나열
진입차수 테이블과 queue(deque)가 필요

  1. 진입차수가 0인 노드를 큐에 넣는다
  2. 큐가 빌때까지 꺼내며 해당 노드부터 출발하는 간선을 제거하며 진입차수가 0인 노드를 큐에 넣는다 O(V+E)
deftopologySort(graph, indegree):
result= []
q=deque()
# 처음 시작시 indegree 값이 0인 노드를 넣고 시작foriinrange(1, v+1):
ifindegree[i] ==0:
q.append(i)
whileq:
node=q.popleft()
result.append(node)
foriingraph[node]:
indegree[i] -=1ifindegree[i] ==0:
q.append(i)
returnresult
fromcollectionsimportdequev, e=map(int, input().split())
indegree= [0] * (v+1)
graph= [[] foriinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
graph[a].append(b)
indegree[b] +=1sorted=topologySort(graph, indegree)
forninresult:
print(n, end=" ")

소수 알고리즘

2부터 n-1까지 나누어 떨어지는 수가 없는 경우 소수 판별

defisPrime(n):
ifn<2:
returnFalseforiinrange(2, n):
ifn%i==0:
returnFalsereturnTrue

에라토스테네스의 체

정해진 범위 내 소수를 구하는 경우 사용되는 알고리즘
범위 내 숫자들에서 합성수를 반복적으로 지우는 방식으로 소수를 찾는다.

  • 지워지지 않은수 중 작은수를 소수로 채택, 해당 수의 배수를 모두 제거
defprime_list(n):
prime= [True]*(n+1)
foriinrange(2, int(n**0.5)+1):
ifprime[i] ==True:
forjinrange(2*i, n+1, i):
prime[j] =Falsereturn [iforiinrange(2, n+1) ifprime[i] ==True]

에라토스테네스의 체 블로그1에라토스테네스의 체 블로그2

약수구하기

1부터 루트n까지 n을 나누는 수들을 확인

defdivisors(n):
result= []
foriinrange(1, int(n**0.5)+1):
ifn%i==0:
result.append(i)
result.append(n/i)
returnset(result)

About

파이썬 코딩테스트

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

165 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

CodingTest with Python

Python 문법 및 알고리즘 정리 블로그링크


코딩테스트 시간복잡도

제한시간 1초(n초) = 10^8번 연산 (100,000,000번)
따라서 입력값 n의 크기에 따라 시간복잡도를 고려하여 10^8번 연산 이내로 풀릴 수 있어야 한다.

n <= 20

O(n!), O(2^n)

  • 어떤 알고리즘이든 가능

n <= 100

O(n^3)

  • 3중 for문
  • 플로이드 워셜 알고리즘
  • 완전탐색

n <= 1000

O(n^2)

  • 2중 for문
  • 벨만 포드 알고리즘
  • 선택 정렬
  • 삽입 정렬

n <= 10000 ⭐️

O(n)

  • 동적 프로그래밍(DP)
  • 계수 정렬
  • 유니언 파인드
  • 위상 정렬

O(n log n)

  • 이진 탐색
  • 파라메트릭 서치
  • 다익스트라 알고리즘
  • 세그먼트 트리
  • 투 포인터
  • 퀵 정렬
  • 크루스칼 알고리즘

n <= 10^8

O(log n)

  • 수학적 기믹이 필요
  • 유클리드 호제법

파이썬 라이브러리

itertools (순열, 조합) ⭐️⭐️

# 순열fromitertoolsimportpermutationforcaseinpermutations(array, len(array)):
...
# 조합fromitertoolsimportcombinationsforcaseincombinations(array, len(array)):
...

functools (reduce)

# reducefromfunctoolsimportreduceresult=reduce(lambdax, y: x*y, array)

collections (deque(queue)) ⭐️

fromcollectionsimportdequequeue=deque() # queue 생성queue.append('a') # pushqueue[0] # peak 'a'queue.popleft() # pop 'a'queue.extend(['b', 'c', 'd']) # 배열 push

heapq (priorityQueue) ⭐️

importheapqpq= [] # priorityQueue 생성heapq.heappush(pq, (0, 1)) # pushpq[0] # peak (0, 1)heapq.heappop(pq) # pop (0, 1)array= [b, c, a]
heapq.heapify(array) # array를 priorityQueue로 생성heapq.heappop(array) # pop 'a'

math (gcd)

importmathvalue=math.gcd(3, 6)

copy

importcopyarray2=copy.deepcopy(array)

sys (readline)

importsysinput=sys.stdin.readline()

Python 기본 자료구조

Stack

stack= [] # stack 생성stack.append('a') # pushstack[0] # peakstack.pop() # pop 'a'

Set

s=set() # set 생성s1=set(['a', 'b', 'c']) # array를 set으로 생성s2=set(['b', 'c', 'd'])
s1.add('c') # pushs1.remove('c') # removes1.update(['a', 'b', 'c']) # 배열 pushs1&s2# 교집합 {'b', 'c'}s1|s2# 합집합 {'a', 'b', 'c', 'd'}s1-s2# 교집합을 제거한 s1 {'a'}
{ xforxin'abcde' } # list comprehension {'a', 'b', 'c', 'd', 'e'}'a'ins1# Truelist(s1) # set to list ['a', 'b', 'c']

Dictionary

d= {} # dictionary 생성d= {'a': 1, 'b': 2}
d=dict([('a', 1), ('b', 2)]) # touble array로 생성d_from_list= { x: ord(x) forxin'abc' } # dictionary comprehension {'a': 97, 'b': 98, 'c': 99}d['a'] # value 접근 1d['a'] =3# value updatedeld['a'] # value delete'a'ind# Truelist(d) # ['a', 'b']d.items(): # [(key, value)] 반환 [('b': 2)]d.keys() # ['b']d.values() # [2]forkey, valueinitems(): # key, value 순차접근print(key, value)

List

array= [] # list 생성array.append('a') # pusharray.remove('a') # deletearray=list(range(0, 5)) # [0, 1, 2, 3, 4]array.index(3) # index 3array.count(3) # 1array[-1] # 4array[:-1] # return [0, 1, 2, 3]array[1:-1] # return [1, 2, 3]array[::2] # return [0, 2, 4]array[-1::-2] # return [4, 2, 0]array[1:-1][::-1] # return [3, 2, 1]temp=array+ [5, 6] # return [1, 2, 3, 4, 5, 6]array+= [5, 6] # update [1, 2, 3, 4, 5, 6]# 2차원배열n, m=2, 3graph= [[0]*mfor_inrange(n)]
# [[0, 0, 0],# [0, 0, 0]]graph[n-1][m-1] =1# [[0, 0, 0],# [0, 0, 1]]

Tuple

tuple= ('a', 1) # tuple 생성tuple[0] # 'a'tuple[1] # 1

String

str(1) # '1'str(1)+str(2) # '12'string='abc'string.swapcase() # return 'ABC'string.isupper() # return Falsestring.islower() # return Truestring.upper() # return 'ABC'string.lower() # return 'abc'string='123'string.isdecimal() # return Truestring=' abcd 'string.strip() # return 'abcd'

range

list(range(0, 5)) # [0, 1, 2, 3, 4]foriinrange(0, 5):
print(i, end=", ") # 0, 1, 2, 3, 4,

파이썬 기능들

sort

# list.sort()array= [1, 4, 5, 2, 3]
array.sort() # update [1, 2, 3, 4, 5]array.sort(key=lambdax: -x) # update [5, 4, 3, 2, 1]array.sort(key=lambdax: abs(x-3)) # update [3, 4, 2, 5, 1]array.sort(reverse=True) # update [5, 4, 3, 2, 1]

sorted

# sorted(array)array= [1, 4, 5, 2, 3]
sorted(array) # return [1, 2, 3, 4, 5]sorted(array, key=lambdax: -x) # return [5, 4, 3, 2, 1]sorted(array, reverse=True) # return [5, 4, 3, 2, 1]sorted({1: 'a', 2: 'b'}) # dictionary: return [1, 2]sorted([('a', 3, 10), ('b', 1, 9), ('c', 2, 9)], key=lambdax: (x[2], x[1])) # tuple: return [('b', 1, 9), ('c', 2, 9), ('a', 3, 10)]

filter

array= [1, 2, 3, 4, 5]
list(filter(lambdax: x>2, array)) # [3, 4, 5]

enumerate

array [1, 2, 3]
foridx, valueinenumerate(array):
print(idx, value, end=",") # 0 1,1 2,2 3,

sum

# 총합total=sum([1, 2, 3]) # 6# 1차원 list화array=sum([[1, 2], [3, 4]], []) # [1, 2, 3, 4]

join

# string.join()''.join(['a', 'b', 'c']) # return 'abc'

replace

# string.replace()'abcde'.replace('a', 'A') # return 'Abcde'

ord

counts= [0]*26forcharin'abcde':
counts[ord(char)-ord('a')] +=1

abs

abs(-4) # 4

map

a, b=map(int, input().split())

Infinity

INF=int(1e9)

3항연산자

num=5odd=Trueifnum%2==1elseFalse# True

코딩테스트 알고리즘

그리디

가장 큰 순서대로, 가장 작은 순서대로 와 같은 기준 (정렬 알고리즘과 한쌍)
큰 단위가 작은 단위의 배수형태여야 한다. 아닌 경우 다이나믹 프로그래밍으로 접근

  • while문 대신 몫을 계산하는 식으로 접근

구현 ⭐️

완전탐색 or 시뮬레이션

move= [(0, 1), (1, 0), (0, -1), (-1, 0)]
ny, nx=y+move[direction][0], x+move[direction][1]
ifny<0orny>=nornx<0ornx>=n:
direction= (direction+1)%4y, x=y+move[direction][0], x+move[direction][1]
dict= {'left': (-1, 0), 'right': (1, 0), 'up': (0, 1), 'down': (0, -1)}
nx, ny=x+dict[key][0], y+dict[key][1]
ifnx<-nornx>n:

그래프

노드와 노드 사이에 연결된 간선 정보를 가지고 있는 자료구조

인접행렬

n, m=map(int, input().split())
graph= []
forrowinrange(n):
line= [int(x) forxinlist(input())]
graph.append(line)

인접 리스트

n, m=map(int, input().split())
graph= [[] for_inrange(n)]
graph[0].append((1, 7)) #노드 1, cost 7graph[1].append((0, 7)) #노드 0, cost 7

DFS/BFS

스택, 큐(deque: from collections) 자료구조 필요
재귀: 스택구조, 종료 조건이 필요

DFS: 스택(재귀)

  • 방문하지 않은 경우 재귀를 통해 새로운 노드 방문
defdfs(graph, v, visited):
visited[v] =Trueforiingraph[v]:
ifnotvisited[i]:
dfs(graph, i, visited)

BFS: 큐(deque)

  • 방문하지 않은 경우 queue 에 추가, queue 순서에 맞게 노드 방문
defbfs(graph, start, visited):
queue=deque([start])
visited[start] =Truewhilequeue:
v=queue.popleft()
foriingraph[v]:
ifnotvisited[i]:
queue.append(i)
visited[i] =True

선택 정렬

O(n^2)

defsort(array):
foriinrange(len(array)):
min_index=i;
forjinrange(i+1, len(array)):
ifarray[min_index] >array[j]:
min_index=j;
array[i], array[min_index] =array[min_index], array[i]
returnarray

삽입 정렬

O(n^2) but 데이터가 거의 정렬되어 있을 때 효율적
정렬된 상태 + 적절한 위치 찾아 삽입하여 정렬

defsort(array):
foriinrange(1, len(array)):
forjinrange(i, 0, -1): # i to 1 까지 -1씩 감소ifarray[j] <array[j-1]:
array[j], array[j-1] =array[j-1], array[j]
else:
breakreturnarray

퀵 정렬 ⭐️

O(n log n) but 데이터가 거의 정렬되어 있을 때 비효율적
pivot 을 기준으로 작은, 데이터 좌우 분리, 분리된 좌, 우 역시 동일 방식으로 정렬

좌 → 우: pivot 보다 큰 데이터의 위치(left), 좌 ← 우: pivot 보다 작은 데이터의 위치(right)를 찾아 위치를 서로 변경
작은 데이터 위치(left)가 큰 데이터 위치(right)보다 크거나 같은 경우: pivot, 작은 데이터 switch, 종료

재귀 종료조건: 리스트가 1개인 경우 정렬된 상태이므로 반환
pivot 선택기준: 첫번째 데이터 (호어 분할 방식)

defsort(array):
quick_sort(array, 0, len(array)-1)
defquick_sort(array, start, end):
ifstart>=end: # 원소가 1개인 경우 종료returnpivot=startleft=start+1right=endwhileleft<=right:
whileleft<=endandarray[left] <=array[pivot]:
left+=1# find 큰값 indexwhileright>startandarray[right] >=aray[pivot]:
right-=1# find 작은값 indexifleft>right: # 엇갈린 경우: pivot, 작은값 switcharray[right], array[pivot] =array[pivot], array[right]
pivot=rightelse: # 큰값, 작은값 switch, 이후 while 통해 다음 switch 진행array[left], array[right] =array[right], array[left]
# pivot 보다 작은 값들 | pivot | pivot 보다 큰 값들quick_sort(array, start, pivot-1)
quick_sort(array, pivot+1, end)
defsort(array):
iflen(array) <=1:
returnarraypivot=array[0]
tail=array[1:]
left_side= [xforxintailifx<=pivot]
right_side= [xforxintailifx>pivot]
returnsort(left_side) + [pivot] +sort(right_side)

병합 정렬

O(n log n) 분할 정복(divide and conqure) 방법을 통해 크가 같은 두 리스트로 분할 후 각각 정렬한 결과를 합병
분할하는 횟수는 절반식 줄어들기에 O(logN) 시간이 소모된다.
병합할 때 모든 값들을 비교해야 하기 때문에 O(N) 시간이 소모된다.
퀵정렬보다는 느리지만 최악의 경우도 O(n log n)보장

defmerge_sort(arr):
iflen(arr) <2:
returnarrmid=len(arr) //2low_arr=merge_sort(arr[:mid])
high_arr=merge_sort(arr[mid:])
merged_arr= []
l=h=0whilel<len(low_arr) andh<len(high_arr):
# 좌측이 더 작은 경우 -> 좌측 merged에 추가iflow_arr[l] <high_arr[h]:
merged_arr.append(low_arr[l])
l+=1# 우측이 더 작은 경우 -> 우측 merged에 추가else:
merged_arr.append(high_arr[h])
h+=1merged_arr+=low_arr[l:]
merged_arr+=high_arr[h:]
returnmerged_arr

계수 정렬

O(n + k) (k: 가장큰 수)
범위가 정해진 정수, 중복이 많은 경우 효율적
위치변경 X, 리스트 정보를 통한 정렬

defsort(array):
counts= [0]*(max(array)+1)
foriinrange(len(array)):
counts[array[i]] +=1sorted= []
foriinrange(len(counts)):
sorted+= [i] *counts[i]
returnsorted

순차 탐색

O(n) 앞에서부터 차례로 확인

defsearch(array, target):
foriinrange(len(array)):
ifarray[i] ==target:
returnireturn-1

이진 탐색 ⭐️

O(log n) 정렬된 배열 내에서 중간점 위치 데이터와 비교하며 반복적으로 절반씩 좁혀가며 찾는다
재귀 형식과 반복문 형식이 있는데, stack에 쌓이지 않는 반복문 방식이 더 좋다.

defbinary_search(array, target, start, end):
whilestart<=end:
mid= (start+end) //2ifarray[mid] ==target:
returnmideliftarget<array[mid]:
end=mid-1else:
start=mid+1return-1

파라메트릭 서치 (Parametric Search) ⭐️

주어진 범위 내에서 원하는 값 또는 원하는 조건에 가장 일치하는 값을 찾아내는 알고리즘
최적화 문제(최댓값 x = ?) -> 결정문제(x일 경우 최댓값?)로 바꾸어 풀 수 있다.

  • 범위의 중간값과 조건을 비교하며 범위를 수정해나간다
  • 이진 탐색과 차이점은 값 비교 -> 조건 비교, 반환값 (mid, -1 -> result (현재까지의 최적값))이 다르다는 점.
# 최적화문제: 24시간을 7시간마다 배고파지는 사람이 최소 몇끼니를 먹어야 배부르게 지낼수있는가?# 결정문제: 7시간마다 배고파지는 사람이 x끼니를 먹으면 24시간을 배부르게 지낼 수 있다.defparametricSearch(start, end) {
result=0while (start<=end):
mid= (start+end) /2if (24/mid) <7:
end=mid-0.1else:
start=mid+0.1result=midreturnresult
}

파라메트릭 서치 블로그

이진 탐색 트리

이진 탐색이 동작할 수 있도록 고안된 트리 자료구조
child-left < parent < child-right 식

다이나믹 프로그래밍

큰 문제를 작은 문제로 나눌 수 있는 경우 동일문제를 중복되지 않게 계산하는 방법
Bottom up(반복문), Top down(재귀) 방식
메모이제이션: 계산된 결과들을 메모하는 기법 (캐싱)
O(n)

d= [0] *100defpibo(x):
d[1] =1d[2] =1foriinrange(3, x+1):
d[i] =d[i-1] +d[i-2]

다익스트라

시작 노드로부터 각 노드까지의 최단거리 알고리즘 (음의 간선이 없는 경우)
반복적으로 최소 최단거리의 노드를 선택하며 테이블을 갱신 (heapq 사용)
O(E log V)

  • V: 노드수, E: 간선수
  • visited: [False] * (n+1)
  • distance: [INF] * (n+1)
defdijkstra(graph, startNode):
distance= [INF]*(len(graph)+1)
distance[startNode] =0q= []
# q: (distance, node), distance 가 적은 node 순으로 정렬된다heapq.heappush(q, (0, startNode))
whileq:
currentDistance, currentNode=heapq.heappop(q)
# currentNode 의 distance 가 갱신된적이 있는 경우 무시ifdistance[currentNode] <currentDistance:
continue# currentNode 의 인접노드들 확인fornodeingraph[currentNode]:
nodeNum=node[0]
nodeCost=node[1]
newDistance=currentDistance+nodeCostifnewDistance<distance[nodeNum]:
distance[nodeNum] =newDistanceheapq.heappush(q, (newDistance, nodeNum))
returndistance
importsysimportheapqinput=sys.stdin.readlineINF=int(1e9)
n, m=map(int, input().split())
startNode=int(input())
graph= [[] foriinrange(n+1)]
for_inrange(m):
from, to, cost=map(int, input().split())
graph[from].append((to, cost))
distance=dijkstra(graph, startNode)
foriinrange(1, n+1):
ifdistance[i] ==INF:
print("INF")
else:
print(distance[i])

플로이드 워셜

여러개의 from, to 노드간 최단거리 구하기 알고리즘
점화식: AtoB = min(AtoB, AtoK + KtoB)
k, a, b 순차적으로 3중 for 구조
O(n^3)

deffloydWarshall(graph):
forkinrange(1, n+1):
forainrange(1, n+1):
forbinrange(1, n+1):
graph[a][b] =min(graph[a][b], graph[a][k] +graph[k][b])
graph[b][a] =graph[a][b]
importsysinput=sys.stdin.readline()
INF=int(1e9)
n, m=map(int, input().split())
graph= [[]]*(n+1) for_inrange(n+1)]
forainrange(1, n+1):
forbinrange(1, n+1):
ifa==b:
graph[a][b] =0graph[b][a] =0for_inrange(m):
a, b, cost=map(int, input().split())
graph[a][b] =costgraph[b][a] =costfloydWarshall(graph)
forainrange(1, n+1):
forbinrange(1, n+1):
val=graph[a][b] ifgraph[a][b] !=INFelse"INF"print(val, end=" ")
print()

유니언 파인드 ⭐️

공통 원소가 없는 집합이 필요한 경우
하나의 집합으로 합치는 union 과 원소가 속한 집합을 알려주는 find 연산이 필요
union 의 경우 작은 노드값이 집합의 값이 된다.

# 노드값과 집합값이 같을때까지 재귀적으로 호출deffindParent(parents, x):
ifparents[x] !=x:
# 경로 압축parents[x] =findParent(parents, parants[x])
returnparents[x]
defunionParent(parents, a, b):
setA=findParent(parents, a)
setB=findParent(parents, b)
ifsetA<setB:
parents[b] =setAelse:
parents[a] =setB
v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
unionParent(parents, a, b)
foriinrange(1, v+1):
print(findParent(parents, i), end=" ")
print()

사이클 판별

유향 그래프: DFS 알고리즘
무향 그래프: 유니언 파인드 집합 알고리즘

# 무향그래프에서 사이클 판별v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
cycle=Falseforiinrange(e):
a, b=map(int, input().split())
iffindParent(parents, a) ==findParent(parents, b):
cycle=Truebreakelse:
unionParent(parents, a, b)
ifcycle:
print("사이클 발생")
else:
print("사이클 비발생")

신장 트리

모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프
모든 도시를 연결할 때 최소 비용과 관련된 크루스칼 알고리즘의 경우 최소 신장 트리를 활용한 알고리즘

최소 신장 트리를 구하기 위한 알고리즘

  • 신장 트리(Spanning tree): 모든 정점을 포함, 정점간 서로 연결, 싸이클이 존재하지 않는 그래프
  • 최소 신장 트리(Minimum Spanning Tree): 신장 트리 중 가중치 합이 최소인 그래프
  • 싸이클 여부: 서로소 집합(Disjoint Set)Union & Find 활용 크루스칼 알고리즘 블로그

크루스칼 알고리즘

경로를 최소한의 비용으로 연결하는 알고리즘
그래프 내 모든 정점들을 가장 적은 비용으로 연결하기 위해 사용 (사이클이 없는, 가중치의 합 최소가 되는 상태)
모든 간선에 대해 비용값 기준 정렬, 사이클이 발생하지 않는 간선부터 집합에 포함
무향 그래프이므로 find, union 을 통해 사이클 여부 판별 후 간선비용 총합
O(E log E)

v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
edges= [(a, b, cost) for_inrange(e) a, b, cost=map(int, iniput().split())]
result=0edges.sort()
foredgeinedges:
cost, a, b=edgeiffindParent(parents, a) !=findParent(parents, b):
unionParent(parents, a, b)
result+=costprint(result)

위상정렬

우선순위(선후관계)에 따라 방향성에 거스르지 않도록 순서대로 나열
진입차수 테이블과 queue(deque)가 필요

  1. 진입차수가 0인 노드를 큐에 넣는다
  2. 큐가 빌때까지 꺼내며 해당 노드부터 출발하는 간선을 제거하며 진입차수가 0인 노드를 큐에 넣는다 O(V+E)
deftopologySort(graph, indegree):
result= []
q=deque()
# 처음 시작시 indegree 값이 0인 노드를 넣고 시작foriinrange(1, v+1):
ifindegree[i] ==0:
q.append(i)
whileq:
node=q.popleft()
result.append(node)
foriingraph[node]:
indegree[i] -=1ifindegree[i] ==0:
q.append(i)
returnresult
fromcollectionsimportdequev, e=map(int, input().split())
indegree= [0] * (v+1)
graph= [[] foriinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
graph[a].append(b)
indegree[b] +=1sorted=topologySort(graph, indegree)
forninresult:
print(n, end=" ")

소수 알고리즘

2부터 n-1까지 나누어 떨어지는 수가 없는 경우 소수 판별

defisPrime(n):
ifn<2:
returnFalseforiinrange(2, n):
ifn%i==0:
returnFalsereturnTrue

에라토스테네스의 체

정해진 범위 내 소수를 구하는 경우 사용되는 알고리즘
범위 내 숫자들에서 합성수를 반복적으로 지우는 방식으로 소수를 찾는다.

  • 지워지지 않은수 중 작은수를 소수로 채택, 해당 수의 배수를 모두 제거
defprime_list(n):
prime= [True]*(n+1)
foriinrange(2, int(n**0.5)+1):
ifprime[i] ==True:
forjinrange(2*i, n+1, i):
prime[j] =Falsereturn [iforiinrange(2, n+1) ifprime[i] ==True]

에라토스테네스의 체 블로그1에라토스테네스의 체 블로그2

약수구하기

1부터 루트n까지 n을 나누는 수들을 확인

defdivisors(n):
result= []
foriinrange(1, int(n**0.5)+1):
ifn%i==0:
result.append(i)
result.append(n/i)
returnset(result)

About

파이썬 코딩테스트

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

165 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

CodingTest with Python

Python 문법 및 알고리즘 정리 블로그링크


코딩테스트 시간복잡도

제한시간 1초(n초) = 10^8번 연산 (100,000,000번)
따라서 입력값 n의 크기에 따라 시간복잡도를 고려하여 10^8번 연산 이내로 풀릴 수 있어야 한다.

n <= 20

O(n!), O(2^n)

  • 어떤 알고리즘이든 가능

n <= 100

O(n^3)

  • 3중 for문
  • 플로이드 워셜 알고리즘
  • 완전탐색

n <= 1000

O(n^2)

  • 2중 for문
  • 벨만 포드 알고리즘
  • 선택 정렬
  • 삽입 정렬

n <= 10000 ⭐️

O(n)

  • 동적 프로그래밍(DP)
  • 계수 정렬
  • 유니언 파인드
  • 위상 정렬

O(n log n)

  • 이진 탐색
  • 파라메트릭 서치
  • 다익스트라 알고리즘
  • 세그먼트 트리
  • 투 포인터
  • 퀵 정렬
  • 크루스칼 알고리즘

n <= 10^8

O(log n)

  • 수학적 기믹이 필요
  • 유클리드 호제법

파이썬 라이브러리

itertools (순열, 조합) ⭐️⭐️

# 순열fromitertoolsimportpermutationforcaseinpermutations(array, len(array)):
...
# 조합fromitertoolsimportcombinationsforcaseincombinations(array, len(array)):
...

functools (reduce)

# reducefromfunctoolsimportreduceresult=reduce(lambdax, y: x*y, array)

collections (deque(queue)) ⭐️

fromcollectionsimportdequequeue=deque() # queue 생성queue.append('a') # pushqueue[0] # peak 'a'queue.popleft() # pop 'a'queue.extend(['b', 'c', 'd']) # 배열 push

heapq (priorityQueue) ⭐️

importheapqpq= [] # priorityQueue 생성heapq.heappush(pq, (0, 1)) # pushpq[0] # peak (0, 1)heapq.heappop(pq) # pop (0, 1)array= [b, c, a]
heapq.heapify(array) # array를 priorityQueue로 생성heapq.heappop(array) # pop 'a'

math (gcd)

importmathvalue=math.gcd(3, 6)

copy

importcopyarray2=copy.deepcopy(array)

sys (readline)

importsysinput=sys.stdin.readline()

Python 기본 자료구조

Stack

stack= [] # stack 생성stack.append('a') # pushstack[0] # peakstack.pop() # pop 'a'

Set

s=set() # set 생성s1=set(['a', 'b', 'c']) # array를 set으로 생성s2=set(['b', 'c', 'd'])
s1.add('c') # pushs1.remove('c') # removes1.update(['a', 'b', 'c']) # 배열 pushs1&s2# 교집합 {'b', 'c'}s1|s2# 합집합 {'a', 'b', 'c', 'd'}s1-s2# 교집합을 제거한 s1 {'a'}
{ xforxin'abcde' } # list comprehension {'a', 'b', 'c', 'd', 'e'}'a'ins1# Truelist(s1) # set to list ['a', 'b', 'c']

Dictionary

d= {} # dictionary 생성d= {'a': 1, 'b': 2}
d=dict([('a', 1), ('b', 2)]) # touble array로 생성d_from_list= { x: ord(x) forxin'abc' } # dictionary comprehension {'a': 97, 'b': 98, 'c': 99}d['a'] # value 접근 1d['a'] =3# value updatedeld['a'] # value delete'a'ind# Truelist(d) # ['a', 'b']d.items(): # [(key, value)] 반환 [('b': 2)]d.keys() # ['b']d.values() # [2]forkey, valueinitems(): # key, value 순차접근print(key, value)

List

array= [] # list 생성array.append('a') # pusharray.remove('a') # deletearray=list(range(0, 5)) # [0, 1, 2, 3, 4]array.index(3) # index 3array.count(3) # 1array[-1] # 4array[:-1] # return [0, 1, 2, 3]array[1:-1] # return [1, 2, 3]array[::2] # return [0, 2, 4]array[-1::-2] # return [4, 2, 0]array[1:-1][::-1] # return [3, 2, 1]temp=array+ [5, 6] # return [1, 2, 3, 4, 5, 6]array+= [5, 6] # update [1, 2, 3, 4, 5, 6]# 2차원배열n, m=2, 3graph= [[0]*mfor_inrange(n)]
# [[0, 0, 0],# [0, 0, 0]]graph[n-1][m-1] =1# [[0, 0, 0],# [0, 0, 1]]

Tuple

tuple= ('a', 1) # tuple 생성tuple[0] # 'a'tuple[1] # 1

String

str(1) # '1'str(1)+str(2) # '12'string='abc'string.swapcase() # return 'ABC'string.isupper() # return Falsestring.islower() # return Truestring.upper() # return 'ABC'string.lower() # return 'abc'string='123'string.isdecimal() # return Truestring=' abcd 'string.strip() # return 'abcd'

range

list(range(0, 5)) # [0, 1, 2, 3, 4]foriinrange(0, 5):
print(i, end=", ") # 0, 1, 2, 3, 4,

파이썬 기능들

sort

# list.sort()array= [1, 4, 5, 2, 3]
array.sort() # update [1, 2, 3, 4, 5]array.sort(key=lambdax: -x) # update [5, 4, 3, 2, 1]array.sort(key=lambdax: abs(x-3)) # update [3, 4, 2, 5, 1]array.sort(reverse=True) # update [5, 4, 3, 2, 1]

sorted

# sorted(array)array= [1, 4, 5, 2, 3]
sorted(array) # return [1, 2, 3, 4, 5]sorted(array, key=lambdax: -x) # return [5, 4, 3, 2, 1]sorted(array, reverse=True) # return [5, 4, 3, 2, 1]sorted({1: 'a', 2: 'b'}) # dictionary: return [1, 2]sorted([('a', 3, 10), ('b', 1, 9), ('c', 2, 9)], key=lambdax: (x[2], x[1])) # tuple: return [('b', 1, 9), ('c', 2, 9), ('a', 3, 10)]

filter

array= [1, 2, 3, 4, 5]
list(filter(lambdax: x>2, array)) # [3, 4, 5]

enumerate

array [1, 2, 3]
foridx, valueinenumerate(array):
print(idx, value, end=",") # 0 1,1 2,2 3,

sum

# 총합total=sum([1, 2, 3]) # 6# 1차원 list화array=sum([[1, 2], [3, 4]], []) # [1, 2, 3, 4]

join

# string.join()''.join(['a', 'b', 'c']) # return 'abc'

replace

# string.replace()'abcde'.replace('a', 'A') # return 'Abcde'

ord

counts= [0]*26forcharin'abcde':
counts[ord(char)-ord('a')] +=1

abs

abs(-4) # 4

map

a, b=map(int, input().split())

Infinity

INF=int(1e9)

3항연산자

num=5odd=Trueifnum%2==1elseFalse# True

코딩테스트 알고리즘

그리디

가장 큰 순서대로, 가장 작은 순서대로 와 같은 기준 (정렬 알고리즘과 한쌍)
큰 단위가 작은 단위의 배수형태여야 한다. 아닌 경우 다이나믹 프로그래밍으로 접근

  • while문 대신 몫을 계산하는 식으로 접근

구현 ⭐️

완전탐색 or 시뮬레이션

move= [(0, 1), (1, 0), (0, -1), (-1, 0)]
ny, nx=y+move[direction][0], x+move[direction][1]
ifny<0orny>=nornx<0ornx>=n:
direction= (direction+1)%4y, x=y+move[direction][0], x+move[direction][1]
dict= {'left': (-1, 0), 'right': (1, 0), 'up': (0, 1), 'down': (0, -1)}
nx, ny=x+dict[key][0], y+dict[key][1]
ifnx<-nornx>n:

그래프

노드와 노드 사이에 연결된 간선 정보를 가지고 있는 자료구조

인접행렬

n, m=map(int, input().split())
graph= []
forrowinrange(n):
line= [int(x) forxinlist(input())]
graph.append(line)

인접 리스트

n, m=map(int, input().split())
graph= [[] for_inrange(n)]
graph[0].append((1, 7)) #노드 1, cost 7graph[1].append((0, 7)) #노드 0, cost 7

DFS/BFS

스택, 큐(deque: from collections) 자료구조 필요
재귀: 스택구조, 종료 조건이 필요

DFS: 스택(재귀)

  • 방문하지 않은 경우 재귀를 통해 새로운 노드 방문
defdfs(graph, v, visited):
visited[v] =Trueforiingraph[v]:
ifnotvisited[i]:
dfs(graph, i, visited)

BFS: 큐(deque)

  • 방문하지 않은 경우 queue 에 추가, queue 순서에 맞게 노드 방문
defbfs(graph, start, visited):
queue=deque([start])
visited[start] =Truewhilequeue:
v=queue.popleft()
foriingraph[v]:
ifnotvisited[i]:
queue.append(i)
visited[i] =True

선택 정렬

O(n^2)

defsort(array):
foriinrange(len(array)):
min_index=i;
forjinrange(i+1, len(array)):
ifarray[min_index] >array[j]:
min_index=j;
array[i], array[min_index] =array[min_index], array[i]
returnarray

삽입 정렬

O(n^2) but 데이터가 거의 정렬되어 있을 때 효율적
정렬된 상태 + 적절한 위치 찾아 삽입하여 정렬

defsort(array):
foriinrange(1, len(array)):
forjinrange(i, 0, -1): # i to 1 까지 -1씩 감소ifarray[j] <array[j-1]:
array[j], array[j-1] =array[j-1], array[j]
else:
breakreturnarray

퀵 정렬 ⭐️

O(n log n) but 데이터가 거의 정렬되어 있을 때 비효율적
pivot 을 기준으로 작은, 데이터 좌우 분리, 분리된 좌, 우 역시 동일 방식으로 정렬

좌 → 우: pivot 보다 큰 데이터의 위치(left), 좌 ← 우: pivot 보다 작은 데이터의 위치(right)를 찾아 위치를 서로 변경
작은 데이터 위치(left)가 큰 데이터 위치(right)보다 크거나 같은 경우: pivot, 작은 데이터 switch, 종료

재귀 종료조건: 리스트가 1개인 경우 정렬된 상태이므로 반환
pivot 선택기준: 첫번째 데이터 (호어 분할 방식)

defsort(array):
quick_sort(array, 0, len(array)-1)
defquick_sort(array, start, end):
ifstart>=end: # 원소가 1개인 경우 종료returnpivot=startleft=start+1right=endwhileleft<=right:
whileleft<=endandarray[left] <=array[pivot]:
left+=1# find 큰값 indexwhileright>startandarray[right] >=aray[pivot]:
right-=1# find 작은값 indexifleft>right: # 엇갈린 경우: pivot, 작은값 switcharray[right], array[pivot] =array[pivot], array[right]
pivot=rightelse: # 큰값, 작은값 switch, 이후 while 통해 다음 switch 진행array[left], array[right] =array[right], array[left]
# pivot 보다 작은 값들 | pivot | pivot 보다 큰 값들quick_sort(array, start, pivot-1)
quick_sort(array, pivot+1, end)
defsort(array):
iflen(array) <=1:
returnarraypivot=array[0]
tail=array[1:]
left_side= [xforxintailifx<=pivot]
right_side= [xforxintailifx>pivot]
returnsort(left_side) + [pivot] +sort(right_side)

병합 정렬

O(n log n) 분할 정복(divide and conqure) 방법을 통해 크가 같은 두 리스트로 분할 후 각각 정렬한 결과를 합병
분할하는 횟수는 절반식 줄어들기에 O(logN) 시간이 소모된다.
병합할 때 모든 값들을 비교해야 하기 때문에 O(N) 시간이 소모된다.
퀵정렬보다는 느리지만 최악의 경우도 O(n log n)보장

defmerge_sort(arr):
iflen(arr) <2:
returnarrmid=len(arr) //2low_arr=merge_sort(arr[:mid])
high_arr=merge_sort(arr[mid:])
merged_arr= []
l=h=0whilel<len(low_arr) andh<len(high_arr):
# 좌측이 더 작은 경우 -> 좌측 merged에 추가iflow_arr[l] <high_arr[h]:
merged_arr.append(low_arr[l])
l+=1# 우측이 더 작은 경우 -> 우측 merged에 추가else:
merged_arr.append(high_arr[h])
h+=1merged_arr+=low_arr[l:]
merged_arr+=high_arr[h:]
returnmerged_arr

계수 정렬

O(n + k) (k: 가장큰 수)
범위가 정해진 정수, 중복이 많은 경우 효율적
위치변경 X, 리스트 정보를 통한 정렬

defsort(array):
counts= [0]*(max(array)+1)
foriinrange(len(array)):
counts[array[i]] +=1sorted= []
foriinrange(len(counts)):
sorted+= [i] *counts[i]
returnsorted

순차 탐색

O(n) 앞에서부터 차례로 확인

defsearch(array, target):
foriinrange(len(array)):
ifarray[i] ==target:
returnireturn-1

이진 탐색 ⭐️

O(log n) 정렬된 배열 내에서 중간점 위치 데이터와 비교하며 반복적으로 절반씩 좁혀가며 찾는다
재귀 형식과 반복문 형식이 있는데, stack에 쌓이지 않는 반복문 방식이 더 좋다.

defbinary_search(array, target, start, end):
whilestart<=end:
mid= (start+end) //2ifarray[mid] ==target:
returnmideliftarget<array[mid]:
end=mid-1else:
start=mid+1return-1

파라메트릭 서치 (Parametric Search) ⭐️

주어진 범위 내에서 원하는 값 또는 원하는 조건에 가장 일치하는 값을 찾아내는 알고리즘
최적화 문제(최댓값 x = ?) -> 결정문제(x일 경우 최댓값?)로 바꾸어 풀 수 있다.

  • 범위의 중간값과 조건을 비교하며 범위를 수정해나간다
  • 이진 탐색과 차이점은 값 비교 -> 조건 비교, 반환값 (mid, -1 -> result (현재까지의 최적값))이 다르다는 점.
# 최적화문제: 24시간을 7시간마다 배고파지는 사람이 최소 몇끼니를 먹어야 배부르게 지낼수있는가?# 결정문제: 7시간마다 배고파지는 사람이 x끼니를 먹으면 24시간을 배부르게 지낼 수 있다.defparametricSearch(start, end) {
result=0while (start<=end):
mid= (start+end) /2if (24/mid) <7:
end=mid-0.1else:
start=mid+0.1result=midreturnresult
}

파라메트릭 서치 블로그

이진 탐색 트리

이진 탐색이 동작할 수 있도록 고안된 트리 자료구조
child-left < parent < child-right 식

다이나믹 프로그래밍

큰 문제를 작은 문제로 나눌 수 있는 경우 동일문제를 중복되지 않게 계산하는 방법
Bottom up(반복문), Top down(재귀) 방식
메모이제이션: 계산된 결과들을 메모하는 기법 (캐싱)
O(n)

d= [0] *100defpibo(x):
d[1] =1d[2] =1foriinrange(3, x+1):
d[i] =d[i-1] +d[i-2]

다익스트라

시작 노드로부터 각 노드까지의 최단거리 알고리즘 (음의 간선이 없는 경우)
반복적으로 최소 최단거리의 노드를 선택하며 테이블을 갱신 (heapq 사용)
O(E log V)

  • V: 노드수, E: 간선수
  • visited: [False] * (n+1)
  • distance: [INF] * (n+1)
defdijkstra(graph, startNode):
distance= [INF]*(len(graph)+1)
distance[startNode] =0q= []
# q: (distance, node), distance 가 적은 node 순으로 정렬된다heapq.heappush(q, (0, startNode))
whileq:
currentDistance, currentNode=heapq.heappop(q)
# currentNode 의 distance 가 갱신된적이 있는 경우 무시ifdistance[currentNode] <currentDistance:
continue# currentNode 의 인접노드들 확인fornodeingraph[currentNode]:
nodeNum=node[0]
nodeCost=node[1]
newDistance=currentDistance+nodeCostifnewDistance<distance[nodeNum]:
distance[nodeNum] =newDistanceheapq.heappush(q, (newDistance, nodeNum))
returndistance
importsysimportheapqinput=sys.stdin.readlineINF=int(1e9)
n, m=map(int, input().split())
startNode=int(input())
graph= [[] foriinrange(n+1)]
for_inrange(m):
from, to, cost=map(int, input().split())
graph[from].append((to, cost))
distance=dijkstra(graph, startNode)
foriinrange(1, n+1):
ifdistance[i] ==INF:
print("INF")
else:
print(distance[i])

플로이드 워셜

여러개의 from, to 노드간 최단거리 구하기 알고리즘
점화식: AtoB = min(AtoB, AtoK + KtoB)
k, a, b 순차적으로 3중 for 구조
O(n^3)

deffloydWarshall(graph):
forkinrange(1, n+1):
forainrange(1, n+1):
forbinrange(1, n+1):
graph[a][b] =min(graph[a][b], graph[a][k] +graph[k][b])
graph[b][a] =graph[a][b]
importsysinput=sys.stdin.readline()
INF=int(1e9)
n, m=map(int, input().split())
graph= [[]]*(n+1) for_inrange(n+1)]
forainrange(1, n+1):
forbinrange(1, n+1):
ifa==b:
graph[a][b] =0graph[b][a] =0for_inrange(m):
a, b, cost=map(int, input().split())
graph[a][b] =costgraph[b][a] =costfloydWarshall(graph)
forainrange(1, n+1):
forbinrange(1, n+1):
val=graph[a][b] ifgraph[a][b] !=INFelse"INF"print(val, end=" ")
print()

유니언 파인드 ⭐️

공통 원소가 없는 집합이 필요한 경우
하나의 집합으로 합치는 union 과 원소가 속한 집합을 알려주는 find 연산이 필요
union 의 경우 작은 노드값이 집합의 값이 된다.

# 노드값과 집합값이 같을때까지 재귀적으로 호출deffindParent(parents, x):
ifparents[x] !=x:
# 경로 압축parents[x] =findParent(parents, parants[x])
returnparents[x]
defunionParent(parents, a, b):
setA=findParent(parents, a)
setB=findParent(parents, b)
ifsetA<setB:
parents[b] =setAelse:
parents[a] =setB
v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
unionParent(parents, a, b)
foriinrange(1, v+1):
print(findParent(parents, i), end=" ")
print()

사이클 판별

유향 그래프: DFS 알고리즘
무향 그래프: 유니언 파인드 집합 알고리즘

# 무향그래프에서 사이클 판별v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
cycle=Falseforiinrange(e):
a, b=map(int, input().split())
iffindParent(parents, a) ==findParent(parents, b):
cycle=Truebreakelse:
unionParent(parents, a, b)
ifcycle:
print("사이클 발생")
else:
print("사이클 비발생")

신장 트리

모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프
모든 도시를 연결할 때 최소 비용과 관련된 크루스칼 알고리즘의 경우 최소 신장 트리를 활용한 알고리즘

최소 신장 트리를 구하기 위한 알고리즘

  • 신장 트리(Spanning tree): 모든 정점을 포함, 정점간 서로 연결, 싸이클이 존재하지 않는 그래프
  • 최소 신장 트리(Minimum Spanning Tree): 신장 트리 중 가중치 합이 최소인 그래프
  • 싸이클 여부: 서로소 집합(Disjoint Set)Union & Find 활용 크루스칼 알고리즘 블로그

크루스칼 알고리즘

경로를 최소한의 비용으로 연결하는 알고리즘
그래프 내 모든 정점들을 가장 적은 비용으로 연결하기 위해 사용 (사이클이 없는, 가중치의 합 최소가 되는 상태)
모든 간선에 대해 비용값 기준 정렬, 사이클이 발생하지 않는 간선부터 집합에 포함
무향 그래프이므로 find, union 을 통해 사이클 여부 판별 후 간선비용 총합
O(E log E)

v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
edges= [(a, b, cost) for_inrange(e) a, b, cost=map(int, iniput().split())]
result=0edges.sort()
foredgeinedges:
cost, a, b=edgeiffindParent(parents, a) !=findParent(parents, b):
unionParent(parents, a, b)
result+=costprint(result)

위상정렬

우선순위(선후관계)에 따라 방향성에 거스르지 않도록 순서대로 나열
진입차수 테이블과 queue(deque)가 필요

  1. 진입차수가 0인 노드를 큐에 넣는다
  2. 큐가 빌때까지 꺼내며 해당 노드부터 출발하는 간선을 제거하며 진입차수가 0인 노드를 큐에 넣는다 O(V+E)
deftopologySort(graph, indegree):
result= []
q=deque()
# 처음 시작시 indegree 값이 0인 노드를 넣고 시작foriinrange(1, v+1):
ifindegree[i] ==0:
q.append(i)
whileq:
node=q.popleft()
result.append(node)
foriingraph[node]:
indegree[i] -=1ifindegree[i] ==0:
q.append(i)
returnresult
fromcollectionsimportdequev, e=map(int, input().split())
indegree= [0] * (v+1)
graph= [[] foriinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
graph[a].append(b)
indegree[b] +=1sorted=topologySort(graph, indegree)
forninresult:
print(n, end=" ")

소수 알고리즘

2부터 n-1까지 나누어 떨어지는 수가 없는 경우 소수 판별

defisPrime(n):
ifn<2:
returnFalseforiinrange(2, n):
ifn%i==0:
returnFalsereturnTrue

에라토스테네스의 체

정해진 범위 내 소수를 구하는 경우 사용되는 알고리즘
범위 내 숫자들에서 합성수를 반복적으로 지우는 방식으로 소수를 찾는다.

  • 지워지지 않은수 중 작은수를 소수로 채택, 해당 수의 배수를 모두 제거
defprime_list(n):
prime= [True]*(n+1)
foriinrange(2, int(n**0.5)+1):
ifprime[i] ==True:
forjinrange(2*i, n+1, i):
prime[j] =Falsereturn [iforiinrange(2, n+1) ifprime[i] ==True]

에라토스테네스의 체 블로그1에라토스테네스의 체 블로그2

약수구하기

1부터 루트n까지 n을 나누는 수들을 확인

defdivisors(n):
result= []
foriinrange(1, int(n**0.5)+1):
ifn%i==0:
result.append(i)
result.append(n/i)
returnset(result)

About

파이썬 코딩테스트

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

165 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

CodingTest with Python

Python 문법 및 알고리즘 정리 블로그링크


코딩테스트 시간복잡도

제한시간 1초(n초) = 10^8번 연산 (100,000,000번)
따라서 입력값 n의 크기에 따라 시간복잡도를 고려하여 10^8번 연산 이내로 풀릴 수 있어야 한다.

n <= 20

O(n!), O(2^n)

  • 어떤 알고리즘이든 가능

n <= 100

O(n^3)

  • 3중 for문
  • 플로이드 워셜 알고리즘
  • 완전탐색

n <= 1000

O(n^2)

  • 2중 for문
  • 벨만 포드 알고리즘
  • 선택 정렬
  • 삽입 정렬

n <= 10000 ⭐️

O(n)

  • 동적 프로그래밍(DP)
  • 계수 정렬
  • 유니언 파인드
  • 위상 정렬

O(n log n)

  • 이진 탐색
  • 파라메트릭 서치
  • 다익스트라 알고리즘
  • 세그먼트 트리
  • 투 포인터
  • 퀵 정렬
  • 크루스칼 알고리즘

n <= 10^8

O(log n)

  • 수학적 기믹이 필요
  • 유클리드 호제법

파이썬 라이브러리

itertools (순열, 조합) ⭐️⭐️

# 순열fromitertoolsimportpermutationforcaseinpermutations(array, len(array)):
...
# 조합fromitertoolsimportcombinationsforcaseincombinations(array, len(array)):
...

functools (reduce)

# reducefromfunctoolsimportreduceresult=reduce(lambdax, y: x*y, array)

collections (deque(queue)) ⭐️

fromcollectionsimportdequequeue=deque() # queue 생성queue.append('a') # pushqueue[0] # peak 'a'queue.popleft() # pop 'a'queue.extend(['b', 'c', 'd']) # 배열 push

heapq (priorityQueue) ⭐️

importheapqpq= [] # priorityQueue 생성heapq.heappush(pq, (0, 1)) # pushpq[0] # peak (0, 1)heapq.heappop(pq) # pop (0, 1)array= [b, c, a]
heapq.heapify(array) # array를 priorityQueue로 생성heapq.heappop(array) # pop 'a'

math (gcd)

importmathvalue=math.gcd(3, 6)

copy

importcopyarray2=copy.deepcopy(array)

sys (readline)

importsysinput=sys.stdin.readline()

Python 기본 자료구조

Stack

stack= [] # stack 생성stack.append('a') # pushstack[0] # peakstack.pop() # pop 'a'

Set

s=set() # set 생성s1=set(['a', 'b', 'c']) # array를 set으로 생성s2=set(['b', 'c', 'd'])
s1.add('c') # pushs1.remove('c') # removes1.update(['a', 'b', 'c']) # 배열 pushs1&s2# 교집합 {'b', 'c'}s1|s2# 합집합 {'a', 'b', 'c', 'd'}s1-s2# 교집합을 제거한 s1 {'a'}
{ xforxin'abcde' } # list comprehension {'a', 'b', 'c', 'd', 'e'}'a'ins1# Truelist(s1) # set to list ['a', 'b', 'c']

Dictionary

d= {} # dictionary 생성d= {'a': 1, 'b': 2}
d=dict([('a', 1), ('b', 2)]) # touble array로 생성d_from_list= { x: ord(x) forxin'abc' } # dictionary comprehension {'a': 97, 'b': 98, 'c': 99}d['a'] # value 접근 1d['a'] =3# value updatedeld['a'] # value delete'a'ind# Truelist(d) # ['a', 'b']d.items(): # [(key, value)] 반환 [('b': 2)]d.keys() # ['b']d.values() # [2]forkey, valueinitems(): # key, value 순차접근print(key, value)

List

array= [] # list 생성array.append('a') # pusharray.remove('a') # deletearray=list(range(0, 5)) # [0, 1, 2, 3, 4]array.index(3) # index 3array.count(3) # 1array[-1] # 4array[:-1] # return [0, 1, 2, 3]array[1:-1] # return [1, 2, 3]array[::2] # return [0, 2, 4]array[-1::-2] # return [4, 2, 0]array[1:-1][::-1] # return [3, 2, 1]temp=array+ [5, 6] # return [1, 2, 3, 4, 5, 6]array+= [5, 6] # update [1, 2, 3, 4, 5, 6]# 2차원배열n, m=2, 3graph= [[0]*mfor_inrange(n)]
# [[0, 0, 0],# [0, 0, 0]]graph[n-1][m-1] =1# [[0, 0, 0],# [0, 0, 1]]

Tuple

tuple= ('a', 1) # tuple 생성tuple[0] # 'a'tuple[1] # 1

String

str(1) # '1'str(1)+str(2) # '12'string='abc'string.swapcase() # return 'ABC'string.isupper() # return Falsestring.islower() # return Truestring.upper() # return 'ABC'string.lower() # return 'abc'string='123'string.isdecimal() # return Truestring=' abcd 'string.strip() # return 'abcd'

range

list(range(0, 5)) # [0, 1, 2, 3, 4]foriinrange(0, 5):
print(i, end=", ") # 0, 1, 2, 3, 4,

파이썬 기능들

sort

# list.sort()array= [1, 4, 5, 2, 3]
array.sort() # update [1, 2, 3, 4, 5]array.sort(key=lambdax: -x) # update [5, 4, 3, 2, 1]array.sort(key=lambdax: abs(x-3)) # update [3, 4, 2, 5, 1]array.sort(reverse=True) # update [5, 4, 3, 2, 1]

sorted

# sorted(array)array= [1, 4, 5, 2, 3]
sorted(array) # return [1, 2, 3, 4, 5]sorted(array, key=lambdax: -x) # return [5, 4, 3, 2, 1]sorted(array, reverse=True) # return [5, 4, 3, 2, 1]sorted({1: 'a', 2: 'b'}) # dictionary: return [1, 2]sorted([('a', 3, 10), ('b', 1, 9), ('c', 2, 9)], key=lambdax: (x[2], x[1])) # tuple: return [('b', 1, 9), ('c', 2, 9), ('a', 3, 10)]

filter

array= [1, 2, 3, 4, 5]
list(filter(lambdax: x>2, array)) # [3, 4, 5]

enumerate

array [1, 2, 3]
foridx, valueinenumerate(array):
print(idx, value, end=",") # 0 1,1 2,2 3,

sum

# 총합total=sum([1, 2, 3]) # 6# 1차원 list화array=sum([[1, 2], [3, 4]], []) # [1, 2, 3, 4]

join

# string.join()''.join(['a', 'b', 'c']) # return 'abc'

replace

# string.replace()'abcde'.replace('a', 'A') # return 'Abcde'

ord

counts= [0]*26forcharin'abcde':
counts[ord(char)-ord('a')] +=1

abs

abs(-4) # 4

map

a, b=map(int, input().split())

Infinity

INF=int(1e9)

3항연산자

num=5odd=Trueifnum%2==1elseFalse# True

코딩테스트 알고리즘

그리디

가장 큰 순서대로, 가장 작은 순서대로 와 같은 기준 (정렬 알고리즘과 한쌍)
큰 단위가 작은 단위의 배수형태여야 한다. 아닌 경우 다이나믹 프로그래밍으로 접근

  • while문 대신 몫을 계산하는 식으로 접근

구현 ⭐️

완전탐색 or 시뮬레이션

move= [(0, 1), (1, 0), (0, -1), (-1, 0)]
ny, nx=y+move[direction][0], x+move[direction][1]
ifny<0orny>=nornx<0ornx>=n:
direction= (direction+1)%4y, x=y+move[direction][0], x+move[direction][1]
dict= {'left': (-1, 0), 'right': (1, 0), 'up': (0, 1), 'down': (0, -1)}
nx, ny=x+dict[key][0], y+dict[key][1]
ifnx<-nornx>n:

그래프

노드와 노드 사이에 연결된 간선 정보를 가지고 있는 자료구조

인접행렬

n, m=map(int, input().split())
graph= []
forrowinrange(n):
line= [int(x) forxinlist(input())]
graph.append(line)

인접 리스트

n, m=map(int, input().split())
graph= [[] for_inrange(n)]
graph[0].append((1, 7)) #노드 1, cost 7graph[1].append((0, 7)) #노드 0, cost 7

DFS/BFS

스택, 큐(deque: from collections) 자료구조 필요
재귀: 스택구조, 종료 조건이 필요

DFS: 스택(재귀)

  • 방문하지 않은 경우 재귀를 통해 새로운 노드 방문
defdfs(graph, v, visited):
visited[v] =Trueforiingraph[v]:
ifnotvisited[i]:
dfs(graph, i, visited)

BFS: 큐(deque)

  • 방문하지 않은 경우 queue 에 추가, queue 순서에 맞게 노드 방문
defbfs(graph, start, visited):
queue=deque([start])
visited[start] =Truewhilequeue:
v=queue.popleft()
foriingraph[v]:
ifnotvisited[i]:
queue.append(i)
visited[i] =True

선택 정렬

O(n^2)

defsort(array):
foriinrange(len(array)):
min_index=i;
forjinrange(i+1, len(array)):
ifarray[min_index] >array[j]:
min_index=j;
array[i], array[min_index] =array[min_index], array[i]
returnarray

삽입 정렬

O(n^2) but 데이터가 거의 정렬되어 있을 때 효율적
정렬된 상태 + 적절한 위치 찾아 삽입하여 정렬

defsort(array):
foriinrange(1, len(array)):
forjinrange(i, 0, -1): # i to 1 까지 -1씩 감소ifarray[j] <array[j-1]:
array[j], array[j-1] =array[j-1], array[j]
else:
breakreturnarray

퀵 정렬 ⭐️

O(n log n) but 데이터가 거의 정렬되어 있을 때 비효율적
pivot 을 기준으로 작은, 데이터 좌우 분리, 분리된 좌, 우 역시 동일 방식으로 정렬

좌 → 우: pivot 보다 큰 데이터의 위치(left), 좌 ← 우: pivot 보다 작은 데이터의 위치(right)를 찾아 위치를 서로 변경
작은 데이터 위치(left)가 큰 데이터 위치(right)보다 크거나 같은 경우: pivot, 작은 데이터 switch, 종료

재귀 종료조건: 리스트가 1개인 경우 정렬된 상태이므로 반환
pivot 선택기준: 첫번째 데이터 (호어 분할 방식)

defsort(array):
quick_sort(array, 0, len(array)-1)
defquick_sort(array, start, end):
ifstart>=end: # 원소가 1개인 경우 종료returnpivot=startleft=start+1right=endwhileleft<=right:
whileleft<=endandarray[left] <=array[pivot]:
left+=1# find 큰값 indexwhileright>startandarray[right] >=aray[pivot]:
right-=1# find 작은값 indexifleft>right: # 엇갈린 경우: pivot, 작은값 switcharray[right], array[pivot] =array[pivot], array[right]
pivot=rightelse: # 큰값, 작은값 switch, 이후 while 통해 다음 switch 진행array[left], array[right] =array[right], array[left]
# pivot 보다 작은 값들 | pivot | pivot 보다 큰 값들quick_sort(array, start, pivot-1)
quick_sort(array, pivot+1, end)
defsort(array):
iflen(array) <=1:
returnarraypivot=array[0]
tail=array[1:]
left_side= [xforxintailifx<=pivot]
right_side= [xforxintailifx>pivot]
returnsort(left_side) + [pivot] +sort(right_side)

병합 정렬

O(n log n) 분할 정복(divide and conqure) 방법을 통해 크가 같은 두 리스트로 분할 후 각각 정렬한 결과를 합병
분할하는 횟수는 절반식 줄어들기에 O(logN) 시간이 소모된다.
병합할 때 모든 값들을 비교해야 하기 때문에 O(N) 시간이 소모된다.
퀵정렬보다는 느리지만 최악의 경우도 O(n log n)보장

defmerge_sort(arr):
iflen(arr) <2:
returnarrmid=len(arr) //2low_arr=merge_sort(arr[:mid])
high_arr=merge_sort(arr[mid:])
merged_arr= []
l=h=0whilel<len(low_arr) andh<len(high_arr):
# 좌측이 더 작은 경우 -> 좌측 merged에 추가iflow_arr[l] <high_arr[h]:
merged_arr.append(low_arr[l])
l+=1# 우측이 더 작은 경우 -> 우측 merged에 추가else:
merged_arr.append(high_arr[h])
h+=1merged_arr+=low_arr[l:]
merged_arr+=high_arr[h:]
returnmerged_arr

계수 정렬

O(n + k) (k: 가장큰 수)
범위가 정해진 정수, 중복이 많은 경우 효율적
위치변경 X, 리스트 정보를 통한 정렬

defsort(array):
counts= [0]*(max(array)+1)
foriinrange(len(array)):
counts[array[i]] +=1sorted= []
foriinrange(len(counts)):
sorted+= [i] *counts[i]
returnsorted

순차 탐색

O(n) 앞에서부터 차례로 확인

defsearch(array, target):
foriinrange(len(array)):
ifarray[i] ==target:
returnireturn-1

이진 탐색 ⭐️

O(log n) 정렬된 배열 내에서 중간점 위치 데이터와 비교하며 반복적으로 절반씩 좁혀가며 찾는다
재귀 형식과 반복문 형식이 있는데, stack에 쌓이지 않는 반복문 방식이 더 좋다.

defbinary_search(array, target, start, end):
whilestart<=end:
mid= (start+end) //2ifarray[mid] ==target:
returnmideliftarget<array[mid]:
end=mid-1else:
start=mid+1return-1

파라메트릭 서치 (Parametric Search) ⭐️

주어진 범위 내에서 원하는 값 또는 원하는 조건에 가장 일치하는 값을 찾아내는 알고리즘
최적화 문제(최댓값 x = ?) -> 결정문제(x일 경우 최댓값?)로 바꾸어 풀 수 있다.

  • 범위의 중간값과 조건을 비교하며 범위를 수정해나간다
  • 이진 탐색과 차이점은 값 비교 -> 조건 비교, 반환값 (mid, -1 -> result (현재까지의 최적값))이 다르다는 점.
# 최적화문제: 24시간을 7시간마다 배고파지는 사람이 최소 몇끼니를 먹어야 배부르게 지낼수있는가?# 결정문제: 7시간마다 배고파지는 사람이 x끼니를 먹으면 24시간을 배부르게 지낼 수 있다.defparametricSearch(start, end) {
result=0while (start<=end):
mid= (start+end) /2if (24/mid) <7:
end=mid-0.1else:
start=mid+0.1result=midreturnresult
}

파라메트릭 서치 블로그

이진 탐색 트리

이진 탐색이 동작할 수 있도록 고안된 트리 자료구조
child-left < parent < child-right 식

다이나믹 프로그래밍

큰 문제를 작은 문제로 나눌 수 있는 경우 동일문제를 중복되지 않게 계산하는 방법
Bottom up(반복문), Top down(재귀) 방식
메모이제이션: 계산된 결과들을 메모하는 기법 (캐싱)
O(n)

d= [0] *100defpibo(x):
d[1] =1d[2] =1foriinrange(3, x+1):
d[i] =d[i-1] +d[i-2]

다익스트라

시작 노드로부터 각 노드까지의 최단거리 알고리즘 (음의 간선이 없는 경우)
반복적으로 최소 최단거리의 노드를 선택하며 테이블을 갱신 (heapq 사용)
O(E log V)

  • V: 노드수, E: 간선수
  • visited: [False] * (n+1)
  • distance: [INF] * (n+1)
defdijkstra(graph, startNode):
distance= [INF]*(len(graph)+1)
distance[startNode] =0q= []
# q: (distance, node), distance 가 적은 node 순으로 정렬된다heapq.heappush(q, (0, startNode))
whileq:
currentDistance, currentNode=heapq.heappop(q)
# currentNode 의 distance 가 갱신된적이 있는 경우 무시ifdistance[currentNode] <currentDistance:
continue# currentNode 의 인접노드들 확인fornodeingraph[currentNode]:
nodeNum=node[0]
nodeCost=node[1]
newDistance=currentDistance+nodeCostifnewDistance<distance[nodeNum]:
distance[nodeNum] =newDistanceheapq.heappush(q, (newDistance, nodeNum))
returndistance
importsysimportheapqinput=sys.stdin.readlineINF=int(1e9)
n, m=map(int, input().split())
startNode=int(input())
graph= [[] foriinrange(n+1)]
for_inrange(m):
from, to, cost=map(int, input().split())
graph[from].append((to, cost))
distance=dijkstra(graph, startNode)
foriinrange(1, n+1):
ifdistance[i] ==INF:
print("INF")
else:
print(distance[i])

플로이드 워셜

여러개의 from, to 노드간 최단거리 구하기 알고리즘
점화식: AtoB = min(AtoB, AtoK + KtoB)
k, a, b 순차적으로 3중 for 구조
O(n^3)

deffloydWarshall(graph):
forkinrange(1, n+1):
forainrange(1, n+1):
forbinrange(1, n+1):
graph[a][b] =min(graph[a][b], graph[a][k] +graph[k][b])
graph[b][a] =graph[a][b]
importsysinput=sys.stdin.readline()
INF=int(1e9)
n, m=map(int, input().split())
graph= [[]]*(n+1) for_inrange(n+1)]
forainrange(1, n+1):
forbinrange(1, n+1):
ifa==b:
graph[a][b] =0graph[b][a] =0for_inrange(m):
a, b, cost=map(int, input().split())
graph[a][b] =costgraph[b][a] =costfloydWarshall(graph)
forainrange(1, n+1):
forbinrange(1, n+1):
val=graph[a][b] ifgraph[a][b] !=INFelse"INF"print(val, end=" ")
print()

유니언 파인드 ⭐️

공통 원소가 없는 집합이 필요한 경우
하나의 집합으로 합치는 union 과 원소가 속한 집합을 알려주는 find 연산이 필요
union 의 경우 작은 노드값이 집합의 값이 된다.

# 노드값과 집합값이 같을때까지 재귀적으로 호출deffindParent(parents, x):
ifparents[x] !=x:
# 경로 압축parents[x] =findParent(parents, parants[x])
returnparents[x]
defunionParent(parents, a, b):
setA=findParent(parents, a)
setB=findParent(parents, b)
ifsetA<setB:
parents[b] =setAelse:
parents[a] =setB
v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
unionParent(parents, a, b)
foriinrange(1, v+1):
print(findParent(parents, i), end=" ")
print()

사이클 판별

유향 그래프: DFS 알고리즘
무향 그래프: 유니언 파인드 집합 알고리즘

# 무향그래프에서 사이클 판별v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
cycle=Falseforiinrange(e):
a, b=map(int, input().split())
iffindParent(parents, a) ==findParent(parents, b):
cycle=Truebreakelse:
unionParent(parents, a, b)
ifcycle:
print("사이클 발생")
else:
print("사이클 비발생")

신장 트리

모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프
모든 도시를 연결할 때 최소 비용과 관련된 크루스칼 알고리즘의 경우 최소 신장 트리를 활용한 알고리즘

최소 신장 트리를 구하기 위한 알고리즘

  • 신장 트리(Spanning tree): 모든 정점을 포함, 정점간 서로 연결, 싸이클이 존재하지 않는 그래프
  • 최소 신장 트리(Minimum Spanning Tree): 신장 트리 중 가중치 합이 최소인 그래프
  • 싸이클 여부: 서로소 집합(Disjoint Set)Union & Find 활용 크루스칼 알고리즘 블로그

크루스칼 알고리즘

경로를 최소한의 비용으로 연결하는 알고리즘
그래프 내 모든 정점들을 가장 적은 비용으로 연결하기 위해 사용 (사이클이 없는, 가중치의 합 최소가 되는 상태)
모든 간선에 대해 비용값 기준 정렬, 사이클이 발생하지 않는 간선부터 집합에 포함
무향 그래프이므로 find, union 을 통해 사이클 여부 판별 후 간선비용 총합
O(E log E)

v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
edges= [(a, b, cost) for_inrange(e) a, b, cost=map(int, iniput().split())]
result=0edges.sort()
foredgeinedges:
cost, a, b=edgeiffindParent(parents, a) !=findParent(parents, b):
unionParent(parents, a, b)
result+=costprint(result)

위상정렬

우선순위(선후관계)에 따라 방향성에 거스르지 않도록 순서대로 나열
진입차수 테이블과 queue(deque)가 필요

  1. 진입차수가 0인 노드를 큐에 넣는다
  2. 큐가 빌때까지 꺼내며 해당 노드부터 출발하는 간선을 제거하며 진입차수가 0인 노드를 큐에 넣는다 O(V+E)
deftopologySort(graph, indegree):
result= []
q=deque()
# 처음 시작시 indegree 값이 0인 노드를 넣고 시작foriinrange(1, v+1):
ifindegree[i] ==0:
q.append(i)
whileq:
node=q.popleft()
result.append(node)
foriingraph[node]:
indegree[i] -=1ifindegree[i] ==0:
q.append(i)
returnresult
fromcollectionsimportdequev, e=map(int, input().split())
indegree= [0] * (v+1)
graph= [[] foriinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
graph[a].append(b)
indegree[b] +=1sorted=topologySort(graph, indegree)
forninresult:
print(n, end=" ")

소수 알고리즘

2부터 n-1까지 나누어 떨어지는 수가 없는 경우 소수 판별

defisPrime(n):
ifn<2:
returnFalseforiinrange(2, n):
ifn%i==0:
returnFalsereturnTrue

에라토스테네스의 체

정해진 범위 내 소수를 구하는 경우 사용되는 알고리즘
범위 내 숫자들에서 합성수를 반복적으로 지우는 방식으로 소수를 찾는다.

  • 지워지지 않은수 중 작은수를 소수로 채택, 해당 수의 배수를 모두 제거
defprime_list(n):
prime= [True]*(n+1)
foriinrange(2, int(n**0.5)+1):
ifprime[i] ==True:
forjinrange(2*i, n+1, i):
prime[j] =Falsereturn [iforiinrange(2, n+1) ifprime[i] ==True]

에라토스테네스의 체 블로그1에라토스테네스의 체 블로그2

약수구하기

1부터 루트n까지 n을 나누는 수들을 확인

defdivisors(n):
result= []
foriinrange(1, int(n**0.5)+1):
ifn%i==0:
result.append(i)
result.append(n/i)
returnset(result)

About

파이썬 코딩테스트

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

165 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

CodingTest with Python

Python 문법 및 알고리즘 정리 블로그링크


코딩테스트 시간복잡도

제한시간 1초(n초) = 10^8번 연산 (100,000,000번)
따라서 입력값 n의 크기에 따라 시간복잡도를 고려하여 10^8번 연산 이내로 풀릴 수 있어야 한다.

n <= 20

O(n!), O(2^n)

  • 어떤 알고리즘이든 가능

n <= 100

O(n^3)

  • 3중 for문
  • 플로이드 워셜 알고리즘
  • 완전탐색

n <= 1000

O(n^2)

  • 2중 for문
  • 벨만 포드 알고리즘
  • 선택 정렬
  • 삽입 정렬

n <= 10000 ⭐️

O(n)

  • 동적 프로그래밍(DP)
  • 계수 정렬
  • 유니언 파인드
  • 위상 정렬

O(n log n)

  • 이진 탐색
  • 파라메트릭 서치
  • 다익스트라 알고리즘
  • 세그먼트 트리
  • 투 포인터
  • 퀵 정렬
  • 크루스칼 알고리즘

n <= 10^8

O(log n)

  • 수학적 기믹이 필요
  • 유클리드 호제법

파이썬 라이브러리

itertools (순열, 조합) ⭐️⭐️

# 순열fromitertoolsimportpermutationforcaseinpermutations(array, len(array)):
...
# 조합fromitertoolsimportcombinationsforcaseincombinations(array, len(array)):
...

functools (reduce)

# reducefromfunctoolsimportreduceresult=reduce(lambdax, y: x*y, array)

collections (deque(queue)) ⭐️

fromcollectionsimportdequequeue=deque() # queue 생성queue.append('a') # pushqueue[0] # peak 'a'queue.popleft() # pop 'a'queue.extend(['b', 'c', 'd']) # 배열 push

heapq (priorityQueue) ⭐️

importheapqpq= [] # priorityQueue 생성heapq.heappush(pq, (0, 1)) # pushpq[0] # peak (0, 1)heapq.heappop(pq) # pop (0, 1)array= [b, c, a]
heapq.heapify(array) # array를 priorityQueue로 생성heapq.heappop(array) # pop 'a'

math (gcd)

importmathvalue=math.gcd(3, 6)

copy

importcopyarray2=copy.deepcopy(array)

sys (readline)

importsysinput=sys.stdin.readline()

Python 기본 자료구조

Stack

stack= [] # stack 생성stack.append('a') # pushstack[0] # peakstack.pop() # pop 'a'

Set

s=set() # set 생성s1=set(['a', 'b', 'c']) # array를 set으로 생성s2=set(['b', 'c', 'd'])
s1.add('c') # pushs1.remove('c') # removes1.update(['a', 'b', 'c']) # 배열 pushs1&s2# 교집합 {'b', 'c'}s1|s2# 합집합 {'a', 'b', 'c', 'd'}s1-s2# 교집합을 제거한 s1 {'a'}
{ xforxin'abcde' } # list comprehension {'a', 'b', 'c', 'd', 'e'}'a'ins1# Truelist(s1) # set to list ['a', 'b', 'c']

Dictionary

d= {} # dictionary 생성d= {'a': 1, 'b': 2}
d=dict([('a', 1), ('b', 2)]) # touble array로 생성d_from_list= { x: ord(x) forxin'abc' } # dictionary comprehension {'a': 97, 'b': 98, 'c': 99}d['a'] # value 접근 1d['a'] =3# value updatedeld['a'] # value delete'a'ind# Truelist(d) # ['a', 'b']d.items(): # [(key, value)] 반환 [('b': 2)]d.keys() # ['b']d.values() # [2]forkey, valueinitems(): # key, value 순차접근print(key, value)

List

array= [] # list 생성array.append('a') # pusharray.remove('a') # deletearray=list(range(0, 5)) # [0, 1, 2, 3, 4]array.index(3) # index 3array.count(3) # 1array[-1] # 4array[:-1] # return [0, 1, 2, 3]array[1:-1] # return [1, 2, 3]array[::2] # return [0, 2, 4]array[-1::-2] # return [4, 2, 0]array[1:-1][::-1] # return [3, 2, 1]temp=array+ [5, 6] # return [1, 2, 3, 4, 5, 6]array+= [5, 6] # update [1, 2, 3, 4, 5, 6]# 2차원배열n, m=2, 3graph= [[0]*mfor_inrange(n)]
# [[0, 0, 0],# [0, 0, 0]]graph[n-1][m-1] =1# [[0, 0, 0],# [0, 0, 1]]

Tuple

tuple= ('a', 1) # tuple 생성tuple[0] # 'a'tuple[1] # 1

String

str(1) # '1'str(1)+str(2) # '12'string='abc'string.swapcase() # return 'ABC'string.isupper() # return Falsestring.islower() # return Truestring.upper() # return 'ABC'string.lower() # return 'abc'string='123'string.isdecimal() # return Truestring=' abcd 'string.strip() # return 'abcd'

range

list(range(0, 5)) # [0, 1, 2, 3, 4]foriinrange(0, 5):
print(i, end=", ") # 0, 1, 2, 3, 4,

파이썬 기능들

sort

# list.sort()array= [1, 4, 5, 2, 3]
array.sort() # update [1, 2, 3, 4, 5]array.sort(key=lambdax: -x) # update [5, 4, 3, 2, 1]array.sort(key=lambdax: abs(x-3)) # update [3, 4, 2, 5, 1]array.sort(reverse=True) # update [5, 4, 3, 2, 1]

sorted

# sorted(array)array= [1, 4, 5, 2, 3]
sorted(array) # return [1, 2, 3, 4, 5]sorted(array, key=lambdax: -x) # return [5, 4, 3, 2, 1]sorted(array, reverse=True) # return [5, 4, 3, 2, 1]sorted({1: 'a', 2: 'b'}) # dictionary: return [1, 2]sorted([('a', 3, 10), ('b', 1, 9), ('c', 2, 9)], key=lambdax: (x[2], x[1])) # tuple: return [('b', 1, 9), ('c', 2, 9), ('a', 3, 10)]

filter

array= [1, 2, 3, 4, 5]
list(filter(lambdax: x>2, array)) # [3, 4, 5]

enumerate

array [1, 2, 3]
foridx, valueinenumerate(array):
print(idx, value, end=",") # 0 1,1 2,2 3,

sum

# 총합total=sum([1, 2, 3]) # 6# 1차원 list화array=sum([[1, 2], [3, 4]], []) # [1, 2, 3, 4]

join

# string.join()''.join(['a', 'b', 'c']) # return 'abc'

replace

# string.replace()'abcde'.replace('a', 'A') # return 'Abcde'

ord

counts= [0]*26forcharin'abcde':
counts[ord(char)-ord('a')] +=1

abs

abs(-4) # 4

map

a, b=map(int, input().split())

Infinity

INF=int(1e9)

3항연산자

num=5odd=Trueifnum%2==1elseFalse# True

코딩테스트 알고리즘

그리디

가장 큰 순서대로, 가장 작은 순서대로 와 같은 기준 (정렬 알고리즘과 한쌍)
큰 단위가 작은 단위의 배수형태여야 한다. 아닌 경우 다이나믹 프로그래밍으로 접근

  • while문 대신 몫을 계산하는 식으로 접근

구현 ⭐️

완전탐색 or 시뮬레이션

move= [(0, 1), (1, 0), (0, -1), (-1, 0)]
ny, nx=y+move[direction][0], x+move[direction][1]
ifny<0orny>=nornx<0ornx>=n:
direction= (direction+1)%4y, x=y+move[direction][0], x+move[direction][1]
dict= {'left': (-1, 0), 'right': (1, 0), 'up': (0, 1), 'down': (0, -1)}
nx, ny=x+dict[key][0], y+dict[key][1]
ifnx<-nornx>n:

그래프

노드와 노드 사이에 연결된 간선 정보를 가지고 있는 자료구조

인접행렬

n, m=map(int, input().split())
graph= []
forrowinrange(n):
line= [int(x) forxinlist(input())]
graph.append(line)

인접 리스트

n, m=map(int, input().split())
graph= [[] for_inrange(n)]
graph[0].append((1, 7)) #노드 1, cost 7graph[1].append((0, 7)) #노드 0, cost 7

DFS/BFS

스택, 큐(deque: from collections) 자료구조 필요
재귀: 스택구조, 종료 조건이 필요

DFS: 스택(재귀)

  • 방문하지 않은 경우 재귀를 통해 새로운 노드 방문
defdfs(graph, v, visited):
visited[v] =Trueforiingraph[v]:
ifnotvisited[i]:
dfs(graph, i, visited)

BFS: 큐(deque)

  • 방문하지 않은 경우 queue 에 추가, queue 순서에 맞게 노드 방문
defbfs(graph, start, visited):
queue=deque([start])
visited[start] =Truewhilequeue:
v=queue.popleft()
foriingraph[v]:
ifnotvisited[i]:
queue.append(i)
visited[i] =True

선택 정렬

O(n^2)

defsort(array):
foriinrange(len(array)):
min_index=i;
forjinrange(i+1, len(array)):
ifarray[min_index] >array[j]:
min_index=j;
array[i], array[min_index] =array[min_index], array[i]
returnarray

삽입 정렬

O(n^2) but 데이터가 거의 정렬되어 있을 때 효율적
정렬된 상태 + 적절한 위치 찾아 삽입하여 정렬

defsort(array):
foriinrange(1, len(array)):
forjinrange(i, 0, -1): # i to 1 까지 -1씩 감소ifarray[j] <array[j-1]:
array[j], array[j-1] =array[j-1], array[j]
else:
breakreturnarray

퀵 정렬 ⭐️

O(n log n) but 데이터가 거의 정렬되어 있을 때 비효율적
pivot 을 기준으로 작은, 데이터 좌우 분리, 분리된 좌, 우 역시 동일 방식으로 정렬

좌 → 우: pivot 보다 큰 데이터의 위치(left), 좌 ← 우: pivot 보다 작은 데이터의 위치(right)를 찾아 위치를 서로 변경
작은 데이터 위치(left)가 큰 데이터 위치(right)보다 크거나 같은 경우: pivot, 작은 데이터 switch, 종료

재귀 종료조건: 리스트가 1개인 경우 정렬된 상태이므로 반환
pivot 선택기준: 첫번째 데이터 (호어 분할 방식)

defsort(array):
quick_sort(array, 0, len(array)-1)
defquick_sort(array, start, end):
ifstart>=end: # 원소가 1개인 경우 종료returnpivot=startleft=start+1right=endwhileleft<=right:
whileleft<=endandarray[left] <=array[pivot]:
left+=1# find 큰값 indexwhileright>startandarray[right] >=aray[pivot]:
right-=1# find 작은값 indexifleft>right: # 엇갈린 경우: pivot, 작은값 switcharray[right], array[pivot] =array[pivot], array[right]
pivot=rightelse: # 큰값, 작은값 switch, 이후 while 통해 다음 switch 진행array[left], array[right] =array[right], array[left]
# pivot 보다 작은 값들 | pivot | pivot 보다 큰 값들quick_sort(array, start, pivot-1)
quick_sort(array, pivot+1, end)
defsort(array):
iflen(array) <=1:
returnarraypivot=array[0]
tail=array[1:]
left_side= [xforxintailifx<=pivot]
right_side= [xforxintailifx>pivot]
returnsort(left_side) + [pivot] +sort(right_side)

병합 정렬

O(n log n) 분할 정복(divide and conqure) 방법을 통해 크가 같은 두 리스트로 분할 후 각각 정렬한 결과를 합병
분할하는 횟수는 절반식 줄어들기에 O(logN) 시간이 소모된다.
병합할 때 모든 값들을 비교해야 하기 때문에 O(N) 시간이 소모된다.
퀵정렬보다는 느리지만 최악의 경우도 O(n log n)보장

defmerge_sort(arr):
iflen(arr) <2:
returnarrmid=len(arr) //2low_arr=merge_sort(arr[:mid])
high_arr=merge_sort(arr[mid:])
merged_arr= []
l=h=0whilel<len(low_arr) andh<len(high_arr):
# 좌측이 더 작은 경우 -> 좌측 merged에 추가iflow_arr[l] <high_arr[h]:
merged_arr.append(low_arr[l])
l+=1# 우측이 더 작은 경우 -> 우측 merged에 추가else:
merged_arr.append(high_arr[h])
h+=1merged_arr+=low_arr[l:]
merged_arr+=high_arr[h:]
returnmerged_arr

계수 정렬

O(n + k) (k: 가장큰 수)
범위가 정해진 정수, 중복이 많은 경우 효율적
위치변경 X, 리스트 정보를 통한 정렬

defsort(array):
counts= [0]*(max(array)+1)
foriinrange(len(array)):
counts[array[i]] +=1sorted= []
foriinrange(len(counts)):
sorted+= [i] *counts[i]
returnsorted

순차 탐색

O(n) 앞에서부터 차례로 확인

defsearch(array, target):
foriinrange(len(array)):
ifarray[i] ==target:
returnireturn-1

이진 탐색 ⭐️

O(log n) 정렬된 배열 내에서 중간점 위치 데이터와 비교하며 반복적으로 절반씩 좁혀가며 찾는다
재귀 형식과 반복문 형식이 있는데, stack에 쌓이지 않는 반복문 방식이 더 좋다.

defbinary_search(array, target, start, end):
whilestart<=end:
mid= (start+end) //2ifarray[mid] ==target:
returnmideliftarget<array[mid]:
end=mid-1else:
start=mid+1return-1

파라메트릭 서치 (Parametric Search) ⭐️

주어진 범위 내에서 원하는 값 또는 원하는 조건에 가장 일치하는 값을 찾아내는 알고리즘
최적화 문제(최댓값 x = ?) -> 결정문제(x일 경우 최댓값?)로 바꾸어 풀 수 있다.

  • 범위의 중간값과 조건을 비교하며 범위를 수정해나간다
  • 이진 탐색과 차이점은 값 비교 -> 조건 비교, 반환값 (mid, -1 -> result (현재까지의 최적값))이 다르다는 점.
# 최적화문제: 24시간을 7시간마다 배고파지는 사람이 최소 몇끼니를 먹어야 배부르게 지낼수있는가?# 결정문제: 7시간마다 배고파지는 사람이 x끼니를 먹으면 24시간을 배부르게 지낼 수 있다.defparametricSearch(start, end) {
result=0while (start<=end):
mid= (start+end) /2if (24/mid) <7:
end=mid-0.1else:
start=mid+0.1result=midreturnresult
}

파라메트릭 서치 블로그

이진 탐색 트리

이진 탐색이 동작할 수 있도록 고안된 트리 자료구조
child-left < parent < child-right 식

다이나믹 프로그래밍

큰 문제를 작은 문제로 나눌 수 있는 경우 동일문제를 중복되지 않게 계산하는 방법
Bottom up(반복문), Top down(재귀) 방식
메모이제이션: 계산된 결과들을 메모하는 기법 (캐싱)
O(n)

d= [0] *100defpibo(x):
d[1] =1d[2] =1foriinrange(3, x+1):
d[i] =d[i-1] +d[i-2]

다익스트라

시작 노드로부터 각 노드까지의 최단거리 알고리즘 (음의 간선이 없는 경우)
반복적으로 최소 최단거리의 노드를 선택하며 테이블을 갱신 (heapq 사용)
O(E log V)

  • V: 노드수, E: 간선수
  • visited: [False] * (n+1)
  • distance: [INF] * (n+1)
defdijkstra(graph, startNode):
distance= [INF]*(len(graph)+1)
distance[startNode] =0q= []
# q: (distance, node), distance 가 적은 node 순으로 정렬된다heapq.heappush(q, (0, startNode))
whileq:
currentDistance, currentNode=heapq.heappop(q)
# currentNode 의 distance 가 갱신된적이 있는 경우 무시ifdistance[currentNode] <currentDistance:
continue# currentNode 의 인접노드들 확인fornodeingraph[currentNode]:
nodeNum=node[0]
nodeCost=node[1]
newDistance=currentDistance+nodeCostifnewDistance<distance[nodeNum]:
distance[nodeNum] =newDistanceheapq.heappush(q, (newDistance, nodeNum))
returndistance
importsysimportheapqinput=sys.stdin.readlineINF=int(1e9)
n, m=map(int, input().split())
startNode=int(input())
graph= [[] foriinrange(n+1)]
for_inrange(m):
from, to, cost=map(int, input().split())
graph[from].append((to, cost))
distance=dijkstra(graph, startNode)
foriinrange(1, n+1):
ifdistance[i] ==INF:
print("INF")
else:
print(distance[i])

플로이드 워셜

여러개의 from, to 노드간 최단거리 구하기 알고리즘
점화식: AtoB = min(AtoB, AtoK + KtoB)
k, a, b 순차적으로 3중 for 구조
O(n^3)

deffloydWarshall(graph):
forkinrange(1, n+1):
forainrange(1, n+1):
forbinrange(1, n+1):
graph[a][b] =min(graph[a][b], graph[a][k] +graph[k][b])
graph[b][a] =graph[a][b]
importsysinput=sys.stdin.readline()
INF=int(1e9)
n, m=map(int, input().split())
graph= [[]]*(n+1) for_inrange(n+1)]
forainrange(1, n+1):
forbinrange(1, n+1):
ifa==b:
graph[a][b] =0graph[b][a] =0for_inrange(m):
a, b, cost=map(int, input().split())
graph[a][b] =costgraph[b][a] =costfloydWarshall(graph)
forainrange(1, n+1):
forbinrange(1, n+1):
val=graph[a][b] ifgraph[a][b] !=INFelse"INF"print(val, end=" ")
print()

유니언 파인드 ⭐️

공통 원소가 없는 집합이 필요한 경우
하나의 집합으로 합치는 union 과 원소가 속한 집합을 알려주는 find 연산이 필요
union 의 경우 작은 노드값이 집합의 값이 된다.

# 노드값과 집합값이 같을때까지 재귀적으로 호출deffindParent(parents, x):
ifparents[x] !=x:
# 경로 압축parents[x] =findParent(parents, parants[x])
returnparents[x]
defunionParent(parents, a, b):
setA=findParent(parents, a)
setB=findParent(parents, b)
ifsetA<setB:
parents[b] =setAelse:
parents[a] =setB
v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
unionParent(parents, a, b)
foriinrange(1, v+1):
print(findParent(parents, i), end=" ")
print()

사이클 판별

유향 그래프: DFS 알고리즘
무향 그래프: 유니언 파인드 집합 알고리즘

# 무향그래프에서 사이클 판별v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
cycle=Falseforiinrange(e):
a, b=map(int, input().split())
iffindParent(parents, a) ==findParent(parents, b):
cycle=Truebreakelse:
unionParent(parents, a, b)
ifcycle:
print("사이클 발생")
else:
print("사이클 비발생")

신장 트리

모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프
모든 도시를 연결할 때 최소 비용과 관련된 크루스칼 알고리즘의 경우 최소 신장 트리를 활용한 알고리즘

최소 신장 트리를 구하기 위한 알고리즘

  • 신장 트리(Spanning tree): 모든 정점을 포함, 정점간 서로 연결, 싸이클이 존재하지 않는 그래프
  • 최소 신장 트리(Minimum Spanning Tree): 신장 트리 중 가중치 합이 최소인 그래프
  • 싸이클 여부: 서로소 집합(Disjoint Set)Union & Find 활용 크루스칼 알고리즘 블로그

크루스칼 알고리즘

경로를 최소한의 비용으로 연결하는 알고리즘
그래프 내 모든 정점들을 가장 적은 비용으로 연결하기 위해 사용 (사이클이 없는, 가중치의 합 최소가 되는 상태)
모든 간선에 대해 비용값 기준 정렬, 사이클이 발생하지 않는 간선부터 집합에 포함
무향 그래프이므로 find, union 을 통해 사이클 여부 판별 후 간선비용 총합
O(E log E)

v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
edges= [(a, b, cost) for_inrange(e) a, b, cost=map(int, iniput().split())]
result=0edges.sort()
foredgeinedges:
cost, a, b=edgeiffindParent(parents, a) !=findParent(parents, b):
unionParent(parents, a, b)
result+=costprint(result)

위상정렬

우선순위(선후관계)에 따라 방향성에 거스르지 않도록 순서대로 나열
진입차수 테이블과 queue(deque)가 필요

  1. 진입차수가 0인 노드를 큐에 넣는다
  2. 큐가 빌때까지 꺼내며 해당 노드부터 출발하는 간선을 제거하며 진입차수가 0인 노드를 큐에 넣는다 O(V+E)
deftopologySort(graph, indegree):
result= []
q=deque()
# 처음 시작시 indegree 값이 0인 노드를 넣고 시작foriinrange(1, v+1):
ifindegree[i] ==0:
q.append(i)
whileq:
node=q.popleft()
result.append(node)
foriingraph[node]:
indegree[i] -=1ifindegree[i] ==0:
q.append(i)
returnresult
fromcollectionsimportdequev, e=map(int, input().split())
indegree= [0] * (v+1)
graph= [[] foriinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
graph[a].append(b)
indegree[b] +=1sorted=topologySort(graph, indegree)
forninresult:
print(n, end=" ")

소수 알고리즘

2부터 n-1까지 나누어 떨어지는 수가 없는 경우 소수 판별

defisPrime(n):
ifn<2:
returnFalseforiinrange(2, n):
ifn%i==0:
returnFalsereturnTrue

에라토스테네스의 체

정해진 범위 내 소수를 구하는 경우 사용되는 알고리즘
범위 내 숫자들에서 합성수를 반복적으로 지우는 방식으로 소수를 찾는다.

  • 지워지지 않은수 중 작은수를 소수로 채택, 해당 수의 배수를 모두 제거
defprime_list(n):
prime= [True]*(n+1)
foriinrange(2, int(n**0.5)+1):
ifprime[i] ==True:
forjinrange(2*i, n+1, i):
prime[j] =Falsereturn [iforiinrange(2, n+1) ifprime[i] ==True]

에라토스테네스의 체 블로그1에라토스테네스의 체 블로그2

약수구하기

1부터 루트n까지 n을 나누는 수들을 확인

defdivisors(n):
result= []
foriinrange(1, int(n**0.5)+1):
ifn%i==0:
result.append(i)
result.append(n/i)
returnset(result)

About

파이썬 코딩테스트

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

165 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

CodingTest with Python

Python 문법 및 알고리즘 정리 블로그링크


코딩테스트 시간복잡도

제한시간 1초(n초) = 10^8번 연산 (100,000,000번)
따라서 입력값 n의 크기에 따라 시간복잡도를 고려하여 10^8번 연산 이내로 풀릴 수 있어야 한다.

n <= 20

O(n!), O(2^n)

  • 어떤 알고리즘이든 가능

n <= 100

O(n^3)

  • 3중 for문
  • 플로이드 워셜 알고리즘
  • 완전탐색

n <= 1000

O(n^2)

  • 2중 for문
  • 벨만 포드 알고리즘
  • 선택 정렬
  • 삽입 정렬

n <= 10000 ⭐️

O(n)

  • 동적 프로그래밍(DP)
  • 계수 정렬
  • 유니언 파인드
  • 위상 정렬

O(n log n)

  • 이진 탐색
  • 파라메트릭 서치
  • 다익스트라 알고리즘
  • 세그먼트 트리
  • 투 포인터
  • 퀵 정렬
  • 크루스칼 알고리즘

n <= 10^8

O(log n)

  • 수학적 기믹이 필요
  • 유클리드 호제법

파이썬 라이브러리

itertools (순열, 조합) ⭐️⭐️

# 순열fromitertoolsimportpermutationforcaseinpermutations(array, len(array)):
...
# 조합fromitertoolsimportcombinationsforcaseincombinations(array, len(array)):
...

functools (reduce)

# reducefromfunctoolsimportreduceresult=reduce(lambdax, y: x*y, array)

collections (deque(queue)) ⭐️

fromcollectionsimportdequequeue=deque() # queue 생성queue.append('a') # pushqueue[0] # peak 'a'queue.popleft() # pop 'a'queue.extend(['b', 'c', 'd']) # 배열 push

heapq (priorityQueue) ⭐️

importheapqpq= [] # priorityQueue 생성heapq.heappush(pq, (0, 1)) # pushpq[0] # peak (0, 1)heapq.heappop(pq) # pop (0, 1)array= [b, c, a]
heapq.heapify(array) # array를 priorityQueue로 생성heapq.heappop(array) # pop 'a'

math (gcd)

importmathvalue=math.gcd(3, 6)

copy

importcopyarray2=copy.deepcopy(array)

sys (readline)

importsysinput=sys.stdin.readline()

Python 기본 자료구조

Stack

stack= [] # stack 생성stack.append('a') # pushstack[0] # peakstack.pop() # pop 'a'

Set

s=set() # set 생성s1=set(['a', 'b', 'c']) # array를 set으로 생성s2=set(['b', 'c', 'd'])
s1.add('c') # pushs1.remove('c') # removes1.update(['a', 'b', 'c']) # 배열 pushs1&s2# 교집합 {'b', 'c'}s1|s2# 합집합 {'a', 'b', 'c', 'd'}s1-s2# 교집합을 제거한 s1 {'a'}
{ xforxin'abcde' } # list comprehension {'a', 'b', 'c', 'd', 'e'}'a'ins1# Truelist(s1) # set to list ['a', 'b', 'c']

Dictionary

d= {} # dictionary 생성d= {'a': 1, 'b': 2}
d=dict([('a', 1), ('b', 2)]) # touble array로 생성d_from_list= { x: ord(x) forxin'abc' } # dictionary comprehension {'a': 97, 'b': 98, 'c': 99}d['a'] # value 접근 1d['a'] =3# value updatedeld['a'] # value delete'a'ind# Truelist(d) # ['a', 'b']d.items(): # [(key, value)] 반환 [('b': 2)]d.keys() # ['b']d.values() # [2]forkey, valueinitems(): # key, value 순차접근print(key, value)

List

array= [] # list 생성array.append('a') # pusharray.remove('a') # deletearray=list(range(0, 5)) # [0, 1, 2, 3, 4]array.index(3) # index 3array.count(3) # 1array[-1] # 4array[:-1] # return [0, 1, 2, 3]array[1:-1] # return [1, 2, 3]array[::2] # return [0, 2, 4]array[-1::-2] # return [4, 2, 0]array[1:-1][::-1] # return [3, 2, 1]temp=array+ [5, 6] # return [1, 2, 3, 4, 5, 6]array+= [5, 6] # update [1, 2, 3, 4, 5, 6]# 2차원배열n, m=2, 3graph= [[0]*mfor_inrange(n)]
# [[0, 0, 0],# [0, 0, 0]]graph[n-1][m-1] =1# [[0, 0, 0],# [0, 0, 1]]

Tuple

tuple= ('a', 1) # tuple 생성tuple[0] # 'a'tuple[1] # 1

String

str(1) # '1'str(1)+str(2) # '12'string='abc'string.swapcase() # return 'ABC'string.isupper() # return Falsestring.islower() # return Truestring.upper() # return 'ABC'string.lower() # return 'abc'string='123'string.isdecimal() # return Truestring=' abcd 'string.strip() # return 'abcd'

range

list(range(0, 5)) # [0, 1, 2, 3, 4]foriinrange(0, 5):
print(i, end=", ") # 0, 1, 2, 3, 4,

파이썬 기능들

sort

# list.sort()array= [1, 4, 5, 2, 3]
array.sort() # update [1, 2, 3, 4, 5]array.sort(key=lambdax: -x) # update [5, 4, 3, 2, 1]array.sort(key=lambdax: abs(x-3)) # update [3, 4, 2, 5, 1]array.sort(reverse=True) # update [5, 4, 3, 2, 1]

sorted

# sorted(array)array= [1, 4, 5, 2, 3]
sorted(array) # return [1, 2, 3, 4, 5]sorted(array, key=lambdax: -x) # return [5, 4, 3, 2, 1]sorted(array, reverse=True) # return [5, 4, 3, 2, 1]sorted({1: 'a', 2: 'b'}) # dictionary: return [1, 2]sorted([('a', 3, 10), ('b', 1, 9), ('c', 2, 9)], key=lambdax: (x[2], x[1])) # tuple: return [('b', 1, 9), ('c', 2, 9), ('a', 3, 10)]

filter

array= [1, 2, 3, 4, 5]
list(filter(lambdax: x>2, array)) # [3, 4, 5]

enumerate

array [1, 2, 3]
foridx, valueinenumerate(array):
print(idx, value, end=",") # 0 1,1 2,2 3,

sum

# 총합total=sum([1, 2, 3]) # 6# 1차원 list화array=sum([[1, 2], [3, 4]], []) # [1, 2, 3, 4]

join

# string.join()''.join(['a', 'b', 'c']) # return 'abc'

replace

# string.replace()'abcde'.replace('a', 'A') # return 'Abcde'

ord

counts= [0]*26forcharin'abcde':
counts[ord(char)-ord('a')] +=1

abs

abs(-4) # 4

map

a, b=map(int, input().split())

Infinity

INF=int(1e9)

3항연산자

num=5odd=Trueifnum%2==1elseFalse# True

코딩테스트 알고리즘

그리디

가장 큰 순서대로, 가장 작은 순서대로 와 같은 기준 (정렬 알고리즘과 한쌍)
큰 단위가 작은 단위의 배수형태여야 한다. 아닌 경우 다이나믹 프로그래밍으로 접근

  • while문 대신 몫을 계산하는 식으로 접근

구현 ⭐️

완전탐색 or 시뮬레이션

move= [(0, 1), (1, 0), (0, -1), (-1, 0)]
ny, nx=y+move[direction][0], x+move[direction][1]
ifny<0orny>=nornx<0ornx>=n:
direction= (direction+1)%4y, x=y+move[direction][0], x+move[direction][1]
dict= {'left': (-1, 0), 'right': (1, 0), 'up': (0, 1), 'down': (0, -1)}
nx, ny=x+dict[key][0], y+dict[key][1]
ifnx<-nornx>n:

그래프

노드와 노드 사이에 연결된 간선 정보를 가지고 있는 자료구조

인접행렬

n, m=map(int, input().split())
graph= []
forrowinrange(n):
line= [int(x) forxinlist(input())]
graph.append(line)

인접 리스트

n, m=map(int, input().split())
graph= [[] for_inrange(n)]
graph[0].append((1, 7)) #노드 1, cost 7graph[1].append((0, 7)) #노드 0, cost 7

DFS/BFS

스택, 큐(deque: from collections) 자료구조 필요
재귀: 스택구조, 종료 조건이 필요

DFS: 스택(재귀)

  • 방문하지 않은 경우 재귀를 통해 새로운 노드 방문
defdfs(graph, v, visited):
visited[v] =Trueforiingraph[v]:
ifnotvisited[i]:
dfs(graph, i, visited)

BFS: 큐(deque)

  • 방문하지 않은 경우 queue 에 추가, queue 순서에 맞게 노드 방문
defbfs(graph, start, visited):
queue=deque([start])
visited[start] =Truewhilequeue:
v=queue.popleft()
foriingraph[v]:
ifnotvisited[i]:
queue.append(i)
visited[i] =True

선택 정렬

O(n^2)

defsort(array):
foriinrange(len(array)):
min_index=i;
forjinrange(i+1, len(array)):
ifarray[min_index] >array[j]:
min_index=j;
array[i], array[min_index] =array[min_index], array[i]
returnarray

삽입 정렬

O(n^2) but 데이터가 거의 정렬되어 있을 때 효율적
정렬된 상태 + 적절한 위치 찾아 삽입하여 정렬

defsort(array):
foriinrange(1, len(array)):
forjinrange(i, 0, -1): # i to 1 까지 -1씩 감소ifarray[j] <array[j-1]:
array[j], array[j-1] =array[j-1], array[j]
else:
breakreturnarray

퀵 정렬 ⭐️

O(n log n) but 데이터가 거의 정렬되어 있을 때 비효율적
pivot 을 기준으로 작은, 데이터 좌우 분리, 분리된 좌, 우 역시 동일 방식으로 정렬

좌 → 우: pivot 보다 큰 데이터의 위치(left), 좌 ← 우: pivot 보다 작은 데이터의 위치(right)를 찾아 위치를 서로 변경
작은 데이터 위치(left)가 큰 데이터 위치(right)보다 크거나 같은 경우: pivot, 작은 데이터 switch, 종료

재귀 종료조건: 리스트가 1개인 경우 정렬된 상태이므로 반환
pivot 선택기준: 첫번째 데이터 (호어 분할 방식)

defsort(array):
quick_sort(array, 0, len(array)-1)
defquick_sort(array, start, end):
ifstart>=end: # 원소가 1개인 경우 종료returnpivot=startleft=start+1right=endwhileleft<=right:
whileleft<=endandarray[left] <=array[pivot]:
left+=1# find 큰값 indexwhileright>startandarray[right] >=aray[pivot]:
right-=1# find 작은값 indexifleft>right: # 엇갈린 경우: pivot, 작은값 switcharray[right], array[pivot] =array[pivot], array[right]
pivot=rightelse: # 큰값, 작은값 switch, 이후 while 통해 다음 switch 진행array[left], array[right] =array[right], array[left]
# pivot 보다 작은 값들 | pivot | pivot 보다 큰 값들quick_sort(array, start, pivot-1)
quick_sort(array, pivot+1, end)
defsort(array):
iflen(array) <=1:
returnarraypivot=array[0]
tail=array[1:]
left_side= [xforxintailifx<=pivot]
right_side= [xforxintailifx>pivot]
returnsort(left_side) + [pivot] +sort(right_side)

병합 정렬

O(n log n) 분할 정복(divide and conqure) 방법을 통해 크가 같은 두 리스트로 분할 후 각각 정렬한 결과를 합병
분할하는 횟수는 절반식 줄어들기에 O(logN) 시간이 소모된다.
병합할 때 모든 값들을 비교해야 하기 때문에 O(N) 시간이 소모된다.
퀵정렬보다는 느리지만 최악의 경우도 O(n log n)보장

defmerge_sort(arr):
iflen(arr) <2:
returnarrmid=len(arr) //2low_arr=merge_sort(arr[:mid])
high_arr=merge_sort(arr[mid:])
merged_arr= []
l=h=0whilel<len(low_arr) andh<len(high_arr):
# 좌측이 더 작은 경우 -> 좌측 merged에 추가iflow_arr[l] <high_arr[h]:
merged_arr.append(low_arr[l])
l+=1# 우측이 더 작은 경우 -> 우측 merged에 추가else:
merged_arr.append(high_arr[h])
h+=1merged_arr+=low_arr[l:]
merged_arr+=high_arr[h:]
returnmerged_arr

계수 정렬

O(n + k) (k: 가장큰 수)
범위가 정해진 정수, 중복이 많은 경우 효율적
위치변경 X, 리스트 정보를 통한 정렬

defsort(array):
counts= [0]*(max(array)+1)
foriinrange(len(array)):
counts[array[i]] +=1sorted= []
foriinrange(len(counts)):
sorted+= [i] *counts[i]
returnsorted

순차 탐색

O(n) 앞에서부터 차례로 확인

defsearch(array, target):
foriinrange(len(array)):
ifarray[i] ==target:
returnireturn-1

이진 탐색 ⭐️

O(log n) 정렬된 배열 내에서 중간점 위치 데이터와 비교하며 반복적으로 절반씩 좁혀가며 찾는다
재귀 형식과 반복문 형식이 있는데, stack에 쌓이지 않는 반복문 방식이 더 좋다.

defbinary_search(array, target, start, end):
whilestart<=end:
mid= (start+end) //2ifarray[mid] ==target:
returnmideliftarget<array[mid]:
end=mid-1else:
start=mid+1return-1

파라메트릭 서치 (Parametric Search) ⭐️

주어진 범위 내에서 원하는 값 또는 원하는 조건에 가장 일치하는 값을 찾아내는 알고리즘
최적화 문제(최댓값 x = ?) -> 결정문제(x일 경우 최댓값?)로 바꾸어 풀 수 있다.

  • 범위의 중간값과 조건을 비교하며 범위를 수정해나간다
  • 이진 탐색과 차이점은 값 비교 -> 조건 비교, 반환값 (mid, -1 -> result (현재까지의 최적값))이 다르다는 점.
# 최적화문제: 24시간을 7시간마다 배고파지는 사람이 최소 몇끼니를 먹어야 배부르게 지낼수있는가?# 결정문제: 7시간마다 배고파지는 사람이 x끼니를 먹으면 24시간을 배부르게 지낼 수 있다.defparametricSearch(start, end) {
result=0while (start<=end):
mid= (start+end) /2if (24/mid) <7:
end=mid-0.1else:
start=mid+0.1result=midreturnresult
}

파라메트릭 서치 블로그

이진 탐색 트리

이진 탐색이 동작할 수 있도록 고안된 트리 자료구조
child-left < parent < child-right 식

다이나믹 프로그래밍

큰 문제를 작은 문제로 나눌 수 있는 경우 동일문제를 중복되지 않게 계산하는 방법
Bottom up(반복문), Top down(재귀) 방식
메모이제이션: 계산된 결과들을 메모하는 기법 (캐싱)
O(n)

d= [0] *100defpibo(x):
d[1] =1d[2] =1foriinrange(3, x+1):
d[i] =d[i-1] +d[i-2]

다익스트라

시작 노드로부터 각 노드까지의 최단거리 알고리즘 (음의 간선이 없는 경우)
반복적으로 최소 최단거리의 노드를 선택하며 테이블을 갱신 (heapq 사용)
O(E log V)

  • V: 노드수, E: 간선수
  • visited: [False] * (n+1)
  • distance: [INF] * (n+1)
defdijkstra(graph, startNode):
distance= [INF]*(len(graph)+1)
distance[startNode] =0q= []
# q: (distance, node), distance 가 적은 node 순으로 정렬된다heapq.heappush(q, (0, startNode))
whileq:
currentDistance, currentNode=heapq.heappop(q)
# currentNode 의 distance 가 갱신된적이 있는 경우 무시ifdistance[currentNode] <currentDistance:
continue# currentNode 의 인접노드들 확인fornodeingraph[currentNode]:
nodeNum=node[0]
nodeCost=node[1]
newDistance=currentDistance+nodeCostifnewDistance<distance[nodeNum]:
distance[nodeNum] =newDistanceheapq.heappush(q, (newDistance, nodeNum))
returndistance
importsysimportheapqinput=sys.stdin.readlineINF=int(1e9)
n, m=map(int, input().split())
startNode=int(input())
graph= [[] foriinrange(n+1)]
for_inrange(m):
from, to, cost=map(int, input().split())
graph[from].append((to, cost))
distance=dijkstra(graph, startNode)
foriinrange(1, n+1):
ifdistance[i] ==INF:
print("INF")
else:
print(distance[i])

플로이드 워셜

여러개의 from, to 노드간 최단거리 구하기 알고리즘
점화식: AtoB = min(AtoB, AtoK + KtoB)
k, a, b 순차적으로 3중 for 구조
O(n^3)

deffloydWarshall(graph):
forkinrange(1, n+1):
forainrange(1, n+1):
forbinrange(1, n+1):
graph[a][b] =min(graph[a][b], graph[a][k] +graph[k][b])
graph[b][a] =graph[a][b]
importsysinput=sys.stdin.readline()
INF=int(1e9)
n, m=map(int, input().split())
graph= [[]]*(n+1) for_inrange(n+1)]
forainrange(1, n+1):
forbinrange(1, n+1):
ifa==b:
graph[a][b] =0graph[b][a] =0for_inrange(m):
a, b, cost=map(int, input().split())
graph[a][b] =costgraph[b][a] =costfloydWarshall(graph)
forainrange(1, n+1):
forbinrange(1, n+1):
val=graph[a][b] ifgraph[a][b] !=INFelse"INF"print(val, end=" ")
print()

유니언 파인드 ⭐️

공통 원소가 없는 집합이 필요한 경우
하나의 집합으로 합치는 union 과 원소가 속한 집합을 알려주는 find 연산이 필요
union 의 경우 작은 노드값이 집합의 값이 된다.

# 노드값과 집합값이 같을때까지 재귀적으로 호출deffindParent(parents, x):
ifparents[x] !=x:
# 경로 압축parents[x] =findParent(parents, parants[x])
returnparents[x]
defunionParent(parents, a, b):
setA=findParent(parents, a)
setB=findParent(parents, b)
ifsetA<setB:
parents[b] =setAelse:
parents[a] =setB
v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
unionParent(parents, a, b)
foriinrange(1, v+1):
print(findParent(parents, i), end=" ")
print()

사이클 판별

유향 그래프: DFS 알고리즘
무향 그래프: 유니언 파인드 집합 알고리즘

# 무향그래프에서 사이클 판별v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
cycle=Falseforiinrange(e):
a, b=map(int, input().split())
iffindParent(parents, a) ==findParent(parents, b):
cycle=Truebreakelse:
unionParent(parents, a, b)
ifcycle:
print("사이클 발생")
else:
print("사이클 비발생")

신장 트리

모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프
모든 도시를 연결할 때 최소 비용과 관련된 크루스칼 알고리즘의 경우 최소 신장 트리를 활용한 알고리즘

최소 신장 트리를 구하기 위한 알고리즘

  • 신장 트리(Spanning tree): 모든 정점을 포함, 정점간 서로 연결, 싸이클이 존재하지 않는 그래프
  • 최소 신장 트리(Minimum Spanning Tree): 신장 트리 중 가중치 합이 최소인 그래프
  • 싸이클 여부: 서로소 집합(Disjoint Set)Union & Find 활용 크루스칼 알고리즘 블로그

크루스칼 알고리즘

경로를 최소한의 비용으로 연결하는 알고리즘
그래프 내 모든 정점들을 가장 적은 비용으로 연결하기 위해 사용 (사이클이 없는, 가중치의 합 최소가 되는 상태)
모든 간선에 대해 비용값 기준 정렬, 사이클이 발생하지 않는 간선부터 집합에 포함
무향 그래프이므로 find, union 을 통해 사이클 여부 판별 후 간선비용 총합
O(E log E)

v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
edges= [(a, b, cost) for_inrange(e) a, b, cost=map(int, iniput().split())]
result=0edges.sort()
foredgeinedges:
cost, a, b=edgeiffindParent(parents, a) !=findParent(parents, b):
unionParent(parents, a, b)
result+=costprint(result)

위상정렬

우선순위(선후관계)에 따라 방향성에 거스르지 않도록 순서대로 나열
진입차수 테이블과 queue(deque)가 필요

  1. 진입차수가 0인 노드를 큐에 넣는다
  2. 큐가 빌때까지 꺼내며 해당 노드부터 출발하는 간선을 제거하며 진입차수가 0인 노드를 큐에 넣는다 O(V+E)
deftopologySort(graph, indegree):
result= []
q=deque()
# 처음 시작시 indegree 값이 0인 노드를 넣고 시작foriinrange(1, v+1):
ifindegree[i] ==0:
q.append(i)
whileq:
node=q.popleft()
result.append(node)
foriingraph[node]:
indegree[i] -=1ifindegree[i] ==0:
q.append(i)
returnresult
fromcollectionsimportdequev, e=map(int, input().split())
indegree= [0] * (v+1)
graph= [[] foriinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
graph[a].append(b)
indegree[b] +=1sorted=topologySort(graph, indegree)
forninresult:
print(n, end=" ")

소수 알고리즘

2부터 n-1까지 나누어 떨어지는 수가 없는 경우 소수 판별

defisPrime(n):
ifn<2:
returnFalseforiinrange(2, n):
ifn%i==0:
returnFalsereturnTrue

에라토스테네스의 체

정해진 범위 내 소수를 구하는 경우 사용되는 알고리즘
범위 내 숫자들에서 합성수를 반복적으로 지우는 방식으로 소수를 찾는다.

  • 지워지지 않은수 중 작은수를 소수로 채택, 해당 수의 배수를 모두 제거
defprime_list(n):
prime= [True]*(n+1)
foriinrange(2, int(n**0.5)+1):
ifprime[i] ==True:
forjinrange(2*i, n+1, i):
prime[j] =Falsereturn [iforiinrange(2, n+1) ifprime[i] ==True]

에라토스테네스의 체 블로그1에라토스테네스의 체 블로그2

약수구하기

1부터 루트n까지 n을 나누는 수들을 확인

defdivisors(n):
result= []
foriinrange(1, int(n**0.5)+1):
ifn%i==0:
result.append(i)
result.append(n/i)
returnset(result)

About

파이썬 코딩테스트

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

165 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

CodingTest with Python

Python 문법 및 알고리즘 정리 블로그링크


코딩테스트 시간복잡도

제한시간 1초(n초) = 10^8번 연산 (100,000,000번)
따라서 입력값 n의 크기에 따라 시간복잡도를 고려하여 10^8번 연산 이내로 풀릴 수 있어야 한다.

n <= 20

O(n!), O(2^n)

  • 어떤 알고리즘이든 가능

n <= 100

O(n^3)

  • 3중 for문
  • 플로이드 워셜 알고리즘
  • 완전탐색

n <= 1000

O(n^2)

  • 2중 for문
  • 벨만 포드 알고리즘
  • 선택 정렬
  • 삽입 정렬

n <= 10000 ⭐️

O(n)

  • 동적 프로그래밍(DP)
  • 계수 정렬
  • 유니언 파인드
  • 위상 정렬

O(n log n)

  • 이진 탐색
  • 파라메트릭 서치
  • 다익스트라 알고리즘
  • 세그먼트 트리
  • 투 포인터
  • 퀵 정렬
  • 크루스칼 알고리즘

n <= 10^8

O(log n)

  • 수학적 기믹이 필요
  • 유클리드 호제법

파이썬 라이브러리

itertools (순열, 조합) ⭐️⭐️

# 순열fromitertoolsimportpermutationforcaseinpermutations(array, len(array)):
...
# 조합fromitertoolsimportcombinationsforcaseincombinations(array, len(array)):
...

functools (reduce)

# reducefromfunctoolsimportreduceresult=reduce(lambdax, y: x*y, array)

collections (deque(queue)) ⭐️

fromcollectionsimportdequequeue=deque() # queue 생성queue.append('a') # pushqueue[0] # peak 'a'queue.popleft() # pop 'a'queue.extend(['b', 'c', 'd']) # 배열 push

heapq (priorityQueue) ⭐️

importheapqpq= [] # priorityQueue 생성heapq.heappush(pq, (0, 1)) # pushpq[0] # peak (0, 1)heapq.heappop(pq) # pop (0, 1)array= [b, c, a]
heapq.heapify(array) # array를 priorityQueue로 생성heapq.heappop(array) # pop 'a'

math (gcd)

importmathvalue=math.gcd(3, 6)

copy

importcopyarray2=copy.deepcopy(array)

sys (readline)

importsysinput=sys.stdin.readline()

Python 기본 자료구조

Stack

stack= [] # stack 생성stack.append('a') # pushstack[0] # peakstack.pop() # pop 'a'

Set

s=set() # set 생성s1=set(['a', 'b', 'c']) # array를 set으로 생성s2=set(['b', 'c', 'd'])
s1.add('c') # pushs1.remove('c') # removes1.update(['a', 'b', 'c']) # 배열 pushs1&s2# 교집합 {'b', 'c'}s1|s2# 합집합 {'a', 'b', 'c', 'd'}s1-s2# 교집합을 제거한 s1 {'a'}
{ xforxin'abcde' } # list comprehension {'a', 'b', 'c', 'd', 'e'}'a'ins1# Truelist(s1) # set to list ['a', 'b', 'c']

Dictionary

d= {} # dictionary 생성d= {'a': 1, 'b': 2}
d=dict([('a', 1), ('b', 2)]) # touble array로 생성d_from_list= { x: ord(x) forxin'abc' } # dictionary comprehension {'a': 97, 'b': 98, 'c': 99}d['a'] # value 접근 1d['a'] =3# value updatedeld['a'] # value delete'a'ind# Truelist(d) # ['a', 'b']d.items(): # [(key, value)] 반환 [('b': 2)]d.keys() # ['b']d.values() # [2]forkey, valueinitems(): # key, value 순차접근print(key, value)

List

array= [] # list 생성array.append('a') # pusharray.remove('a') # deletearray=list(range(0, 5)) # [0, 1, 2, 3, 4]array.index(3) # index 3array.count(3) # 1array[-1] # 4array[:-1] # return [0, 1, 2, 3]array[1:-1] # return [1, 2, 3]array[::2] # return [0, 2, 4]array[-1::-2] # return [4, 2, 0]array[1:-1][::-1] # return [3, 2, 1]temp=array+ [5, 6] # return [1, 2, 3, 4, 5, 6]array+= [5, 6] # update [1, 2, 3, 4, 5, 6]# 2차원배열n, m=2, 3graph= [[0]*mfor_inrange(n)]
# [[0, 0, 0],# [0, 0, 0]]graph[n-1][m-1] =1# [[0, 0, 0],# [0, 0, 1]]

Tuple

tuple= ('a', 1) # tuple 생성tuple[0] # 'a'tuple[1] # 1

String

str(1) # '1'str(1)+str(2) # '12'string='abc'string.swapcase() # return 'ABC'string.isupper() # return Falsestring.islower() # return Truestring.upper() # return 'ABC'string.lower() # return 'abc'string='123'string.isdecimal() # return Truestring=' abcd 'string.strip() # return 'abcd'

range

list(range(0, 5)) # [0, 1, 2, 3, 4]foriinrange(0, 5):
print(i, end=", ") # 0, 1, 2, 3, 4,

파이썬 기능들

sort

# list.sort()array= [1, 4, 5, 2, 3]
array.sort() # update [1, 2, 3, 4, 5]array.sort(key=lambdax: -x) # update [5, 4, 3, 2, 1]array.sort(key=lambdax: abs(x-3)) # update [3, 4, 2, 5, 1]array.sort(reverse=True) # update [5, 4, 3, 2, 1]

sorted

# sorted(array)array= [1, 4, 5, 2, 3]
sorted(array) # return [1, 2, 3, 4, 5]sorted(array, key=lambdax: -x) # return [5, 4, 3, 2, 1]sorted(array, reverse=True) # return [5, 4, 3, 2, 1]sorted({1: 'a', 2: 'b'}) # dictionary: return [1, 2]sorted([('a', 3, 10), ('b', 1, 9), ('c', 2, 9)], key=lambdax: (x[2], x[1])) # tuple: return [('b', 1, 9), ('c', 2, 9), ('a', 3, 10)]

filter

array= [1, 2, 3, 4, 5]
list(filter(lambdax: x>2, array)) # [3, 4, 5]

enumerate

array [1, 2, 3]
foridx, valueinenumerate(array):
print(idx, value, end=",") # 0 1,1 2,2 3,

sum

# 총합total=sum([1, 2, 3]) # 6# 1차원 list화array=sum([[1, 2], [3, 4]], []) # [1, 2, 3, 4]

join

# string.join()''.join(['a', 'b', 'c']) # return 'abc'

replace

# string.replace()'abcde'.replace('a', 'A') # return 'Abcde'

ord

counts= [0]*26forcharin'abcde':
counts[ord(char)-ord('a')] +=1

abs

abs(-4) # 4

map

a, b=map(int, input().split())

Infinity

INF=int(1e9)

3항연산자

num=5odd=Trueifnum%2==1elseFalse# True

코딩테스트 알고리즘

그리디

가장 큰 순서대로, 가장 작은 순서대로 와 같은 기준 (정렬 알고리즘과 한쌍)
큰 단위가 작은 단위의 배수형태여야 한다. 아닌 경우 다이나믹 프로그래밍으로 접근

  • while문 대신 몫을 계산하는 식으로 접근

구현 ⭐️

완전탐색 or 시뮬레이션

move= [(0, 1), (1, 0), (0, -1), (-1, 0)]
ny, nx=y+move[direction][0], x+move[direction][1]
ifny<0orny>=nornx<0ornx>=n:
direction= (direction+1)%4y, x=y+move[direction][0], x+move[direction][1]
dict= {'left': (-1, 0), 'right': (1, 0), 'up': (0, 1), 'down': (0, -1)}
nx, ny=x+dict[key][0], y+dict[key][1]
ifnx<-nornx>n:

그래프

노드와 노드 사이에 연결된 간선 정보를 가지고 있는 자료구조

인접행렬

n, m=map(int, input().split())
graph= []
forrowinrange(n):
line= [int(x) forxinlist(input())]
graph.append(line)

인접 리스트

n, m=map(int, input().split())
graph= [[] for_inrange(n)]
graph[0].append((1, 7)) #노드 1, cost 7graph[1].append((0, 7)) #노드 0, cost 7

DFS/BFS

스택, 큐(deque: from collections) 자료구조 필요
재귀: 스택구조, 종료 조건이 필요

DFS: 스택(재귀)

  • 방문하지 않은 경우 재귀를 통해 새로운 노드 방문
defdfs(graph, v, visited):
visited[v] =Trueforiingraph[v]:
ifnotvisited[i]:
dfs(graph, i, visited)

BFS: 큐(deque)

  • 방문하지 않은 경우 queue 에 추가, queue 순서에 맞게 노드 방문
defbfs(graph, start, visited):
queue=deque([start])
visited[start] =Truewhilequeue:
v=queue.popleft()
foriingraph[v]:
ifnotvisited[i]:
queue.append(i)
visited[i] =True

선택 정렬

O(n^2)

defsort(array):
foriinrange(len(array)):
min_index=i;
forjinrange(i+1, len(array)):
ifarray[min_index] >array[j]:
min_index=j;
array[i], array[min_index] =array[min_index], array[i]
returnarray

삽입 정렬

O(n^2) but 데이터가 거의 정렬되어 있을 때 효율적
정렬된 상태 + 적절한 위치 찾아 삽입하여 정렬

defsort(array):
foriinrange(1, len(array)):
forjinrange(i, 0, -1): # i to 1 까지 -1씩 감소ifarray[j] <array[j-1]:
array[j], array[j-1] =array[j-1], array[j]
else:
breakreturnarray

퀵 정렬 ⭐️

O(n log n) but 데이터가 거의 정렬되어 있을 때 비효율적
pivot 을 기준으로 작은, 데이터 좌우 분리, 분리된 좌, 우 역시 동일 방식으로 정렬

좌 → 우: pivot 보다 큰 데이터의 위치(left), 좌 ← 우: pivot 보다 작은 데이터의 위치(right)를 찾아 위치를 서로 변경
작은 데이터 위치(left)가 큰 데이터 위치(right)보다 크거나 같은 경우: pivot, 작은 데이터 switch, 종료

재귀 종료조건: 리스트가 1개인 경우 정렬된 상태이므로 반환
pivot 선택기준: 첫번째 데이터 (호어 분할 방식)

defsort(array):
quick_sort(array, 0, len(array)-1)
defquick_sort(array, start, end):
ifstart>=end: # 원소가 1개인 경우 종료returnpivot=startleft=start+1right=endwhileleft<=right:
whileleft<=endandarray[left] <=array[pivot]:
left+=1# find 큰값 indexwhileright>startandarray[right] >=aray[pivot]:
right-=1# find 작은값 indexifleft>right: # 엇갈린 경우: pivot, 작은값 switcharray[right], array[pivot] =array[pivot], array[right]
pivot=rightelse: # 큰값, 작은값 switch, 이후 while 통해 다음 switch 진행array[left], array[right] =array[right], array[left]
# pivot 보다 작은 값들 | pivot | pivot 보다 큰 값들quick_sort(array, start, pivot-1)
quick_sort(array, pivot+1, end)
defsort(array):
iflen(array) <=1:
returnarraypivot=array[0]
tail=array[1:]
left_side= [xforxintailifx<=pivot]
right_side= [xforxintailifx>pivot]
returnsort(left_side) + [pivot] +sort(right_side)

병합 정렬

O(n log n) 분할 정복(divide and conqure) 방법을 통해 크가 같은 두 리스트로 분할 후 각각 정렬한 결과를 합병
분할하는 횟수는 절반식 줄어들기에 O(logN) 시간이 소모된다.
병합할 때 모든 값들을 비교해야 하기 때문에 O(N) 시간이 소모된다.
퀵정렬보다는 느리지만 최악의 경우도 O(n log n)보장

defmerge_sort(arr):
iflen(arr) <2:
returnarrmid=len(arr) //2low_arr=merge_sort(arr[:mid])
high_arr=merge_sort(arr[mid:])
merged_arr= []
l=h=0whilel<len(low_arr) andh<len(high_arr):
# 좌측이 더 작은 경우 -> 좌측 merged에 추가iflow_arr[l] <high_arr[h]:
merged_arr.append(low_arr[l])
l+=1# 우측이 더 작은 경우 -> 우측 merged에 추가else:
merged_arr.append(high_arr[h])
h+=1merged_arr+=low_arr[l:]
merged_arr+=high_arr[h:]
returnmerged_arr

계수 정렬

O(n + k) (k: 가장큰 수)
범위가 정해진 정수, 중복이 많은 경우 효율적
위치변경 X, 리스트 정보를 통한 정렬

defsort(array):
counts= [0]*(max(array)+1)
foriinrange(len(array)):
counts[array[i]] +=1sorted= []
foriinrange(len(counts)):
sorted+= [i] *counts[i]
returnsorted

순차 탐색

O(n) 앞에서부터 차례로 확인

defsearch(array, target):
foriinrange(len(array)):
ifarray[i] ==target:
returnireturn-1

이진 탐색 ⭐️

O(log n) 정렬된 배열 내에서 중간점 위치 데이터와 비교하며 반복적으로 절반씩 좁혀가며 찾는다
재귀 형식과 반복문 형식이 있는데, stack에 쌓이지 않는 반복문 방식이 더 좋다.

defbinary_search(array, target, start, end):
whilestart<=end:
mid= (start+end) //2ifarray[mid] ==target:
returnmideliftarget<array[mid]:
end=mid-1else:
start=mid+1return-1

파라메트릭 서치 (Parametric Search) ⭐️

주어진 범위 내에서 원하는 값 또는 원하는 조건에 가장 일치하는 값을 찾아내는 알고리즘
최적화 문제(최댓값 x = ?) -> 결정문제(x일 경우 최댓값?)로 바꾸어 풀 수 있다.

  • 범위의 중간값과 조건을 비교하며 범위를 수정해나간다
  • 이진 탐색과 차이점은 값 비교 -> 조건 비교, 반환값 (mid, -1 -> result (현재까지의 최적값))이 다르다는 점.
# 최적화문제: 24시간을 7시간마다 배고파지는 사람이 최소 몇끼니를 먹어야 배부르게 지낼수있는가?# 결정문제: 7시간마다 배고파지는 사람이 x끼니를 먹으면 24시간을 배부르게 지낼 수 있다.defparametricSearch(start, end) {
result=0while (start<=end):
mid= (start+end) /2if (24/mid) <7:
end=mid-0.1else:
start=mid+0.1result=midreturnresult
}

파라메트릭 서치 블로그

이진 탐색 트리

이진 탐색이 동작할 수 있도록 고안된 트리 자료구조
child-left < parent < child-right 식

다이나믹 프로그래밍

큰 문제를 작은 문제로 나눌 수 있는 경우 동일문제를 중복되지 않게 계산하는 방법
Bottom up(반복문), Top down(재귀) 방식
메모이제이션: 계산된 결과들을 메모하는 기법 (캐싱)
O(n)

d= [0] *100defpibo(x):
d[1] =1d[2] =1foriinrange(3, x+1):
d[i] =d[i-1] +d[i-2]

다익스트라

시작 노드로부터 각 노드까지의 최단거리 알고리즘 (음의 간선이 없는 경우)
반복적으로 최소 최단거리의 노드를 선택하며 테이블을 갱신 (heapq 사용)
O(E log V)

  • V: 노드수, E: 간선수
  • visited: [False] * (n+1)
  • distance: [INF] * (n+1)
defdijkstra(graph, startNode):
distance= [INF]*(len(graph)+1)
distance[startNode] =0q= []
# q: (distance, node), distance 가 적은 node 순으로 정렬된다heapq.heappush(q, (0, startNode))
whileq:
currentDistance, currentNode=heapq.heappop(q)
# currentNode 의 distance 가 갱신된적이 있는 경우 무시ifdistance[currentNode] <currentDistance:
continue# currentNode 의 인접노드들 확인fornodeingraph[currentNode]:
nodeNum=node[0]
nodeCost=node[1]
newDistance=currentDistance+nodeCostifnewDistance<distance[nodeNum]:
distance[nodeNum] =newDistanceheapq.heappush(q, (newDistance, nodeNum))
returndistance
importsysimportheapqinput=sys.stdin.readlineINF=int(1e9)
n, m=map(int, input().split())
startNode=int(input())
graph= [[] foriinrange(n+1)]
for_inrange(m):
from, to, cost=map(int, input().split())
graph[from].append((to, cost))
distance=dijkstra(graph, startNode)
foriinrange(1, n+1):
ifdistance[i] ==INF:
print("INF")
else:
print(distance[i])

플로이드 워셜

여러개의 from, to 노드간 최단거리 구하기 알고리즘
점화식: AtoB = min(AtoB, AtoK + KtoB)
k, a, b 순차적으로 3중 for 구조
O(n^3)

deffloydWarshall(graph):
forkinrange(1, n+1):
forainrange(1, n+1):
forbinrange(1, n+1):
graph[a][b] =min(graph[a][b], graph[a][k] +graph[k][b])
graph[b][a] =graph[a][b]
importsysinput=sys.stdin.readline()
INF=int(1e9)
n, m=map(int, input().split())
graph= [[]]*(n+1) for_inrange(n+1)]
forainrange(1, n+1):
forbinrange(1, n+1):
ifa==b:
graph[a][b] =0graph[b][a] =0for_inrange(m):
a, b, cost=map(int, input().split())
graph[a][b] =costgraph[b][a] =costfloydWarshall(graph)
forainrange(1, n+1):
forbinrange(1, n+1):
val=graph[a][b] ifgraph[a][b] !=INFelse"INF"print(val, end=" ")
print()

유니언 파인드 ⭐️

공통 원소가 없는 집합이 필요한 경우
하나의 집합으로 합치는 union 과 원소가 속한 집합을 알려주는 find 연산이 필요
union 의 경우 작은 노드값이 집합의 값이 된다.

# 노드값과 집합값이 같을때까지 재귀적으로 호출deffindParent(parents, x):
ifparents[x] !=x:
# 경로 압축parents[x] =findParent(parents, parants[x])
returnparents[x]
defunionParent(parents, a, b):
setA=findParent(parents, a)
setB=findParent(parents, b)
ifsetA<setB:
parents[b] =setAelse:
parents[a] =setB
v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
unionParent(parents, a, b)
foriinrange(1, v+1):
print(findParent(parents, i), end=" ")
print()

사이클 판별

유향 그래프: DFS 알고리즘
무향 그래프: 유니언 파인드 집합 알고리즘

# 무향그래프에서 사이클 판별v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
cycle=Falseforiinrange(e):
a, b=map(int, input().split())
iffindParent(parents, a) ==findParent(parents, b):
cycle=Truebreakelse:
unionParent(parents, a, b)
ifcycle:
print("사이클 발생")
else:
print("사이클 비발생")

신장 트리

모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프
모든 도시를 연결할 때 최소 비용과 관련된 크루스칼 알고리즘의 경우 최소 신장 트리를 활용한 알고리즘

최소 신장 트리를 구하기 위한 알고리즘

  • 신장 트리(Spanning tree): 모든 정점을 포함, 정점간 서로 연결, 싸이클이 존재하지 않는 그래프
  • 최소 신장 트리(Minimum Spanning Tree): 신장 트리 중 가중치 합이 최소인 그래프
  • 싸이클 여부: 서로소 집합(Disjoint Set)Union & Find 활용 크루스칼 알고리즘 블로그

크루스칼 알고리즘

경로를 최소한의 비용으로 연결하는 알고리즘
그래프 내 모든 정점들을 가장 적은 비용으로 연결하기 위해 사용 (사이클이 없는, 가중치의 합 최소가 되는 상태)
모든 간선에 대해 비용값 기준 정렬, 사이클이 발생하지 않는 간선부터 집합에 포함
무향 그래프이므로 find, union 을 통해 사이클 여부 판별 후 간선비용 총합
O(E log E)

v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
edges= [(a, b, cost) for_inrange(e) a, b, cost=map(int, iniput().split())]
result=0edges.sort()
foredgeinedges:
cost, a, b=edgeiffindParent(parents, a) !=findParent(parents, b):
unionParent(parents, a, b)
result+=costprint(result)

위상정렬

우선순위(선후관계)에 따라 방향성에 거스르지 않도록 순서대로 나열
진입차수 테이블과 queue(deque)가 필요

  1. 진입차수가 0인 노드를 큐에 넣는다
  2. 큐가 빌때까지 꺼내며 해당 노드부터 출발하는 간선을 제거하며 진입차수가 0인 노드를 큐에 넣는다 O(V+E)
deftopologySort(graph, indegree):
result= []
q=deque()
# 처음 시작시 indegree 값이 0인 노드를 넣고 시작foriinrange(1, v+1):
ifindegree[i] ==0:
q.append(i)
whileq:
node=q.popleft()
result.append(node)
foriingraph[node]:
indegree[i] -=1ifindegree[i] ==0:
q.append(i)
returnresult
fromcollectionsimportdequev, e=map(int, input().split())
indegree= [0] * (v+1)
graph= [[] foriinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
graph[a].append(b)
indegree[b] +=1sorted=topologySort(graph, indegree)
forninresult:
print(n, end=" ")

소수 알고리즘

2부터 n-1까지 나누어 떨어지는 수가 없는 경우 소수 판별

defisPrime(n):
ifn<2:
returnFalseforiinrange(2, n):
ifn%i==0:
returnFalsereturnTrue

에라토스테네스의 체

정해진 범위 내 소수를 구하는 경우 사용되는 알고리즘
범위 내 숫자들에서 합성수를 반복적으로 지우는 방식으로 소수를 찾는다.

  • 지워지지 않은수 중 작은수를 소수로 채택, 해당 수의 배수를 모두 제거
defprime_list(n):
prime= [True]*(n+1)
foriinrange(2, int(n**0.5)+1):
ifprime[i] ==True:
forjinrange(2*i, n+1, i):
prime[j] =Falsereturn [iforiinrange(2, n+1) ifprime[i] ==True]

에라토스테네스의 체 블로그1에라토스테네스의 체 블로그2

약수구하기

1부터 루트n까지 n을 나누는 수들을 확인

defdivisors(n):
result= []
foriinrange(1, int(n**0.5)+1):
ifn%i==0:
result.append(i)
result.append(n/i)
returnset(result)

About

파이썬 코딩테스트

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

165 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

CodingTest with Python

Python 문법 및 알고리즘 정리 블로그링크


코딩테스트 시간복잡도

제한시간 1초(n초) = 10^8번 연산 (100,000,000번)
따라서 입력값 n의 크기에 따라 시간복잡도를 고려하여 10^8번 연산 이내로 풀릴 수 있어야 한다.

n <= 20

O(n!), O(2^n)

  • 어떤 알고리즘이든 가능

n <= 100

O(n^3)

  • 3중 for문
  • 플로이드 워셜 알고리즘
  • 완전탐색

n <= 1000

O(n^2)

  • 2중 for문
  • 벨만 포드 알고리즘
  • 선택 정렬
  • 삽입 정렬

n <= 10000 ⭐️

O(n)

  • 동적 프로그래밍(DP)
  • 계수 정렬
  • 유니언 파인드
  • 위상 정렬

O(n log n)

  • 이진 탐색
  • 파라메트릭 서치
  • 다익스트라 알고리즘
  • 세그먼트 트리
  • 투 포인터
  • 퀵 정렬
  • 크루스칼 알고리즘

n <= 10^8

O(log n)

  • 수학적 기믹이 필요
  • 유클리드 호제법

파이썬 라이브러리

itertools (순열, 조합) ⭐️⭐️

# 순열fromitertoolsimportpermutationforcaseinpermutations(array, len(array)):
...
# 조합fromitertoolsimportcombinationsforcaseincombinations(array, len(array)):
...

functools (reduce)

# reducefromfunctoolsimportreduceresult=reduce(lambdax, y: x*y, array)

collections (deque(queue)) ⭐️

fromcollectionsimportdequequeue=deque() # queue 생성queue.append('a') # pushqueue[0] # peak 'a'queue.popleft() # pop 'a'queue.extend(['b', 'c', 'd']) # 배열 push

heapq (priorityQueue) ⭐️

importheapqpq= [] # priorityQueue 생성heapq.heappush(pq, (0, 1)) # pushpq[0] # peak (0, 1)heapq.heappop(pq) # pop (0, 1)array= [b, c, a]
heapq.heapify(array) # array를 priorityQueue로 생성heapq.heappop(array) # pop 'a'

math (gcd)

importmathvalue=math.gcd(3, 6)

copy

importcopyarray2=copy.deepcopy(array)

sys (readline)

importsysinput=sys.stdin.readline()

Python 기본 자료구조

Stack

stack= [] # stack 생성stack.append('a') # pushstack[0] # peakstack.pop() # pop 'a'

Set

s=set() # set 생성s1=set(['a', 'b', 'c']) # array를 set으로 생성s2=set(['b', 'c', 'd'])
s1.add('c') # pushs1.remove('c') # removes1.update(['a', 'b', 'c']) # 배열 pushs1&s2# 교집합 {'b', 'c'}s1|s2# 합집합 {'a', 'b', 'c', 'd'}s1-s2# 교집합을 제거한 s1 {'a'}
{ xforxin'abcde' } # list comprehension {'a', 'b', 'c', 'd', 'e'}'a'ins1# Truelist(s1) # set to list ['a', 'b', 'c']

Dictionary

d= {} # dictionary 생성d= {'a': 1, 'b': 2}
d=dict([('a', 1), ('b', 2)]) # touble array로 생성d_from_list= { x: ord(x) forxin'abc' } # dictionary comprehension {'a': 97, 'b': 98, 'c': 99}d['a'] # value 접근 1d['a'] =3# value updatedeld['a'] # value delete'a'ind# Truelist(d) # ['a', 'b']d.items(): # [(key, value)] 반환 [('b': 2)]d.keys() # ['b']d.values() # [2]forkey, valueinitems(): # key, value 순차접근print(key, value)

List

array= [] # list 생성array.append('a') # pusharray.remove('a') # deletearray=list(range(0, 5)) # [0, 1, 2, 3, 4]array.index(3) # index 3array.count(3) # 1array[-1] # 4array[:-1] # return [0, 1, 2, 3]array[1:-1] # return [1, 2, 3]array[::2] # return [0, 2, 4]array[-1::-2] # return [4, 2, 0]array[1:-1][::-1] # return [3, 2, 1]temp=array+ [5, 6] # return [1, 2, 3, 4, 5, 6]array+= [5, 6] # update [1, 2, 3, 4, 5, 6]# 2차원배열n, m=2, 3graph= [[0]*mfor_inrange(n)]
# [[0, 0, 0],# [0, 0, 0]]graph[n-1][m-1] =1# [[0, 0, 0],# [0, 0, 1]]

Tuple

tuple= ('a', 1) # tuple 생성tuple[0] # 'a'tuple[1] # 1

String

str(1) # '1'str(1)+str(2) # '12'string='abc'string.swapcase() # return 'ABC'string.isupper() # return Falsestring.islower() # return Truestring.upper() # return 'ABC'string.lower() # return 'abc'string='123'string.isdecimal() # return Truestring=' abcd 'string.strip() # return 'abcd'

range

list(range(0, 5)) # [0, 1, 2, 3, 4]foriinrange(0, 5):
print(i, end=", ") # 0, 1, 2, 3, 4,

파이썬 기능들

sort

# list.sort()array= [1, 4, 5, 2, 3]
array.sort() # update [1, 2, 3, 4, 5]array.sort(key=lambdax: -x) # update [5, 4, 3, 2, 1]array.sort(key=lambdax: abs(x-3)) # update [3, 4, 2, 5, 1]array.sort(reverse=True) # update [5, 4, 3, 2, 1]

sorted

# sorted(array)array= [1, 4, 5, 2, 3]
sorted(array) # return [1, 2, 3, 4, 5]sorted(array, key=lambdax: -x) # return [5, 4, 3, 2, 1]sorted(array, reverse=True) # return [5, 4, 3, 2, 1]sorted({1: 'a', 2: 'b'}) # dictionary: return [1, 2]sorted([('a', 3, 10), ('b', 1, 9), ('c', 2, 9)], key=lambdax: (x[2], x[1])) # tuple: return [('b', 1, 9), ('c', 2, 9), ('a', 3, 10)]

filter

array= [1, 2, 3, 4, 5]
list(filter(lambdax: x>2, array)) # [3, 4, 5]

enumerate

array [1, 2, 3]
foridx, valueinenumerate(array):
print(idx, value, end=",") # 0 1,1 2,2 3,

sum

# 총합total=sum([1, 2, 3]) # 6# 1차원 list화array=sum([[1, 2], [3, 4]], []) # [1, 2, 3, 4]

join

# string.join()''.join(['a', 'b', 'c']) # return 'abc'

replace

# string.replace()'abcde'.replace('a', 'A') # return 'Abcde'

ord

counts= [0]*26forcharin'abcde':
counts[ord(char)-ord('a')] +=1

abs

abs(-4) # 4

map

a, b=map(int, input().split())

Infinity

INF=int(1e9)

3항연산자

num=5odd=Trueifnum%2==1elseFalse# True

코딩테스트 알고리즘

그리디

가장 큰 순서대로, 가장 작은 순서대로 와 같은 기준 (정렬 알고리즘과 한쌍)
큰 단위가 작은 단위의 배수형태여야 한다. 아닌 경우 다이나믹 프로그래밍으로 접근

  • while문 대신 몫을 계산하는 식으로 접근

구현 ⭐️

완전탐색 or 시뮬레이션

move= [(0, 1), (1, 0), (0, -1), (-1, 0)]
ny, nx=y+move[direction][0], x+move[direction][1]
ifny<0orny>=nornx<0ornx>=n:
direction= (direction+1)%4y, x=y+move[direction][0], x+move[direction][1]
dict= {'left': (-1, 0), 'right': (1, 0), 'up': (0, 1), 'down': (0, -1)}
nx, ny=x+dict[key][0], y+dict[key][1]
ifnx<-nornx>n:

그래프

노드와 노드 사이에 연결된 간선 정보를 가지고 있는 자료구조

인접행렬

n, m=map(int, input().split())
graph= []
forrowinrange(n):
line= [int(x) forxinlist(input())]
graph.append(line)

인접 리스트

n, m=map(int, input().split())
graph= [[] for_inrange(n)]
graph[0].append((1, 7)) #노드 1, cost 7graph[1].append((0, 7)) #노드 0, cost 7

DFS/BFS

스택, 큐(deque: from collections) 자료구조 필요
재귀: 스택구조, 종료 조건이 필요

DFS: 스택(재귀)

  • 방문하지 않은 경우 재귀를 통해 새로운 노드 방문
defdfs(graph, v, visited):
visited[v] =Trueforiingraph[v]:
ifnotvisited[i]:
dfs(graph, i, visited)

BFS: 큐(deque)

  • 방문하지 않은 경우 queue 에 추가, queue 순서에 맞게 노드 방문
defbfs(graph, start, visited):
queue=deque([start])
visited[start] =Truewhilequeue:
v=queue.popleft()
foriingraph[v]:
ifnotvisited[i]:
queue.append(i)
visited[i] =True

선택 정렬

O(n^2)

defsort(array):
foriinrange(len(array)):
min_index=i;
forjinrange(i+1, len(array)):
ifarray[min_index] >array[j]:
min_index=j;
array[i], array[min_index] =array[min_index], array[i]
returnarray

삽입 정렬

O(n^2) but 데이터가 거의 정렬되어 있을 때 효율적
정렬된 상태 + 적절한 위치 찾아 삽입하여 정렬

defsort(array):
foriinrange(1, len(array)):
forjinrange(i, 0, -1): # i to 1 까지 -1씩 감소ifarray[j] <array[j-1]:
array[j], array[j-1] =array[j-1], array[j]
else:
breakreturnarray

퀵 정렬 ⭐️

O(n log n) but 데이터가 거의 정렬되어 있을 때 비효율적
pivot 을 기준으로 작은, 데이터 좌우 분리, 분리된 좌, 우 역시 동일 방식으로 정렬

좌 → 우: pivot 보다 큰 데이터의 위치(left), 좌 ← 우: pivot 보다 작은 데이터의 위치(right)를 찾아 위치를 서로 변경
작은 데이터 위치(left)가 큰 데이터 위치(right)보다 크거나 같은 경우: pivot, 작은 데이터 switch, 종료

재귀 종료조건: 리스트가 1개인 경우 정렬된 상태이므로 반환
pivot 선택기준: 첫번째 데이터 (호어 분할 방식)

defsort(array):
quick_sort(array, 0, len(array)-1)
defquick_sort(array, start, end):
ifstart>=end: # 원소가 1개인 경우 종료returnpivot=startleft=start+1right=endwhileleft<=right:
whileleft<=endandarray[left] <=array[pivot]:
left+=1# find 큰값 indexwhileright>startandarray[right] >=aray[pivot]:
right-=1# find 작은값 indexifleft>right: # 엇갈린 경우: pivot, 작은값 switcharray[right], array[pivot] =array[pivot], array[right]
pivot=rightelse: # 큰값, 작은값 switch, 이후 while 통해 다음 switch 진행array[left], array[right] =array[right], array[left]
# pivot 보다 작은 값들 | pivot | pivot 보다 큰 값들quick_sort(array, start, pivot-1)
quick_sort(array, pivot+1, end)
defsort(array):
iflen(array) <=1:
returnarraypivot=array[0]
tail=array[1:]
left_side= [xforxintailifx<=pivot]
right_side= [xforxintailifx>pivot]
returnsort(left_side) + [pivot] +sort(right_side)

병합 정렬

O(n log n) 분할 정복(divide and conqure) 방법을 통해 크가 같은 두 리스트로 분할 후 각각 정렬한 결과를 합병
분할하는 횟수는 절반식 줄어들기에 O(logN) 시간이 소모된다.
병합할 때 모든 값들을 비교해야 하기 때문에 O(N) 시간이 소모된다.
퀵정렬보다는 느리지만 최악의 경우도 O(n log n)보장

defmerge_sort(arr):
iflen(arr) <2:
returnarrmid=len(arr) //2low_arr=merge_sort(arr[:mid])
high_arr=merge_sort(arr[mid:])
merged_arr= []
l=h=0whilel<len(low_arr) andh<len(high_arr):
# 좌측이 더 작은 경우 -> 좌측 merged에 추가iflow_arr[l] <high_arr[h]:
merged_arr.append(low_arr[l])
l+=1# 우측이 더 작은 경우 -> 우측 merged에 추가else:
merged_arr.append(high_arr[h])
h+=1merged_arr+=low_arr[l:]
merged_arr+=high_arr[h:]
returnmerged_arr

계수 정렬

O(n + k) (k: 가장큰 수)
범위가 정해진 정수, 중복이 많은 경우 효율적
위치변경 X, 리스트 정보를 통한 정렬

defsort(array):
counts= [0]*(max(array)+1)
foriinrange(len(array)):
counts[array[i]] +=1sorted= []
foriinrange(len(counts)):
sorted+= [i] *counts[i]
returnsorted

순차 탐색

O(n) 앞에서부터 차례로 확인

defsearch(array, target):
foriinrange(len(array)):
ifarray[i] ==target:
returnireturn-1

이진 탐색 ⭐️

O(log n) 정렬된 배열 내에서 중간점 위치 데이터와 비교하며 반복적으로 절반씩 좁혀가며 찾는다
재귀 형식과 반복문 형식이 있는데, stack에 쌓이지 않는 반복문 방식이 더 좋다.

defbinary_search(array, target, start, end):
whilestart<=end:
mid= (start+end) //2ifarray[mid] ==target:
returnmideliftarget<array[mid]:
end=mid-1else:
start=mid+1return-1

파라메트릭 서치 (Parametric Search) ⭐️

주어진 범위 내에서 원하는 값 또는 원하는 조건에 가장 일치하는 값을 찾아내는 알고리즘
최적화 문제(최댓값 x = ?) -> 결정문제(x일 경우 최댓값?)로 바꾸어 풀 수 있다.

  • 범위의 중간값과 조건을 비교하며 범위를 수정해나간다
  • 이진 탐색과 차이점은 값 비교 -> 조건 비교, 반환값 (mid, -1 -> result (현재까지의 최적값))이 다르다는 점.
# 최적화문제: 24시간을 7시간마다 배고파지는 사람이 최소 몇끼니를 먹어야 배부르게 지낼수있는가?# 결정문제: 7시간마다 배고파지는 사람이 x끼니를 먹으면 24시간을 배부르게 지낼 수 있다.defparametricSearch(start, end) {
result=0while (start<=end):
mid= (start+end) /2if (24/mid) <7:
end=mid-0.1else:
start=mid+0.1result=midreturnresult
}

파라메트릭 서치 블로그

이진 탐색 트리

이진 탐색이 동작할 수 있도록 고안된 트리 자료구조
child-left < parent < child-right 식

다이나믹 프로그래밍

큰 문제를 작은 문제로 나눌 수 있는 경우 동일문제를 중복되지 않게 계산하는 방법
Bottom up(반복문), Top down(재귀) 방식
메모이제이션: 계산된 결과들을 메모하는 기법 (캐싱)
O(n)

d= [0] *100defpibo(x):
d[1] =1d[2] =1foriinrange(3, x+1):
d[i] =d[i-1] +d[i-2]

다익스트라

시작 노드로부터 각 노드까지의 최단거리 알고리즘 (음의 간선이 없는 경우)
반복적으로 최소 최단거리의 노드를 선택하며 테이블을 갱신 (heapq 사용)
O(E log V)

  • V: 노드수, E: 간선수
  • visited: [False] * (n+1)
  • distance: [INF] * (n+1)
defdijkstra(graph, startNode):
distance= [INF]*(len(graph)+1)
distance[startNode] =0q= []
# q: (distance, node), distance 가 적은 node 순으로 정렬된다heapq.heappush(q, (0, startNode))
whileq:
currentDistance, currentNode=heapq.heappop(q)
# currentNode 의 distance 가 갱신된적이 있는 경우 무시ifdistance[currentNode] <currentDistance:
continue# currentNode 의 인접노드들 확인fornodeingraph[currentNode]:
nodeNum=node[0]
nodeCost=node[1]
newDistance=currentDistance+nodeCostifnewDistance<distance[nodeNum]:
distance[nodeNum] =newDistanceheapq.heappush(q, (newDistance, nodeNum))
returndistance
importsysimportheapqinput=sys.stdin.readlineINF=int(1e9)
n, m=map(int, input().split())
startNode=int(input())
graph= [[] foriinrange(n+1)]
for_inrange(m):
from, to, cost=map(int, input().split())
graph[from].append((to, cost))
distance=dijkstra(graph, startNode)
foriinrange(1, n+1):
ifdistance[i] ==INF:
print("INF")
else:
print(distance[i])

플로이드 워셜

여러개의 from, to 노드간 최단거리 구하기 알고리즘
점화식: AtoB = min(AtoB, AtoK + KtoB)
k, a, b 순차적으로 3중 for 구조
O(n^3)

deffloydWarshall(graph):
forkinrange(1, n+1):
forainrange(1, n+1):
forbinrange(1, n+1):
graph[a][b] =min(graph[a][b], graph[a][k] +graph[k][b])
graph[b][a] =graph[a][b]
importsysinput=sys.stdin.readline()
INF=int(1e9)
n, m=map(int, input().split())
graph= [[]]*(n+1) for_inrange(n+1)]
forainrange(1, n+1):
forbinrange(1, n+1):
ifa==b:
graph[a][b] =0graph[b][a] =0for_inrange(m):
a, b, cost=map(int, input().split())
graph[a][b] =costgraph[b][a] =costfloydWarshall(graph)
forainrange(1, n+1):
forbinrange(1, n+1):
val=graph[a][b] ifgraph[a][b] !=INFelse"INF"print(val, end=" ")
print()

유니언 파인드 ⭐️

공통 원소가 없는 집합이 필요한 경우
하나의 집합으로 합치는 union 과 원소가 속한 집합을 알려주는 find 연산이 필요
union 의 경우 작은 노드값이 집합의 값이 된다.

# 노드값과 집합값이 같을때까지 재귀적으로 호출deffindParent(parents, x):
ifparents[x] !=x:
# 경로 압축parents[x] =findParent(parents, parants[x])
returnparents[x]
defunionParent(parents, a, b):
setA=findParent(parents, a)
setB=findParent(parents, b)
ifsetA<setB:
parents[b] =setAelse:
parents[a] =setB
v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
unionParent(parents, a, b)
foriinrange(1, v+1):
print(findParent(parents, i), end=" ")
print()

사이클 판별

유향 그래프: DFS 알고리즘
무향 그래프: 유니언 파인드 집합 알고리즘

# 무향그래프에서 사이클 판별v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
cycle=Falseforiinrange(e):
a, b=map(int, input().split())
iffindParent(parents, a) ==findParent(parents, b):
cycle=Truebreakelse:
unionParent(parents, a, b)
ifcycle:
print("사이클 발생")
else:
print("사이클 비발생")

신장 트리

모든 노드를 포함하면서 사이클이 존재하지 않는 부분 그래프
모든 도시를 연결할 때 최소 비용과 관련된 크루스칼 알고리즘의 경우 최소 신장 트리를 활용한 알고리즘

최소 신장 트리를 구하기 위한 알고리즘

  • 신장 트리(Spanning tree): 모든 정점을 포함, 정점간 서로 연결, 싸이클이 존재하지 않는 그래프
  • 최소 신장 트리(Minimum Spanning Tree): 신장 트리 중 가중치 합이 최소인 그래프
  • 싸이클 여부: 서로소 집합(Disjoint Set)Union & Find 활용 크루스칼 알고리즘 블로그

크루스칼 알고리즘

경로를 최소한의 비용으로 연결하는 알고리즘
그래프 내 모든 정점들을 가장 적은 비용으로 연결하기 위해 사용 (사이클이 없는, 가중치의 합 최소가 되는 상태)
모든 간선에 대해 비용값 기준 정렬, 사이클이 발생하지 않는 간선부터 집합에 포함
무향 그래프이므로 find, union 을 통해 사이클 여부 판별 후 간선비용 총합
O(E log E)

v, e=map(int, input().split())
parents= [iforiinrange(v+1)]
edges= [(a, b, cost) for_inrange(e) a, b, cost=map(int, iniput().split())]
result=0edges.sort()
foredgeinedges:
cost, a, b=edgeiffindParent(parents, a) !=findParent(parents, b):
unionParent(parents, a, b)
result+=costprint(result)

위상정렬

우선순위(선후관계)에 따라 방향성에 거스르지 않도록 순서대로 나열
진입차수 테이블과 queue(deque)가 필요

  1. 진입차수가 0인 노드를 큐에 넣는다
  2. 큐가 빌때까지 꺼내며 해당 노드부터 출발하는 간선을 제거하며 진입차수가 0인 노드를 큐에 넣는다 O(V+E)
deftopologySort(graph, indegree):
result= []
q=deque()
# 처음 시작시 indegree 값이 0인 노드를 넣고 시작foriinrange(1, v+1):
ifindegree[i] ==0:
q.append(i)
whileq:
node=q.popleft()
result.append(node)
foriingraph[node]:
indegree[i] -=1ifindegree[i] ==0:
q.append(i)
returnresult
fromcollectionsimportdequev, e=map(int, input().split())
indegree= [0] * (v+1)
graph= [[] foriinrange(v+1)]
for_inrange(e):
a, b=map(int, input().split())
graph[a].append(b)
indegree[b] +=1sorted=topologySort(graph, indegree)
forninresult:
print(n, end=" ")

소수 알고리즘

2부터 n-1까지 나누어 떨어지는 수가 없는 경우 소수 판별

defisPrime(n):
ifn<2:
returnFalseforiinrange(2, n):
ifn%i==0:
returnFalsereturnTrue

에라토스테네스의 체

정해진 범위 내 소수를 구하는 경우 사용되는 알고리즘
범위 내 숫자들에서 합성수를 반복적으로 지우는 방식으로 소수를 찾는다.

  • 지워지지 않은수 중 작은수를 소수로 채택, 해당 수의 배수를 모두 제거
defprime_list(n):
prime= [True]*(n+1)
foriinrange(2, int(n**0.5)+1):
ifprime[i] ==True:
forjinrange(2*i, n+1, i):
prime[j] =Falsereturn [iforiinrange(2, n+1) ifprime[i] ==True]

에라토스테네스의 체 블로그1에라토스테네스의 체 블로그2

약수구하기

1부터 루트n까지 n을 나누는 수들을 확인

defdivisors(n):
result= []
foriinrange(1, int(n**0.5)+1):
ifn%i==0:
result.append(i)
result.append(n/i)
returnset(result)

About

파이썬 코딩테스트

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages