내 생각/강의
[Udemy] JavaScript 알고리즘 & 자료구조 마스터클래스_Section 28
호랑구야
2023. 10. 5. 09:00
* 수업은 JS 기반이지만 Python으로 구현
Section 28
다익스트라 알고리즘 소개
- 세상에서 가장 많이 쓰이는 알고리즘 중 하나
- 그래프의 두 정점 사이에 존재하는 최단 경로를 찾는 것
다익스트라 알고리즘 사용되는 곳
- GPS: 가장 빠른 노선 찾기
- 네트워크 라우팅: 거대한 네트워크에서 데이터에 대한 최단 경로 찾기
- 생물: 사람들 사이에 전염병이 퍼지는 것을 다루는 모델
- 항공권: 도착지까지의 노선 중 가장 저렴한 것 찾기
가중치 그래프 작성
- 간선을 추가할 때, 연결할 두 노드와 그 사이의 무게 값을 입력한다.
- 인접 리스트에 추가할 때, 객체(dictionary)형태로 넣어준다.
- 노드는 node로, 무게값은 weight으로 속성값을 정해준다.
알고리즘 과정
- 새로운 노드를 방문할 때마다 거리 값이 가장 작은 노드를 먼저 방문한다.
- 우리가 어디로 갈 지 정했다면, 그 인접점들을 각각 확인한다.
- 각 인접점마다 시작 노드부터 그 노드까지의 거리를 합을 내어 구한다.
- 만약 이전보다 더 작은 거리 합이 나온다면, 그 노드에 관해 계산된 새로운 최단거리를 저장한다.
우선 순위 큐 소개
- 새로운 요소에 우선순위를 부여하여 추가하는 배열
- enqueue를 이용하면 삽입, 재정렬되고, dequeue를 이용하면 제일 가장 작은 우선순위를 갖는 것이 반환, 삭제된다.
- sorting: O(N * log(N))
다익스트라 알고리즘
- 이 함수는 시작 노드와 종료 노드를 입력해야 한다.
- distances라는 이름의 객체(각 노드에 대해 현재 가장 짧은 거리를 저장하는 표)를 만들고, 각 key 값은 인접 리스트에 있는 모든 노드를 나타내고 value 값으로는 무한을 가진다. 시작 노드는 value 값이 0이다.
- distances라는 이름의 객체의 모든 값 설정이 끝나면, 각 노드를 우선 순위 큐에 저장해준다. value 값으로는 무한을 가진다. 시작 노드는 우리가 시작하는 곳이므로 0으로 둔다.
- previous라는 다른 객체를 만들고, 각 key 값은 인접 리스트에 있는 모든 노드로 하고, value 값은 Null로 둔다.
- 우선 순위 큐에 무언가 있는 한 아래의 루프를 돌린다.
- 우선 순위 큐에 있는 노드를 dequeue한다.
- 만약 dequeue한 노드가 종료 노드와 같다면 루프를 종료한다.
- 만약 아니라면, 인접 리스트에 있는 노드 값에 따라 루프를 돌린다.
- 시작 노드와 해당 노드 사이의 거리를 계산한다.
- 만약 거리 값이 현재 distances 객체에 저장되어 있는 것보다 적으면
- distances 객체에서 해당 노드의 value값을 더 작은 것으로 바꾼다.
- previous 객체에서 어디서 부터 왔는지 반영해 해당 노드의 값을 바꾼다.
- 시작 노드부터 해당 노드까지의 총 거리를 enqueue한다.
우선 순위 큐의 업그레이드
- 우선 순위 큐를 이진 힙을 활용하면 훨씬 빠르게 작동한다.
- 이진 힙은 배열을 다시 계속해서 정렬해서 최저값을 미리 고르는 전통적인 방식보다 훨씬 빠른 방법이다.
- sort에는 key 속성이 있어 그것으로 정렬 기준을 정할 수 있다
* 구현 내용 수정중
다익스트라 구현
class WeightedGraph:
# constructor
def __init__(self):
self.adjacencyList = {}
def addVertex(self, v):
if not v in self.adjacencyList:
self.adjacencyList[v] = []
def addEdge(self, v1, v2, w):
self.adjacencyList[v1].append({'node': v2, 'weight': w})
self.adjacencyList[v2].append({'node': v1, 'weight': w})
def Dijkstra(self, start, finish):
nodes = PriorityQueue()
distances = {}
previous = {}
# to return at end
path = []
# build up initial state
for V in self.adjacencyList:
if V == start:
distances[V] = 0
nodes.enqueue(V, 0)
else:
distances[V] = float('inf')
nodes.enqueue(V, float('inf'))
previous[V] = None
while(len(nodes.values)):
smallest = nodes.dequeue().val
if smallest == finish:
# we are done
# build up path to return at end
while(previous[smallest]):
path.append(smallest)
smallest = previous[smallest]
break
if smallest or distances[smallest] != float('inf'):
for neighbor in self.adjacencyList[smallest]:
# find neighboring node
nextNode = self.adjacencyList[smallest][neighbor]
# print(nextNode)
# calculate new distances to neighboring node
candidate = distances[smallest] + nextNode['weight']
NextNeighbor = nextNode.node
if candidate < distances[NextNeighbor]:
# updating new smallest distance to neighbor
distances[NextNeighbor] = candidate
# updating previous - how we got to neighbor
previous[NextNeighbor] = smallest
# enqueue in priority queue with new priority
nodes.enqueue(NextNeighbor, candidate)
path = path + [smallest]
path.reverse()
return path
graph = WeightedGraph()
graph.addVertex("A")
graph.addVertex("B")
graph.addVertex("C")
graph.addVertex("D")
graph.addVertex("E")
graph.addVertex("F")
graph.addEdge("A","B", 4)
graph.addEdge("A","C", 2)
graph.addEdge("B","E", 3)
graph.addEdge("C","D", 2)
graph.addEdge("C","F", 4)
graph.addEdge("D","E", 3)
graph.addEdge("D","F", 1)
graph.addEdge("E","F", 1)
graph.adjacencyList
graph.Dijkstra("A", "E")
# graph.Dijkstra.previous
# graph.Dijkstra.distances
# ["A", "C", "D", "F", "E"]
반응형