1. 스택

삽입과 삭제가 LIFO(후입선출) 방식으로 이루어진 자료구조
구현 방법
0번 강의에서 언급했듯이 파이썬에선 리스트로 간단하게 스택 구현이 가능
리스트 이름이 s일때
- s.appped(data): top 위치에 새로운 데이터를 삽입하는 연산
- s.pop(): top 위치에 현재 있는 데이터를 삭제하고 확인하는 연산
- s[-1]: top 위치에 현재 있는 데이터를 단순 확인하는 연산
쓰이는 곳
- DFS
- 쌍이 맞아야 하는 문제(괄호 여닫기 등)
2. DFS

그래프: 노드(정점)와 에지(간선)로 이루어진 자료구조
그래프의 시작 노드에서 출발하여 탐색할 한 쪽 분기를 정하여 최대 깊이까지 탐색을 마친 후, 다른 쪽 분기로 이동하여 다시 탐색을 수행하는 그래프 완전 탐색 알고리즘
시간 복잡도(노드 수: V, 에지 수: E): O(V + E)
시간 복잡도가 중요한 이유:
시간 복잡도는 문제를 풀 때 알고리즘 선택의 기준이 되기 때문입니다.
어려운 알고리즘 문제는 문제를 보아도 어떤 알고리즘, 자료구조를 써야 하는지 모르는 경우가 대다수입니다. 이럴 때 각 알고리즘의 시간 복잡도를 알고 있다면 주어지는 입력값과 비교하여 가능한 알고리즘을 추릴 수 있습니다.
가지고 있는 도구(알고리즘 및 자료구조 등) 중에 어떤 도구를 선택해야 하는지 후보를 추릴 수 있다면 문제해결방법을 고민하는 과정이 크게 단축됩니다.
대략적인 기준으로는 입력값 10^8 기준으로 1초가 걸린다고 생각해주시면 될 것 같습니다.
즉, 1초든 2초든 작은 상수의 영향이 미미한 프로그래밍 세계에서는 10^8안의 연산값으로 해결해야 한다고 생각해주시면 됩니다.
시간 복잡도에 대한 자세한 내용은 빅오-표기법에 대해서 검색하여 공부하셔야 합니다.
구현 방법
DFS를 구현하려면 아래 두 가지 핵심 지식(인접 리스트, 재귀 함수)을 알아야 함
파이썬에서 그래프는 일반적으로 인접 리스트 형태로 표현
인접 리스트:
1. 리스트의 각 index를 노드라 생각하고, 리스트내 원소를 에지라 생각하는 구조. (노드가 1번부터 시작하는 경우가 많기 때문에 index 0은 비워줌)
EX) 1, 2, 3의 노드가 존재하고 에지는 1 -> 2, 2 -> 3, 1-> 3이 존재한다고 하면 리스트의 형태는 [[], [2, 3], [3], []].
2. 한 번 방문한 노드를 다시 방문하면 안되므로 노드 번호를 index로 하는 방문 여부를 체크할 리스트가 필요 visited_list = [False, False, False, False]
재귀 함수:
파이썬에서 함수를 호출하고 나면 그 함수가 종료 된 뒤 기존 함수가 진행
EX)f 함수에서 f2를 호출하면 f2가 종료된 뒤 f의 나머지 코드들이 실행
위 두개 개념을 이용하여 구현
- DFS를 시작할 노드를 정한 후 사용할 자료구조 초기화하기
- 원본 그래프를 인접 리스트로 그래프 표현하기
- 방문 리스트 초기화하기
- 시작 노드를 스택에 삽입(재귀 함수로 표현하면 자동 해결)하기
- DFS 함수 제작
- 인수로 현재 node값을 가지는 DFS 함수를 제작
- 함수는 현재 node에서 갈 수 있는 node들에 대해, 방문하지 않았다면 방문 처리 + DFS 함수 호출을 진행
- 문제 상황에 맞게 함수 내에 기능을 추가
- 시작 노드에서 생성된 DFS 함수를 호출
3. 이분 그래프

그래프의 정점의 집합을 둘로 분할하여, 각 집합에 속한 정점끼리는 서로 인접하지 않도록 분할할 수 있는 그래프
예시)
1 -> 2-> 3은 1번 노드 1집합, 2번노드 2집합, 3번 노드 1집합을 하면 되므로 가능.
1 -> 2-> 3->1은 1번 노드 1집합, 2번노드 2집합, 3번 노드 1집합을 하면 1번과 3번이 이웃함에도 1번 집합이라 불가능
4. 문제
https://www.acmicpc.net/problem/1707
1707번: 이분 그래프
입력은 여러 개의 테스트 케이스로 구성되어 있는데, 첫째 줄에 테스트 케이스의 개수 K가 주어진다. 각 테스트 케이스의 첫째 줄에는 그래프의 정점의 개수 V와 간선의 개수 E가 빈 칸을 사이에
www.acmicpc.net
Basic Idea:
방문 여부를 체크하는 방문 리스트를 각 집합의 이름으로 설정하여 함수를 구성
구현 방법
1. 이웃한 노드들을 집합으로 구분(방문하여 어떤 집합에 속하는지 처리)해야 하므로 탐색 알고리즘 사용
입력 값:
- 2 ≤ K ≤ 5
- 1 ≤ V ≤ 20,000
- 1 ≤ E ≤ 200,000
시간 복잡도: 탐색 알고리즘 DFS의 경우 O(V + E)이므로 테스트 케이스 개수까지 고려할 시 약 10^6 연산이 필요하므로 구현 가능
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**6) # 함수 내 함수 호출을 10^6까지 일시적으로 허용
T = int(input())
n_list = [] # 노드 인접 리스트
v_list = [] # 몇번 그룹을 방문 했는지 리스트
is_possible = True
def dfs(cur_node, group_num): # 이분 그래프라 그룹이 2개인지 확인해야 하므로 인수를 추가
global is_possible
for new_node in n_list[cur_node]:
if not is_possible:
break
if v_list[new_node] == 0: # 방문하지 않았다면
if group_num == 1: # 현재 그룹이 1번이라면
v_list[new_node] = 2 #
else:
v_list[new_node] = 1
dfs(new_node, v_list[new_node])
elif v_list[new_node] == 1:
if group_num == 1:
is_possible = False
break
else:
if group_num == 2:
is_possible = False
break
for t_cnt in range(T):
V, E = map(int, input().split())
n_list = [[] for i in range(V + 1)]
v_list = [0] * (V + 1)
is_possible = True
for i in range(E):
node1, node2 = map(int, input().split())
n_list[node1].append(node2)
n_list[node2].append(node1)
for i in range(1, V+1):
if v_list[i] == 0:
dfs(i, 1)
if not is_possible:
is_possible = True
dfs(i, 2)
if not is_possible:
break
if is_possible:
print('YES')
else:
print('NO')
'컴퓨터 공학 분야 별 지식 > 개념(파이썬)' 카테고리의 다른 글
K번째 최단 거리 찾기 파이썬, 백준 1854 파이썬 (1) | 2024.03.11 |
---|---|
최단거리 찾기 1 - 다익스트라 알고리즘 파이썬, 백준 1753번 파이썬 (0) | 2024.03.10 |
유니온 파인드, 최소 신장 트리, 백준 1197번 파이썬 (1) | 2023.10.29 |
우선순위 큐(Priority Queue)와 그리디 알고리즘(Greedy) 파이썬, 백준 1715 파이썬 (1) | 2023.10.28 |
큐(queue), BFS(너비 우선 탐색), 트리의 지름, 백준 1167 파이썬 (1) | 2023.10.26 |