1. 정렬
1-1. 정렬 라이브러리
"""
1. sorted 함수 사용
"""
array = [7, 5, 9, 0, 1, 6, 2, 4, 8]
result = sorted(array)
print(result) # [0, 1, 2, 4, 5, 6, 7, 8, 9]
"""
2. sort 메소드 사용 - 리스트 변수
"""
array = [7, 5, 9, 0, 1, 6, 2, 4, 8]
array.sort()
print(array) # # [0, 1, 2, 4, 5, 6, 7, 8, 9]
"""
3. sort함수 - 람다
"""
data = [(25, 'Na'), (20, 'Kim'), (23, 'Seo'), (28, 'Park'), (20, 'Ahn')]
data.sort(key=lambda x: x[0]) # Stable Sort (When using a key)
print(data)
"""
4. 정렬 여러개하는 경우
"""
data = [(25, 'Na'), (20, 'Kim'), (23, 'Seo'), (28, 'Park'), (20, 'Ahn')]
s = sorted(data, key=lambda x: (x[1], x[2]))
2. BFS / DFS
2-1. bfs
2-1-1. 인접 리스트
from collections import deque
def bfs(graph, visited, start):
dq = deque([start])
visited[start] = True
while dq:
now = dq.popleft()
print(now, end=" ")
for next in graph[now]:
if not visited[next]:
dq.append(next)
visited[next] = True
graph = [
[],
[2, 3, 8],
[1, 7],
[1, 4, 5],
[3, 5],
[3, 4],
[7],
[2, 6, 8],
[1, 7]
]
visited = [False] * len(graph)
bfs(graph, visited, 1)
2-1-2. 인접 행렬
from collections import deque
# 상, 하, 좌, 우
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
n, m = 4, 3 # 세로, 가로
graph = [
[1, 1, 1],
[0, 0, 0],
[1, 1, 1],
[1, 1, 0]
]
visited = [[False] * m for _ in range(n)] # 방문 유무
count = 0 # 섬의 수
dq = deque()
for i in range(n):
for j in range(m):
if not visited[i][j] and graph[i][j] == 1:
count += 1
dq.append((i, j))
visited[i][j] = True
while dq:
x, y = dq.popleft()
for d in range(4):
nx = x + dx[d]
ny = y + dy[d]
if nx < 0 or ny < 0 or nx >= n or ny >= m:
continue
if visited[nx][ny] or graph[nx][ny] != 1:
continue
dq.append((nx, ny))
visited[nx][ny] = True
print(count)
"""
시작 (0,0) 위치에서 (n, m) 출구까지 이동한 거리
"""
from collections import deque
n, m = 5, 6
graph = [[1, 0, 1, 0, 1, 0],
[1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1]]
dx = [0, 0, -1, 1]
dy = [-1, 1, 0, 0]
result = 0
dq = deque()
dq.append((0, 0))
while dq:
x, y = dq.popleft()
for d in range(4):
nx = x + dx[d]
ny = y + dy[d]
if nx < 0 or ny < 0 or nx >= n or ny >= m:
continue
if graph[nx][ny] == 1:
graph[nx][ny] = graph[x][y] + 1
dq.append((nx, ny))
print(graph[n - 1][m - 1])
2-2. dfs
2-2-1. 인접 리스트
def dfs(graph, visited, start):
visited[start] = True
print(start, end=" ")
for next in graph[start]:
if not visited[next]:
dfs(graph, visited, next)
graph = [
[],
[2, 3, 8],
[1, 7],
[1, 4, 5],
[3, 5],
[3, 4],
[7],
[2, 6, 8],
[1, 7]
]
visited = [False] * len(graph)
dfs(graph, visited, 1)
2-2-2. 인접 행렬
n, m = 4, 3 # 세로, 가로
graph = [
[1, 1, 1],
[0, 0, 0],
[1, 1, 1],
[1, 1, 0]
]
visited = [[False] * m for _ in range(n)] # 방문 유무
def dfs(i, j):
if i < 0 or j < 0 or i >= n or j >= m:
return False
if graph[i][j] == 0:
return False
graph[i][j] = 0
dfs(i + 1, j)
dfs(i - 1, j)
dfs(i, j + 1)
dfs(i, j - 1)
return True
count = 0 # 독립된 노드 수
for i in range(n):
for j in range(m):
if dfs(i, j):
count += 1
print(count)
3. 이진 탐색
3-1. 이진 탐색 구현
def binary_search(array, target):
left = 0
right = len(array) - 1
while left <= right:
mid = (left + right) // 2
if array[mid] < target:
left = mid + 1
elif array[mid] > target:
right = mid - 1
elif array[mid] == target:
return mid
return None
array=[-1, 0, 3, 5, 9, 12] # 정렬된 리스트!
result = binary_search(array=array, target=2)
print(result)
3-2. 이진탐색 모듈 사용(bisect)
import bisect
def binary_search(array=list(), target=int()):
index = bisect.bisect_left(array, target)
if index < len(array) and array[index] == target:
return index
return None
array=[-1, 0 , 3, 5, 9, 12] # 정렬된 리스트!
result = binary_search(array=array, target=5)
print(result)
3-3. 이진탐색 모듈 사용하여 범위 구하기 (bisect)
import bisect
def count_by_range(a, left_val, right_val):
right_val = bisect.bisect_right(a, right_val)
left_val = bisect.bisect_left(a, left_val)
return right_val - left_val
a = [1, 2, 3, 3, 3, 3, 4, 4, 8, 9]
print(count_by_range(a, 4, 4))
print(count_by_range(a, -1, 3))
4. Graph
- 다익스트라, 플로이드 와샬: 가중치가 있는 노드 거리 구하기
- 크루스칼: 가장 적은 비용으로 모든 노드를 연결
- 위상 정렬: 순서가 정해진 작업을 차례대로 수행할 때 사용
4-1. 다익스트라 - 한점 ~ 모든 노드 거리 구하기
import sys
import heapq
INF = int(1e9)
def dijkstra(start):
"""
1. 시작 노드 최단 테이블 0 설정 & q에 삽입
2. 방문하지 않은 노드 중 가장 짧은 노드를 선택 > heapq 사용(최소힙)
3. 해당 노드를 거쳐가는 다른 노드를 계산하여 작은 경우는 갱신 & 큐에 넣기
4. 2~3 반복
:param start:
:return:
"""
q = []
# 1. 최단 거리 테이블 초기화
heapq.heappush(q, (0, start))
distance[start] = 0
while q:
dist, now = heapq.heappop(q)
if dist > distance[now]: # 이미 방문함
continue
for i in graph[now]:
cost = dist + i[1]
if cost < distance[i[0]]:
distance[i[0]] = cost
heapq.heappush(q, (cost, i[0]))
n, m = map(int, sys.stdin.readline().split()) # 노드, 간선
start = int(sys.stdin.readline().split()[0])
graph = [[] for _ in range(n + 1)]
distance = [INF] * (n + 1)
for _ in range(m):
a, b, c = map(int, sys.stdin.readline().split()) # a -> b, c 비용
graph[a].append((b, c))
dijkstra(start)
for i in range(1, n + 1):
if distance[i] == INF:
print("INF")
else:
print(distance[i])
'''
[Input Example 1]
5 6
1
5 1 1
1 2 2
1 3 3
2 3 4
2 4 5
3 4 6
[Output Example 1]
0
2
3
7
INFINITY
'''
4-2. 플로이드 와샬 - 모든 노드
import sys
INF = int(1e9)
n = int(sys.stdin.readline().split()[0]) # 노드 수
m = int(sys.stdin.readline().split()[0]) # 간선 수
# 1. 2차원 배열 - 모든 값을 초기화: 모든 거리 무한, 자신으로 가는 거리 0
graph = [[INF] * (n + 1) for _ in range(n + 1)] # 모든 최단 거리를 저장
for i in range(1, n + 1):
for j in range(1, n + 1):
if i == j:
graph[i][j] = 0
for _ in range(m):
a, b, c = map(int, sys.stdin.readline().split())
graph[a][b] = c
# 2. 다이나믹 프로그래밍
for k in range(1, n + 1):
for a in range(1, n + 1):
for b in range(1, n + 1):
graph[a][b] = min(graph[a][b], graph[a][k] + graph[k][b])
for a in range(1, n + 1):
for b in range(1, n + 1):
if graph[a][b] == INF:
print("inf")
else:
print(graph[a][b], end=" ")
print()
"""
[Input Example 1]
4
7
1 2 4
1 4 6
2 1 3
2 3 7
3 1 5
3 4 4
4 3 2
[Output Example 1]
0 4 8 6
3 0 7 9
5 9 0 4
7 11 2 0
"""
4-3. 크루스칼 - 모든 노드 연결
import sys
def find_parent(parent, x):
"""
자기가 속한 최종 부모 찾기
:param parent:
:param x:
:return:
"""
if parent[x] != x:
parent[x] = find_parent(parent, parent[x])
return parent[x]
def union_parent(parent, a, b):
"""
두 원소 합치기 - 부모 동일
:param parent:
:param a:
:param b:
:return:
"""
a = find_parent(parent, a)
b = find_parent(parent, b)
if a < b:
parent[b] = a
else:
parent[a] = b
v, e = map(int, sys.stdin.readline().split()) # 노드 개수, 간선 개수
parent = [0] * (v + 1)
edges = []
result = 0
# 1. 부모 저장 테이블 - 자기 자신으로 세팅
for i in range(1, v + 1):
parent[i] = i
for _ in range(e):
a, b, cost = map(int, sys.stdin.readline().split())
edges.append((cost, a, b))
# 2. 간선 그래프를 비용에 따른 오름차순으로 설정. 그리디 - 정렬 필요
edges.sort()
for edge in edges:
cost, a, b = edge
if find_parent(parent, a) != find_parent(parent, b): # 사이클 발생 안한 경우 합치기
union_parent(parent, a, b)
result += cost
print(result) # 연결된 노드 최종 비용
"""
크루스칼 알고리즘
[Input Example 1]
7 9
1 2 29
1 5 75
2 3 35
2 6 34
3 4 7
4 6 23
4 7 13
5 6 53
6 7 25
[Output Example 1]
159
"""
4-4. 위상정렬 - 순서
from collections import deque
import sys
v, e = map(int, sys.stdin.readline().split()) # 노드 수, 간선 수
indegree = [0] * (v + 1)
graph = [[] for i in range(v + 1)]
for _ in range(e):
a, b = map(int, sys.stdin.readline().split())
graph[a].append(b) # a > b
indegree[b] += 1 # 진입 차수
def topology_sort():
result = []
q = deque()
# 1. 진입차수가 0인 모든 노드를 큐에 넣는다.
for i in range(1, v + 1):
if indegree[i] == 0:
q.append(i)
while q:
now = q.popleft()
result.append(now)
for i in graph[now]:
# 2. 큐에서 원소를 꺼내 해당 노드에서 나가는 간선을 그래프에서 제거
indegree[i] -= 1
# 3. 새롭게 진입차수가 0이된 노드를 큐에 넣기
if indegree[i] == 0:
q.append(i)
for i in result:
print(i, end=' ')
topology_sort()
"""
위상 정렬 알고리즘
[Input Example 1]
7 8
1 2
1 5
2 3
2 6
3 4
4 7
5 6
6 4
[Output Example 1]
1 2 5 3 6 4 7
"""
5. 완전 탐색
5-1. 백트래킹
6. 기타
6-1. 투포인터
6-2. 구간 합
6-3. 2차원 돌리기