diff --git a/pages/다익스트라 알고리즘 (Dijkstra Algorithm).md b/pages/다익스트라 알고리즘 (Dijkstra Algorithm).md new file mode 100644 index 0000000..0d5afdc --- /dev/null +++ b/pages/다익스트라 알고리즘 (Dijkstra Algorithm).md @@ -0,0 +1,94 @@ +deck:: Logseq/coding tip + +- ## 1. 개념(Concept) + - 그래프의 특정 시작 정점에서 다른 모든 정점까지의 최단 경로를 구하는 알고리즘. + - **특징** + - {{cloze 음의 가중치}}가 있는 간선이 있으면 사용할 수 없음. + extra:: 음의 가중치가 있을 경우에는 [[벨만-포드 알고리즘 (Bellman-Ford Algorithm)]]을 사용 + id:: 695394f8-cd61-4d4b-9fbc-1cba54ba0195 + - 그리디(greedy)방식을 사용하며, 매 단계에서 {{cloze 현재 갈 수 있는 가장 가까운 정점}}을 확정한다. + id:: 695395cf-75da-45ea-a7d4-259f5d3d942b + - +- ## 2. 동작 원리 (Algorithm Flow) + - 1) 초기화 : 시작 정점의 거리는 0, 나머지 모든 정점의 거리는 무한대로 설정한다. + id:: 6953966b-74ec-4cc0-af49-ab5754b3bb80 + #+BEGIN_EXTRA + ```python + INF = sys.maxsize # float("inf") + dist = [INF] * (v + 1) + dist[start_node] = 0 + ``` + #+END_EXTRA + - 2) 우선순위 큐를 생성하고 그곳에 {{cloze (거리: 0, 노드: 시작정점)}} 을 넣는다. #card + id:: 6953968c-552b-41a5-af23-628736952cac + #+BEGIN_EXTRA + ```python + pq = [(0, start_node)] + ``` + #+END_EXTRA + - 3) 큐가 빌 때까지 아래의 과정을 반복한다. + id:: 695396a3-2c9b-4bf5-a292-0cb14b2395fe + (초기화 및 초기 생성을 마친 뒤 반복문 부분의 코드를 답변할 것) #card + - ```python + while pq : + # 가장 최단 거리가 짦은 노드 꺼내기 + curr_dist, curr_node = heapq.heappop(pq) + + # 이미 처리된 적 있는 노드라면 무시 (더 짦은 경로가 이미 발견됨) + if dist[curr_node] < curr_dist : + continue + + # 인접 노드 확인 + for next_weight, next_node in graph[curr_node] : + # 현재 노드를 거쳐서 가는 거리 + new_dist = next_weight + curr_dist + + # 현재 노드를 거쳐서 가는 거리가 기존 거리보다 짦다면 갱신 + if new_dist < dist[next_node] : + dist[next_node] = new_dist + heapq.heappush(pq, (new_dist, next_node)) + ``` + - 4) 종료: 모든 정점에 대한 최단거리가 확정된다. + - +- ## 3. 코드 (python) + - ```python + import heapq + import sys + + # INF: 무한대 값 설정 (초기화용) + INF = sys.maxsize + + def dijkstra(start_node, v, graph): + # 1. 최단 거리 테이블 초기화 (모두 무한대) + dist = [INF] * (v + 1) + + # 2. 시작 정점 설정 + dist[start_node] = 0 + # (거리, 정점) 순서로 큐에 삽입 (거리가 작은 순서대로 꺼내기 위함) + pq = [(0, start_node)] + + while pq: + # 3. 가장 최단 거리가 짧은 노드 꺼내기 + curr_dist, curr_node = heapq.heappop(pq) + + # 4. 이미 처리된 적 있는 노드라면 무시 (더 짧은 경로가 이미 발견됨) + if dist[curr_node] < curr_dist: + continue + + # 5. 인접 노드 확인 + for next_weight, next_node in graph[curr_node]: + # 현재 노드를 거쳐서 가는 거리가 더 짧은 경우 + new_dist = curr_dist + next_weight + + if new_dist < dist[next_node]: + dist[next_node] = new_dist + heapq.heappush(pq, (new_dist, next_node)) + + return dist + + ``` +- +- ## 4. 시간복잡도 + - {{cloze $O(E\log V)$}} + extra:: E : 간선 갯수, V : 정점 갯수 + id:: 6953bf9f-bc68-4829-a2ba-a884cd15a9e8 \ No newline at end of file diff --git a/pages/벨만-포드 알고리즘 (Bellman-Ford Algorithm).md b/pages/벨만-포드 알고리즘 (Bellman-Ford Algorithm).md new file mode 100644 index 0000000..70deb6b --- /dev/null +++ b/pages/벨만-포드 알고리즘 (Bellman-Ford Algorithm).md @@ -0,0 +1,86 @@ +deck:: Logseq/coding tip + +- ## 1. 개념(Concept) + - 그래프의 특정 시작 정점에서 다른 모든 정점까지의 최단 경로를 구하는 알고리즘. + - [[다익스트라 알고리즘 (Dijkstra Algorithm)]] 와의 차이점 + - {{cloze 음의 가중치}}가 있는 간선을 처리 할 수 있다. + id:: 6953c01a-2e75-4d9c-a2df-9392f4270f37 + - {{cloze 음수 사이클}}의 존재 여부를 탐지 할 수 있다. + extra:: 음수 사이클(Negative Cycle)이란, 간선 가중치의 합이 음수가 되는 순환경로를 의미하고 이 사이클을 계속 순환하면 총 거리가 음의 무한대로 발산하기 때문에 최단 거리를 정의 할 수 없게 된다. + id:: 6953c026-75f3-4fb4-babe-71ef726abdd9 +- +- ## 2. 동작 원리 (Algorithm Flow) + - ### 핵심 아이디어 :-> 모든 간선을 $V-1$번 반복해서 확인한다. + extra:: 최단 경로는 최대 V-1개의 간선을 가질 수 있기 때문. + id:: 6953c0e9-4e24-4508-81c3-9f404f9d423b + - 1) 초기화 : 시작 정점의 거리는 0, 나머지 모든 정점의 거리는 무한대로 설정한다. + #+BEGIN_EXTRA + ```python + INF = sys.maxsize # float("inf") + dist = [INF] * (v + 1) + dist[start_node] = 0 + ``` + #+END_EXTRA + - 2) V-1회 반복 + - 그래프의 모든 간선을 하나씩 확인한다.(시작점(u), 도착점(v), 가중치(w)) + - 확인하면서 특정 조건이 되면 갱신한다. + id:: 6953c838-515c-4773-a0f0-2df4a6746ccc + (이때의 갱신 조건과 파이썬 코드를 답하면?) #card + - 이때 dist[u]의 값이 무한대가 아니고 dist[v]의 값이 dist[u]+w 보다 크다면 dist[v]를 갱신한다. + - ```python + for i in range(V): # 원래 V-1번만 반복해야하지만 음수 사이클 판별을 위해 보통 V까지 함. + for u, v, w in edges: + if dist[u] != INF and dist[u] + w < dist[v]: + dist[v] = dist[u] + w + ``` + - 3) 음수 사이클 판별 + - V-1회 반복한 뒤 한번 더(V번째) 확인한다. + id:: 6953c8e8-c7e6-4083-852e-6838e99963cd + 이때에도 갱신되는 값이 있다면 음수 사이클이 존재(가중치 합이 무한히 작아질 수 있다는 뜻)함을 의미한다. + 파이썬 코드로 작성하면 다음과 같다. #card + - ```python + for i in range(V) : + for u, v, w in edges: + if dist[u] != INF and dist[u] + w < dist[v]: + dist[v] = dist[u] + w + + # V번째 반복인데도 갱신이 일어났다면 음수 사이클이 존재한다는 것! + if i == V - 1 : + return True + + return False + ``` +- +- ## 3. 코드 (python) + - ```python + import sys + + INF = sys.maxsize + + def bellman_ford(start, V, edges) : + dist = [INF] * (V+1) + dist[start] = 0 + + for i in range(V) : + for u, v, w in edges : + if dist[u] != INF and dist[u] + w < dist[v] : + dist[v] = dist[u] + w + + if i == V - 1 : + return True + + return False + ``` +- +- ## 4. 시간복잡도 + - {{cloze $O(VE)$}} + extra:: E : 간선 갯수, V : 정점 갯수 + id:: 6953c0cd-c4e5-4773-b010-d6e85614e4b1 +- +- ## 5. 알고리즘 응용(슈퍼소스) + - 만약 특정 시작 지점에서 도달 가능한 지점 까지의 최단거리를 구하는게 목적이 아닌 음의 사이클 존재 여부만을 확인하고 싶다면? #card + id:: 6953cbbd-2d8c-4351-bfc8-98f79fc347a1 + - dist 배열을 처음부터 전부 0으로 초기화 한 뒤 벨만포드 알고리즘을 돌린다. 이때 V번째 반복에서 갱신이 일어나면 사이클이 발생하는 것이다. + - 슈퍼소스란 ? 가상의 시작점으로서 모든 노드와 연결되어 있으면서 그 가중치가 0인 시작점을 의미한다. + - 즉 임의로 간선들의 배열에 가상의 점을 추가하고 그와 연결되는 가중치 0의 간선들을 추가해서 벨만포드 알고리즘을 돌려도 되고, 위에서 말한 것 같이, 처음부터 모든 거리 배열을 0으로 만들어서 돌려도 된다. + - 모든 거리 배열을 0으로 만든다는 것은 임의의 시작점에서 이미 모든 노드로 가중치가 0인 상태로 연결되어서 한번 갱신 되었다고 볼 수 있기 때문. \ No newline at end of file diff --git a/pages/알고리즘.md b/pages/알고리즘.md index 306faa5..9d274eb 100644 --- a/pages/알고리즘.md +++ b/pages/알고리즘.md @@ -1,4 +1,5 @@ - [[위상 정렬(Topological Sort)]] - [[에라토스테네스의 체]] - [[크루스칼 알고리즘(Kruskal’s Algorithm)]] -- [[프림 알고리즘 (Prim’s Algorithm)]] \ No newline at end of file +- [[프림 알고리즘 (Prim’s Algorithm)]] +- [[다익스트라 알고리즘 (Dijkstra Algorithm)]] \ No newline at end of file diff --git a/pages/위상 정렬(Topological Sort).md b/pages/위상 정렬(Topological Sort).md index aefdd5d..9d9e25e 100644 --- a/pages/위상 정렬(Topological Sort).md +++ b/pages/위상 정렬(Topological Sort).md @@ -12,8 +12,8 @@ deck:: Logseq/coding tip id:: 694a369e-6bc2-4e36-a9d3-cca9f3c2dc42 - 모든 원소를 방문하기 전에 큐가 빈다면 {{cloze 사이클}} 이 존재하는 것입니다. id:: 694a36e1-53a9-4afa-be3d-a76239551547 -- ## 2. 기본 구조 (Basic Structure) - - ### 1) 동작원리 +- ## 2. 동작 원리 (Algorithm Flow) + - ### 1) 순서 - 위상 정렬은 주로 진입 차수(Indegree) 와 큐(Queue) 를 이용하여 동작함. - **1단계 : 진입차수 계산** - 그래프의 모든 노드에 대해, {{cloze **자신을 향하는 간선의 개수( indegree )**}} 를 미리 계산해 둡니다. diff --git a/pages/크루스칼 알고리즘(Kruskal’s Algorithm).md b/pages/크루스칼 알고리즘(Kruskal’s Algorithm).md index 9c56304..5c06698 100644 --- a/pages/크루스칼 알고리즘(Kruskal’s Algorithm).md +++ b/pages/크루스칼 알고리즘(Kruskal’s Algorithm).md @@ -6,7 +6,7 @@ deck:: Logseq/coding tip - 이를 위해 {{cloze 분리집합}}을 사용함. extra:: [[분리집합(Disjoint Set) (Union Find)]] id:: 6950f5ad-52f1-4df3-b495-cbfa7a6ebbec -- ## 2. 기본 구조 (Basic Structure) +- ## 2. 동작 원리 (Algorithm Flow) - 1) 그래프의 모든 간선을 {{cloze 가중치 오름차순}} 으로 정렬한다. id:: 6950f5f7-a85f-49a9-96c1-a06afc1af444 - 2) 가장 가중치가 낮은 간선부터 하나씩 꺼낸다. diff --git a/pages/프림 알고리즘 (Prim’s Algorithm).md b/pages/프림 알고리즘 (Prim’s Algorithm).md index 4d0ecb7..52a090a 100644 --- a/pages/프림 알고리즘 (Prim’s Algorithm).md +++ b/pages/프림 알고리즘 (Prim’s Algorithm).md @@ -5,8 +5,8 @@ deck:: Logseq/coding tip - “임의의 시작 정점에서 출발하여, 현재 트리에 연결된 간선 중 가장 싼 것을 추가하며 확장한다.” 라는 전략을 사용 - 이를 위해 {{cloze 우선순위 큐}}가 사용됨. id:: 69511108-8006-4c0a-9452-d248f8c58ca5 - - 다익스트라(Dijkstra) 알고리즘과 비슷함. -- ## 2. 기본 구조 (Basic Structure) + - [[다익스트라 알고리즘 (Dijkstra Algorithm)]]과 비슷함. +- ## 2. 동작 원리 (Algorithm Flow) - 1) 임의의 정점(vertex)을 하나 선택해서 트리에 포함시킨다. - 2) 현재 트리에 포함된 정점들과 포함되지 않은 정점들을 연결하는 간선 중, 가중치가 가장 작은 간선을 찾는다. - 3) 해당 간선과 연결된 새로운 정점을 트리에 추가한다.