파이썬 그래프 구현 | [자료구조 알고리즘] Graph 검색 Dfs, Bfs 구현 In Java 17688 명이 이 답변을 좋아했습니다

당신은 주제를 찾고 있습니까 “파이썬 그래프 구현 – [자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java“? 다음 카테고리의 웹사이트 you.tfvp.org 에서 귀하의 모든 질문에 답변해 드립니다: https://you.tfvp.org/blog/. 바로 아래에서 답을 찾을 수 있습니다. 작성자 엔지니어대한민국 이(가) 작성한 기사에는 조회수 90,584회 및 좋아요 1,819개 개의 좋아요가 있습니다.

파이썬 그래프 구현 주제에 대한 동영상 보기

여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!

d여기에서 [자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java – 파이썬 그래프 구현 주제에 대한 세부정보를 참조하세요

[선행학습자료]Linked List 개념 https://youtu.be/DzGnME1jIwY
단방향/양방향 Linked List 개념 https://youtu.be/G4IIDyfoHeY
단방향 Linked List 구현 in Java https://youtu.be/C1SDkdPvQPA
Stack 구현하기 in Java https://youtu.be/whVUYv0Leg0
Queue 구현하기 in Java https://youtu.be/W3jNbNGyjMs
Tree의 종류 https://youtu.be/LnxEBW29DOw
Binary Tree의 3가지 순회방법 구현하기 https://youtu.be/QN1rZYX6QaA
그래프(Graph)에 대해서 https://youtu.be/fVcKN42YXXI

파이썬 그래프 구현 주제에 대한 자세한 내용은 여기를 참조하세요.

Python의 그래프 구현 – Techie Delight

이 포스트는 그래프의 인접 목록 표현을 사용하여 파이썬에서 가중치 및 가중치가 없는 방향성 그래프 데이터 구조를 구현합니다. 여기서 그래프의 각 정점은 인접 …

+ 자세한 내용은 여기를 클릭하십시오

Source: www.techiedelight.com

Date Published: 10/16/2021

View: 7058

자료구조] 9. 그래프(Graph) – 파이썬 – gongsam21

무방향 그래프의 경우 전치행렬이 되어도 값이 같다. 인접 리스트 기반 그래프. 인접 행렬이 행렬을 이용한것과는 달리 인접 리스트로 구현한다. 파이썬 …

+ 자세한 내용은 여기를 클릭하십시오

Source: rhdtka21.tistory.com

Date Published: 4/25/2021

View: 8794

2. 그래프(Graph) – 파이썬 사전(하면서 필요한 거 위주로 …

본격적으로 DFS, BFS를 공부하기 전에 그래프 구조가 무엇인지 알아야 한다. 여기서부터는 쉽지 않다… 사실 이론과 그림을 이해하는 것은 크게 어렵지 않으나,, 구현 …

+ 여기에 자세히 보기

Source: wikidocs.net

Date Published: 6/4/2021

View: 3875

그래프_구현하기 – muntari Log

먼저, 그래프의 노드를 구현하는 방법이다. 1. 동적 배열 / 파이썬 리스트 구현 방법. 그래프는 노드라는 기본 데이터 단위를 가지며 리스트의 요소로 …

+ 여기에 자세히 보기

Source: codermun-log.tistory.com

Date Published: 6/28/2022

View: 6086

[Python]자료구조 6.그래프 – velog

1-2) 구현. Graph: def __init__(self, vertex_num = …

+ 여기에 표시

Source: velog.io

Date Published: 1/19/2021

View: 1185

[파이썬 알고리즘] 그래프 구현, DFS, BFS, 인접 행렬, 최소 신장 …

[파이썬 알고리즘] 그래프 구현, DFS, BFS, 인접 행렬, 최소 신장 트리 … 그래프는 정점을 연결하는 간선의 방향성 여부에 따라.

+ 여기에 보기

Source: blog.naver.com

Date Published: 9/29/2021

View: 8345

[Python] 그래프(Graph) – 별의 블로그

트리도 그래프의 일종이지만, 트리와 그래프를 구현하는 코드 등이 … 그래프는 정점을 연결하는 간선의 방향성 여부에 따라 방향 그래프와 무방향 …

+ 여기에 보기

Source: starrykss.tistory.com

Date Published: 7/25/2022

View: 5534

파이썬에서 그래프(Graph) 구현하기 – 준화의 개발일기

그래프를 파이썬에서 구현해보는 포스팅을 하겠습니다. 그래프(Graph) 자료구조란? Graph는 정점(Vertex, 혹은 Node) …

+ 여기에 표시

Source: develop-story.tistory.com

Date Published: 8/25/2021

View: 6839

[파이썬(python) 자료구조] 그래프 (Graph) – 유진’s 공부로그

그래프 : 연결되어 있는 객체 간의 관계를 표현하는 비선형 자료구조 가장 … 파이썬은 포인터가 따로 존재하지 않으므로, 연결리스트를 구현하기 …

+ 자세한 내용은 여기를 클릭하십시오

Source: daebaq27.tistory.com

Date Published: 7/20/2022

View: 4252

[Python & Data Structure] Graph – 이것저것 공부방

하나의 리스트는 자신과 인접한 다른 정점을 담고 있다. 가중치 그래프의 인접리스트 표현. 간단한 구조의 그래프는 인접행렬로 구현이 가능 …

+ 여기에 자세히 보기

Source: duckracoon.tistory.com

Date Published: 6/4/2021

View: 375

주제와 관련된 이미지 파이썬 그래프 구현

주제와 관련된 더 많은 사진을 참조하십시오 [자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java. 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.

[자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java
[자료구조 알고리즘] Graph 검색 DFS, BFS 구현 in Java

주제에 대한 기사 평가 파이썬 그래프 구현

  • Author: 엔지니어대한민국
  • Views: 조회수 90,584회
  • Likes: 좋아요 1,819개
  • Date Published: 2017. 12. 28.
  • Video Url link: https://www.youtube.com/watch?v=_hxFgg7TLZQ

Python의 그래프 구현

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

# 그래프 객체를 나타내는 클래스 class Graph : # 그래프를 구성하는 생성자 def __init__ ( self , edges , n ) : # 인접 목록을 나타내는 목록 목록 self . adjList = [ None ] * n # 인접 목록에 대한 메모리 할당 for i in range ( n ) : self . adjList [ i ] = [ ] #는 방향 그래프에 간선을 추가합니다. for ( src , dest , weight ) in edges : #는 인접 목록의 노드를 src에서 dest로 할당합니다. self . adjList [ src ] . append ( ( dest , weight ) ) # 그래프의 인접 목록 표현을 인쇄하는 기능 def printGraph ( graph ) : for src in range ( len ( graph . adjList ) ) : # 현재 정점과 모든 인접 정점을 인쇄합니다. for ( dest , weight ) in graph . adjList [ src ] : print ( f ‘({src} —> {dest}, {weight}) ‘ , end = ” ) print ( ) if __name__ == ‘__main__’ : # 입력: 가중 이중 그래프의 에지(위 다이어그램에 따름) # Edge (x, y, w)는 가중치가 `w`인 `x`에서 `y`까지의 에지를 나타냅니다. edges = [ ( 0 , 1 , 6 ) , ( 1 , 2 , 7 ) , ( 2 , 0 , 5 ) , ( 2 , 1 , 4 ) , ( 3 , 2 , 10 ) , ( 4 , 5 , 1 ) , ( 5 , 4 , 3 ) ] # 꼭짓점 수(0에서 5까지 레이블 지정) n = 6 #는 주어진 간선 목록에서 그래프를 구성합니다. graph = Graph ( edges , n ) # 그래프의 인접 목록 인쇄 printGraph ( graph )

[파이썬 | 자료구조] 9. 그래프(Graph)

9. 그래프(Graph)

9.1 그래프의 개념

그래프란 정점과 간선들로 이루어진 집합으로 표현되는 자료구조

트리도 일종의 그래프라고 할 수 있다.

9.2 그래프의 종류

무방향 그래프 : 간선이 방향을 가지지 않음

방향 그래프 : 간선이 방향을 가지고 있음

가중치 그래프 : 각 간선에 가중치 정보가 포함됨. 가중치는 거리, 비용 등으로 표현 할 수 있다.

9.3 그래프의 구현

인접 행렬 기반 그래프 각 정점간의 가중치나 간선의 유무를 행렬로 표현한다. 무방향 그래프의 경우 전치행렬이 되어도 값이 같다.

인접 리스트 기반 그래프 인접 행렬이 행렬을 이용한것과는 달리 인접 리스트로 구현한다.

파이썬에서는 그냥 딕셔너리 자료형에 리스트를 넣어 쉽게 인접 리스트처럼 구현하여 사용할 수 있다.

BFS

BFS는 너비 우선 탐색으로, 현재 Node(Vertex)에서 연결된 Node로 우선적으로 탐색하는 것을 뜻한다.

즉 아래 그림에서 A에서 BFS를 시작한다고 하면, B, E, I를 우선적으로 탐색하고, 그 후 B, E, I에 연결된 Node를 탐색한다.

즉 방문하는 순서는 [‘A’, ‘B’, ‘E’, ‘I’, ‘C’, ‘F’, ‘H’, ‘J’, ‘D’, ‘G’] 순이 된다.

queue를 이용해서 구현할 수 있다.

DFS

BFS는 깊이 우선 탐색으로, 현재 Node(Vertex)에서 연결된 Node중에서 하나를 골라 더이상 진행할 수 없을때까지 탐색한다. 그 후 더이상 진행이 불가능하면, 진행이 가능한 Node 까지 되돌아 와서 탐색을 한다.

즉 아래 그림에서 A에서 BFS를 시작한다고 하면, B를 우선적으로 탐색하고, 그 후 B에 연결된 Node를 탐색한다.

즉 방문하는 순서는 [‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘I’, ‘J’] 순이 된다.

stack을 이용해서 구현할 수 있고, 함수의 재귀호출을 이용해서 구현할 수도 있다.

2. 그래프(Graph)

본격적으로 DFS, BFS를 공부하기 전에 그래프 구조가 무엇인지 알아야 한다. 여기서부터는 쉽지 않다… 사실 이론과 그림을 이해하는 것은 크게 어렵지 않으나,, 구현이 매우 어려운거 같다…

백트랙킹 문제에서 며칠째 정체하고 있다…

그래프 구조

vertex (정점) (node)과 edge (간선) 으로 이루어진 자료구조다. (비선형 구조)

degree (차수)

: node에 연결된 edge의 수. ex) A의 차수 1 B의 차수 2

경로

Node 표현: A – B – C

Edge 표현: (A, B) (B, C)

– 단순 경로 : node를 최대 한 번만 지나는 경로

– Cycle : 시작과 끝의 node가 같은 경로

– DAG(Directed Acycle Graph)

▶Undirected Graph

▶Directed Graph 방향 o

In-degree (진입차수) : 방향이 향하는 edge 수 (ex) B의 진입차수 1)

Out-degree (진출차수) : 방향이 나가는 edge 수 (ex) A의 진출차수 1 진입차수 0)

▶Weighted Graph 방향 o 가중치 o

Adjacency (인접)

두 개의 정점(node)이 연결된 상태를 인접하다고 한다. (Directed graph에서는 방향이 향하는 쪽 node가 인접한 정점이다.) – 완전 그래프: 모두 Adjacency한 그래프

Edge의 수 :

Undirected ${n(n-1)} \over 2$

directed $n(n-1)$ – 부분 그래프

그래프 구조 구현

1. 인접 ✳ 행렬(adjacency matrix)

adjacency matrix: 인접한 정점들의 상태를 행렬(2차원 배열)로 구성.

인접하면 1이다.

같은 노드끼리는 0이다.

연결되지 않은 노드끼리는 0 또는 무한대 inf로 표현한다.

adjac_matrix = [[0, 1, 1, 0, 0, 0, 0], [1, 0, 0, 1, 0, 0, 0], [1, 0, 0, 0, 1, 1, 0], [0, 1, 0, 0, 0, 0, 1], [0, 0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0]]

Directed graph의 경우 진입차수(방향을 맞은?방향)의 차수를 입력한다.

Undirected의 인접행렬은 대칭행렬(symmetric matrix)다.

😥Node가 많아지면 메모리가 $n^2$ 배로 필요해진다.

2. 인접 ✳리스트 vertex & edge list

유튜브 참고 했습니다. (설명을 매우 잘해주시더라구요!)

# vertext list vert_lst = [‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’] # edge list edge_lst = [(0, 1), (1, 0), (1, 3), (3, 1), (3, 6), (6, 3), (0, 2), (2, 0), (2, 4), (4, 2), (2, 5), (5, 2)] # adjacency list adjac_lst = [[] for vert in vert_lst] for edge in edge_lst: adjac_lst[edge[0]].append(edge[1]) print(adjac_lst) # [[1, 2], [0, 3], [0, 4, 5], [1, 6], [2], [2], [3]]

결과로 정점 0 부터 6까지 인접한 정점들의 집합이 나온다.

이 인접한 정점들의 집합과 스택 쿠조를 이용하여 visited(flag) list를 만들어 그래프를 모두 탐색한다.

def stack_graph(adjac_lst): stack = [0] visit_vert = [] while stack: current = stack.pop() for neighbor in adjac_lst[current]: if neighbor not in visit_vert: stack.append(neighbor) visit_vert.append(current) return visit_vert

구현하기 :: muntari Log

728×90

그래프 구현하기

그래프를 구현하는 방법은 크게 2가지로 나누어 알아보자.

1. 그래프의 노드를 구현하는 방법

– 배열 또는 동적 배열 => 파이썬의 리스트로 구현이 가능하다.

– 해시 테이블 => 파이썬의 딕셔너리로 구현이 가능하다.

2. 그래프의 에지를 구현하는 방법

– 인접 행렬

– 인접 리스트

먼저, 그래프의 노드를 구현하는 방법이다.

1. 동적 배열 / 파이썬 리스트 구현 방법

그래프는 노드라는 기본 데이터 단위를 가지며 리스트의 요소로 단순 값이 아닌 노드 자체를 요소로 하는 리스트를 만드는 방법이다.

이때 노드는 특정 인덱스 값을 부여받는데, 인덱스를 이용해 리스트의 O(1)으로 효율적인 접근이 가능하며, 노드 안의 인스턴스에 대한 접근도 그만큼 효율적으로 할 수 있게 되는 것이다.

class StationNode: “””간단한 지하철 역 노드 클래스””” def __init__(self, station_name, location): self.station_name = station_name self.location = location A = StationNode(“당고개역”, “서울”) B = StationNode(“오이도역”, “경기”) C = StationNode(“부산역”, “부산”) list_1 = [“당고개역”, “오이도역”, “부산역”] list_2 = [A, B, C] print(list_1) print(list_2[1].station_name) # 오이도역 print(list_2[1].location) # 경기

2. 헤시테이블 / 파이썬 딕셔너리 구현 방법

위 방법과 비슷하게 역 이름 자체를 Key 값으로 사용하고 역 노드를 Value 값으로 사용하는 것이다.

이때 주의할점은 해시 테이블에서 동일한 key값은 존재할 수 없다는 점이며, 중복이 이루어지지 않는 고윳값(이메일, 핸드폰 번호 등등)을 Key 값으로 사용해야 한다.

역 이름의 경우 중복되는 역 이름은 존재하지 않으므로 역 이름을 Key 값으로 사용해도 무방하다.

해시 테이블도 Key 값만 있다면 Value는 평균적으로 O(1)으로 가져올 수 있기에 효율적으로 사용이 가능하다.

class StationNode: “””간단한 지하철 역 노드 클래스””” def __init__(self, station_name, location): self.station_name = station_name self.location = location A = StationNode(“당고개역”, “서울”) B = StationNode(“오이도역”, “경기”) C = StationNode(“부산역”, “부산”) my_dict = {} my_dict[“당고개역”] = A my_dict[“오이도역”] = B my_dict[“부산역”] = C print(my_dict) # {‘당고개역’: <__main__.StationNode object at 0x7fdf45868700>, ‘오이도역’: <__main__.StationNode object at 0x7fdf458d4490>, ‘부산역’: <__main__.StationNode object at 0x7fdf458d4520>} print(my_dict[“당고개역”].station_name) # 당고개역 print(my_dict[“당고개역”].location) # 서울

그래프의 엣지를 구현하는 방법

1. 인접 행렬

인접은 두 노드가 서로 연결되어 있다는 의미이고,

행렬은 2차원 배열 또는 2차원 리스트라는 의미이다, 파이썬에서의 리스트 안에 리스트가 들어가 있는 matrix형태를 떠올리면 되겠다.

즉, 요소의 갯수에 따라 행렬을 만들고 이 행렬에 각 노드들끼리 어떻게 연결되었는지를 표시하는 방법이다.

인접 행렬의 목표는 어떤 노드가 어떤 노드와 연결되어 있는지? 를 확인하기 위함이라 할 수 있겠다.

인접 행렬을 아래와 같이 정리할 수 있다.

– 각 노드를 리스트에 저장해 노드는 고유 정수 인덱스 값을 갖는다.

– 노드 수 X 노드 수 크기의 행렬을 만든다.

– 노드들의 엣지 유무 및 가중치에 따라 행렬의 요소를 채운다.

인접 행렬은 무방향 그래프, 방향 그래프, 가중치 그래프일 때 각각 다르기 때문에 그래프 별로 알아보자.

– 무방향 그래프

아래의 예시로 엣지를 구현해보도록 하자.

list_1 = [“영훈”, “동욱”, “현승”, “지웅”, “소원”]

각 노드 간의 연결 관계를 저장하기 위해 노드의 개수만큼의 행렬을 만들어 준다. (5 x 5)

각 노드는 인덱스라는 고유 값을 갖게 되므로 이를 이용해 서로 간의 연결 관계를 정의할 수 있는데

인접 행렬에서는 모든 노드는 스스로와 연결이 되지 않은 상태로 나타낸다. (0)

영훈의 경우 0번째 인덱스로 동욱, 현승과 인접 관계에 있다.

이를 행렬로 나타내면 아래와 같다.

무방향 그래프에서의 영훈 – 동훈 에지는 동훈 – 영훈 에지와 같기 때문에

0행 1열(영훈, 동훈) = 1 / 1행 0열(동훈, 영훈) = 1

0행 2열(영훈, 현승) = 1 / 2행 0열(현승, 영훈) = 1

모든 노드에 위와 같이 관계를 정의해주면 아래와 같은 행렬이 되며,

모든 노드는 스스로와 연결이 되지 않은 상태이기에 대각선을 기준으로 대칭 구조가 된다.

– 방향 그래프

위 예시에 방향성만 추가한다.

방향 그래프에서는 엣지가 방향성을 갖기 때문에 영훈- 현승 에지와 현승 -영훈 에지가 다르다.

0행 1열 (영훈, 현승) = 0 / 1행 0열 (현승, 영훈) = 1

0행 2열 (영훈, 동욱) = 0 / 2행 0열 (동욱, 영훈) = 1

모든 노드에 위와 같이 관계를 정의해주면 아래와 같은 행렬이 되며,

무방향에서와 달리 대칭 구조를 이루지 않는다.

– 가중치 그래프

엣지가 추가 정보를 갖는 가중치 그래프

여기서는 연결 상태 표시를 가중치로 해주면 된다.

위에서 연결 상태 = 1로 표기하였다면 연결 상태 = 가중치로 표기해주면 되는 것이다.

당연히 기본 단위인 노드의 인스턴스는 위 예시와 다르다.

인접 행렬의 목표는 어떤 노드가 어떤 노드와 연결되어 있는지?를 알기 위함이었다.

특정 노드 사이에 서로 인접해 있는지 여부를 확인하는 방법을 알아보자.

영훈과 동욱이 서로 인접해 있는지 확인하기 위해서는

영훈의 인덱스 값 0과 동욱의 인덱스 값 1을 이용해 행렬의 데이터를 찾으면 된다.

즉, 0행 1열의 값 또는 1행 0열의 값을 찾아 서로 인접해 있는지 아닌지 여부를 확인할 수 있다.

matrix[0][1] or matrix[1][0]

특정 노드와 인접해 있는 모든 노드를 확인하고 싶을 때는 특정 노드의 인덱스 값을 이용해 해당 노드의 행 전체를 가져오면 된다.

영훈과 인접해 있는 모든 노드를 확인하고 싶을 때는 아래와 같이 확인할 수 있으며

결과로 동욱, 현승과 인접 관계라는 점을 확인 할 수 있게 된다.

matrix[0]

2. 인접 리스트

인접 행렬의 경우 노드들의 인접 데이터를 행렬에 저장했다면,

인접 리스트의 경우, 말그대로 노드들의 인접 데이터를 리스트에 저장하는 방법이다.

(리스트는 순서 데이터를 저장하는 자료구조이나, 인접 리스트에서는 노드들의 순서는 큰 의미가 없다. 모두 동등한 관계이자 위치에 있기 때문이다.)

인접 리스트는 각 노드마다 스스로 인접한 노드들에 대한 레퍼런스를 리스트로 저장하여 에지를 구현하는 방법이다.

인접 행렬을 아래와 같이 정리할 수 있다.

– 각 노드의 엣지를 리스트에 저장하는 방법.

아래 지하철 역 예시를 통해 더 자세히 알아보자.

강남역의 경우

교대, 역삼, 양재와 인접해있으며, 이 인접 관계를 배열 or 파이썬의 리스트에 저장하는 방법으로

특정 노드에 어떤 노드들이 인접해 있는지 알 수 있게 된다.

인접 행렬에서의 노드 인스턴스에 빈 배열의 인스턴스를 추가하고

이 빈 배열에 연결된 노드들에 대한 레퍼런스를 저장한다!

(이는, 링크드 리스트의 노드에서 다음 노드에 대한 레퍼런스를 저장하는 것과 트리에서 노드가 자식 노드에 대한 레퍼런스를 저장하는 것과 동일하다!!)

class StationNode: “””간단한 지하철 역 노드 클래스””” def __init__(self, station_name, location): self.station_name = station_name self.location = location self.adjacent_stations = []

따라서 그래프의 강남 노드는 아래와 같은 인접 리스트를 갖는다!

인접 리스트를 사용하는 방법

그래프에 따라 인접 리스트를 사용하는 방법에 차이가 있다.

1. 무방향 그래프

A와 B가 인접 관계라면 A의 인접 리스트에 B를 넣고 + B의 인접 리스트에 A를 넣는다.

에지의 방향성이 없기에 A – B 에지와 B – A 에지는 같기 때문이다.

2. 방향 그래프

A와 B가 인접 관계라면 A의 인접 리스트에 B를 넣고, B의 인접 리스트에 A를 넣지 않는다.

엣지의 방향성이 있어, A – B 에지와 B – A 에지는 같지 않기 때문이다.

3. 가중치 그래프

가중치 그래프의 경우 튜플을 사용하여 인접 노드와 가중치를 함께 저장해주면 된다.

만약, 가중치 그래프에서 에지의 방향성이 있다면 위와 마찬가지로 B의 인접 리스트에 A를 넣지 않으면 된다.

그래프는 각 언어별로 아래와 같은 방법으로 구현한다고 한다.

C++ => Vector

JAVA => Arraylist

Python => List

구현의 경우 여태껏 구현하였던 자료구조 중 제일 복잡하였덧 것 같아 코드를 참고하여 공부한다면 더 잘 이해가 될 것 같아 공유한다!

Python_Javascript_CTEST/DataStructure/used_dict_adjacency_list at main · Muntari29/Python_Javascript_CTEST (github.com)

출처 : 코드 잇(자료구조)

자료 구조 | 코드 잇 (codeit.kr)

728×90

[Python]자료구조 6.그래프

6.1. 용어

그래프는 정점(vertex)의 집합 V(G)와 에지(Edge)의 집합 E(G)로 정의 G = (V, E)

6.1.1. 무방향 그래프

G = (V, E)

V(G) = {0, 1, 2, 3}

E(G) = {(0,1), (0,2), (0,3), (2,3)}

정점의 개수가 n인 경우, 최대 에지의 개수 : n(n-1)/2

에지 : 정점과 정점을 잇는 선

6.1.2. 방향 그래프

G =

V(G) = {0, 1, 2, 3}

E(G) = {<0,1>, <0,2>, <1,2>, <2,3>, <3,2>}

정점의 개수가 n인 경우, 최대 에지의 개수 : n(n-1)

<0,1> : 0이 head, 1이 tail

6.1.3. 자기 간선

정점이 head이자 tail

6.1.4. 멀티그래프

중복 에지 인정

6.1.5. 인접

정점 u, v 사이에 에지 (u, v) 가 있는 경우, u와 v는 인접하다

u ∈ V(G), v ∈ V(G)고 (u, v) ∈ E(G)이면 u와 v는 adjacent

6.1.6. 경로

경로(path) : (v1, v2), (v2, v3), (v3, v4)가 집합 E(G)의 원소일 때 v1에서 v4까지 정점 순서 v1->v2->v3->v4

경로의 길이 = 에지의 개수

6.1.6.1. 단순 경로

처음과 마지막 경로가 다름

6.1.6.2. 사이클

단순 경로에서 처음과 마지막 경로가 같음

v2->v3->v4->v2

6.1.7. 연결된 그래프(connected graph)

어떤 임의의 정점 u와 다른 어떤 임의의 정점 v를 골랐을 때 정점 사이에 경로가 있으면 이를 연결되었다(connected)

G = (V, E)

V(G) = {0, 1, 2, 3, 4, 5, 6, 7}

E(G) = {(0, 3), (0, 4), (1, 2), (1, 5), (2, 5), (3, 4), (4, 6), (5, 7)} 연결요소 : 정점 집합 {0, 3, 4, 6}과 {1, 2, 5, 7}을 각각 연결 요소(connected component)임.

6.1.8. 차수(degree)

1) 무방향 그래프의 차수

어떤 정점 v의 차수 d(v)는 정점 v가 부속된 에지 개수

부속되었다(incident) : 정점 u와 정점 v 사이에 에지 (u, v)가 존재할 때 에지 (u, v)를 정점 u에 부속되었다, 정점 v에 부속되었다

2) 방향 그래프의 차수

방향그래프의 차수 : 진입차수 + 진출차수 = d(v)

진입 차수(in-degree) : 정점 v가 head인 경우 = 정점 v로 들어오는 에지 개수 진입차수 : in-d(v)

진출 차수(out-degree) : tail인 경우 = 정점 v에서 나가는 에지 개수 진출 차수 : out-d(v)

6.1.9. 부분 그래프, 신장 부분 그래프

1) 부분 그래프

그래프 G’가 G에 대해 V(G’) ⊆ (G)고 E(G’) ⊆ E(G)면 그래프 G’는 그래프 G의 부분 그래프

2) 신장 부분 그래프(spanning subgraph)

그래프 G’가 그래프 G에 대해 V’=V고 E(G’) ⊆ E(G)를 만족

ex) G’는 V’=V를 만족하므로 신장 부분 그래프이지만, G”에는 정점 3이 누락되었으므로 V’=V를 만족하지 못해서 부분 그래프이지만 신장 부분 그래프는 아님

6.2. 그래프 표현 방법 : 인접리스트, 인접 행렬

1) 인접 리스트(adjacency list)

배열 1개와 연결리스트들로 구성

배열의 인덱스 = 정점

연결리스트 : 해당 정점에 인접한 정점의 집합

ex) 정점 0에 연결된 정점이 1,3 인 경우 리스트 요소는 1,3 O(d(v))

1) 어떤 정점 v에 대해 인접한 모든 노드를 탐색 : 해당 정점을 인덱스로 삼아 해당되는 연결리스트를 통해 순회하므로, 모든 정점을 조사할 필요 없이 해당 정점의 인접한 정점들만 조사

2) 정점 u에 대해 (u, v) ∈ E(G)인지를 검사 : 해당 정점을 인덱스로 연결 리스트를 가져와 인접한 모든 노드를 순회해야 함.

1-1) ADT

1. Object

정점 집합 V와 정점 집합 V에 속하는 u, v에 대해 (u, v)가 속하는 에지 집합 E로 구성된 튜플 G = (V, E)

2. Operation

G.is_empty() : Boolean G.add_vertex() : 정점 추가하고 정점 인덱스 반환(Integer) G.delete_vertex(v) : 정점 v 삭제 G.add_edge(u, v) : 에지 (u, v) 추가 G.delete_edge(u, v) : 에지 (u, v) 삭제 G.adj(v) : 정점 v에 인접한 정점 집합을 동적 배열로 반환

1-2) 구현

class Graph : def __init__ ( self , vertex_num = None ) : self . adj_list = [ ] self . vtx_num = 0 self . vtx_arr = [ ] if vertex_num : self . vtx_num = vertex_num self . vtx_arr = [ True for _ in range ( self . vtx_num ) ] self . adj_list = [ [ ] for _ in range ( self . vtx_num ) ]

vtx_arr : delete_vertex() 사용 시 도중에 있던 정점이 사라질 수 있기 때문에 이를 방지하기 위한 배열

def is_empty ( self ) : if self . vtx_num == 0 : return True return False def add_vertex ( self ) : for i in range ( len ( self . vtx_arr ) ) : if self . vtx_arr [ i ] == False : self . vtx_num += 1 self . vtx_arr [ i ] = True return i self . adj_list . append ( [ ] ) self . vtx_num += 1 self . vtx_arr . append ( True ) return self . vtx_num – 1

add_vertex() : 모든 정점을 순회하면서 비활성화된 정점이 있다면 사용하고, 모두 사용 중이라면 정점을 추가함.

def delete_vertex ( self , v ) : if v >= self . vtx_num : raise Exception ( f”There is no vertex of { v } ” ) if self . vtx_arr [ v ] : self . adj_list [ v ] = [ ] self . vtx_num -= 1 self . vtx_arr [ v ] = False for adj in self . adj_list : for vertex in adj : if vertex == v : adj . remove ( vertex )

정점 v 삭제 = 비활성화

if self.vtx_arr[v] : self.adj_list[v] = [] :해당 정점이 있다면 인접리스트 초기화

:해당 정점이 있다면 인접리스트 초기화 self.vtx_arr[v] = False : 해당 정점 비활성화, 추후 활성화하여 사용 가능하도록 비활성화

: 해당 정점 비활성화, 추후 활성화하여 사용 가능하도록 비활성화 for adj in self.adj_list: for vertex in adj:

: 이중루프를 돌면서 해당 정점과 이어져 있던 에지들 삭제

def add_edge ( self , u , v ) : self . adj_list [ u ] . append ( v ) self . adj_list [ v ] . append ( u ) def delete_edge ( self , u , v ) : self . adj_list [ u ] . remove ( v ) self . adj_list [ v ] . remove ( u )

인접리스트의 에지 추가 : 인접리스트의 각 정점 마지막 요소에 추가

인접리스트의 에지 삭제 : 해당 정점(동적 배열)의 요소 삭제

def adj ( self , v ) : return self . adj_list [ v ]

해당 정점 v에 인접한 모든 노드 집합을 리스트로 반환

2) 인접 행렬(adjacency matrix)

행 : 정점

열 : 자신을 포함한 다른 정점

ex) 1행 2열 = 정점 1과 정점 2의 관계 : 해당 값이 0인 경우, 인접하지 않다

ex) 1행 2열 = 정점 1과 정점 2의 관계 : 해당 값이 0인 경우, 인접하지 않다 이차원 배열 adj_matrix : 에지 (0,1)이 있다면 adj_matrix[0][1] = 1, 없다면 0

무방향 그래프의 경우, (0,1), (1,0) 모두 존재하므로 행렬에서는 모두 1 : 대각선에 대해 대칭 O(n)

1) 어떤 정점 v에 대해 인접한 모든 노드를 탐색 : v행에 대하여 모든 열 탐색

O(1)

2) (u, v)가 있는지 여부를 확인하는 연산 : adj_matrix[u][v] 유무 확인

5.3. 그래프 순회(traversal) : 그래프의 모든 노드 방문

1) 너비 우선 탐색(Breadth First Search, BFS) : 큐 기반

ex) 출발 정점이 3인 경우, 출발 정점을 지나 인접 정점 모두 탐색, 이후 인접 정점들의 인접 정점 탐색 순으로 탐색함

from queue import Queue class Graph : def __init__ ( self , vertex_num ) : self . adj_list = [ [ ] for _ in range ( vertex_num ) ] self . visited = [ False for _ in range ( vertex_num ) ] def add_edge ( self , u , v ) : self . adj_list [ u ] . append ( v ) self . adj_list [ v ] . append ( u ) def init_visited ( self ) : for i in range ( len ( self . visited ) ) : self . visited [ i ] = False

인접 리스트로 구현

visited 변수 : 정점의 방문 여부 확인 -> 각 정점 방문 시, True 반환, 미방문 시 False 반환

def bfs ( self , v ) : q = Queue ( ) self . init_visited ( ) q . put ( v ) self . visited [ v ] = True while not q . empty ( ) : v = q . get ( ) print ( v , end = ‘=’ ) adj_v = self . adj_list [ v ] for u in adj_v : if not self . visited [ u ] : q . put ( u ) self . visited [ u ] = True

큐 생성 후, 매개변수로 전달된 시작 정점을 삽입하고 큐가 비기 전까지 while문을 통해 모든 정점을 방문함.

q = Queue() : 큐 생성

: 큐 생성 self.init_visited() : 모든 정점에 대해 방문 기록 초기화

: 모든 정점에 대해 방문 기록 초기화 q.put(v) : 정점 v를 q에 넣음

: 정점 v를 q에 넣음 self.visited[v] = True : 해당 정점 방문 완료

: 해당 정점 방문 완료 while not q.empty(): : q가 빌 때까지 while 루프를 돌면서 모든 정점 방문

: q가 빌 때까지 while 루프를 돌면서 모든 정점 방문 v = q.get() : get()을 통해 dequeue로 q의 front값을 pop하면서 그 값을 다음 정점으로 선정

: get()을 통해 dequeue로 q의 front값을 pop하면서 그 값을 다음 정점으로 선정 adj_v = self.adj_list[v] : adj_v는 해당 정점 v에 대한 인접한 모든 정점

: adj_v는 해당 정점 v에 대한 인접한 모든 정점 for문을 순회하며 방문하지 않은 정점을 큐에 넣고, 최초 v의 인접 정점의 방문 여부 True로 변경함.

ex) 0-2-1-3으로 된 그래프에서 2가 시작점이었다면, 2를 dequeue 하면서 queue에는 0과 1이 삽입됨. 0이 dequeue될 때는 0과 연결된 인접 정점이 없으므로 그대로 dequeue되며, 1의 경우 3을 삽입 후 dequeue됨. 3이 dequeue될 경우에도 인접 정점이 없으므로 dequeue된 후, 큐는 비게 되어 루프 종료 = 모든 정점 방문 완료

2) 깊이 우선 탐색(Depth First Search, DFS) : 스택 기반

시작 정점을 기준으로 인접한 모든 노드를 방문 후, 다시 시작 정점으로 돌아와 가보지 않은 방향으로 다시 방문

2-1) 재귀 함수를 이용(스택프레임)

def __dfs_recursion ( self , v ) : print ( v , end = ‘=’ ) self . visited [ v ] = True adj_v = self . adj_list [ v ] for u in adj_v : if not self . visited [ u ] : self . __dfs_recursion ( u ) def dfs ( self , v ) : self . init_visited ( ) self . __dfs_recursion ( v )

dfs : 재귀함수를 돌기 전 방문리스트를 초기화함.

__dfs_recursion : 매개변수 v를 먼저 방문 후, v와 인접한 adj_v의 리스트에 있는 모든 정점을 방문함. 방문하지 않은 정점을 만날 때는 재귀 함수 호출

ex) 0-1-2-3으로 된 그래프에서 2를 시작 정점으로 하는 경우 : dfs_rec(2)를 시작하면서 True로 변경, adj[2] = {1,3}이며 for 루프를 돌면서 dfs_rec(1), dfs_rec(0)을 돈 후, dfs_rec(3)까지 방문 후 종료.

-> 스택프레임 구조 : dfs_rec(2)-dfs_rec(1)-dfs_rec(0) -> dfs_rec(2)-dfs_rec(3) -> dfs_rec(2) -> None

ex) 0-1-2-3으로 된 그래프에서 2를 시작 정점으로 하는 경우 : dfs_rec(2)를 시작하면서 True로 변경, adj[2] = {1,3}이며 for 루프를 돌면서 dfs_rec(1), dfs_rec(0)을 돈 후, dfs_rec(3)까지 방문 후 종료. -> 스택프레임 구조 : dfs_rec(2)-dfs_rec(1)-dfs_rec(0) -> dfs_rec(2)-dfs_rec(3) -> dfs_rec(2) -> None 재귀 함수가 끝난 경우 반드시 시작 정점으로 돌아가며, 더이상 방문할 곳이 없는 경우 종료됨

2-2) 스택과 반복문 이용

def iter_dfs ( self , v ) : s = Stack ( ) self . init_visited ( ) s . push ( v ) self . visited [ v ] = True print ( v , end = ‘=’ ) is_visited = False while not s . empty ( ) : is_visited = False v = s . peak ( ) adj_v = self . adj_list [ v ] for u in adj_v : if not self . visited [ u ] : s . push ( u ) self . visited [ u ] = True is_visited = True break if not is_visited : s . pop ( )

s.push(v) : 최초 방문한 정점을 stack에 쌓음

: 최초 방문한 정점을 stack에 쌓음 is_visited = False : 아직 방문하지 않은 정점을 방문하였는가?

: 아직 방문하지 않은 정점을 방문하였는가? v = s.peak() : dfs와는 다르게 dequeue를 통해 정점을 가져오지 않고 peak 메서드 사용하여 스택에는 정점이 그대로 쌓여있음

: dfs와는 다르게 dequeue를 통해 정점을 가져오지 않고 peak 메서드 사용하여 스택에는 정점이 그대로 쌓여있음 is_visited = True break : 인접 정점들에 대해 순회하며 방문하지 않은 정점을 만난 경우 True로 변경 후, 방문하지 않았던 정점에 대하여 for문을 빠져나가 while 루프를 다시 돈다.

: 인접 정점들에 대해 순회하며 방문하지 않은 정점을 만난 경우 True로 변경 후, 방문하지 않았던 정점에 대하여 for문을 빠져나가 while 루프를 다시 돈다. for 문 빠져나가는 법

1) is_visited = True : 방문하지 않은 정점을 발견하여 이동한 경우 -> 스택에는 이동한 정점이 쌓여 있음. -> 다음 모든 정점 방문 후 pop()을 통해 해당 정점을 삭제 후 이전 정점으로 이동할 수 있음.

2) is_visited = False : adj[v]의 모든 노드를 방문한 상태 -> 이전 정점으로 이동해야하므로 현재 정점을 pop해야함

2-3) 그래프가 연결되어 있지 않은 경우

def dfs_all ( self ) : self . init_visited ( ) for i in range ( len ( self . visited ) ) : if not self . visited [ i ] : self . __dfs_recursion ( i )

[파이썬 알고리즘] 그래프 구현, DFS, BFS, 인접 행렬, 최소 신장 트리

파이썬 [파이썬 알고리즘] 그래프 구현, DFS, BFS, 인접 행렬, 최소 신장 트리 이재윤 ・ URL 복사 본문 기타 기능 공유하기 신고하기 그래프란? ​ – 여러 노드가 서로 연결된 자료구조 – 여러 노드가 연결될 수 있음 ​ ​ – 그래프는 정점을 연결하는 간선의 방향성 여부에 따라 방향 그래프와, 무방향 그래프로 나눈다. ​ – 트리의 노드에 해당하는 용어는 그래프에선 정점(Vertex) 이다. ​ – 정점을 연결하는 선은 간선(Edge)이다. ​ – 그래트는 정점과 간선의 집합이다. 무방향 그래프 위에 사진에서 정점의 집합 V(G) = {A,B,C,D} 이다. 간선의 집합 E(G) = { (A,B), (A,C), (A,D), (B,C), (C,D) } ​ * (A,B) , (B,A) 는 중복이므로 한 번만 표기한다. ​ 방향 그래프 ​ – 위에 사진처럼 방향성이 있는 그래프이다. ​ 화살표로 간선 방향을 표기하고, 그래프의 정점 집합이 무방향 그래프와 같다. ​ 정점의 집합 V(G) = {A,B,C,D} 이다. 간선의 집합 E(G) = { , , , } * 방향 그래프에선 <> 로 표기 가중치 그래프 ​ ​ – 간선마다 가중치가 다르게 부여된 그래프이다. ​ – 무방향 가중치 그래프, 방향 가중치 그래프가 있다. ​ 그래프 순회 ​ – 그래프의 모든 정점을 한 번씩 방문하는 것을 의미 ​ – 깊이 우선 탐색 (Depth First Serch, DFS) – 너비 우선 탐색(Breadth First Serch, BFS) ​ 인접 행렬 (Adjacency Matrix) ​ – 그래프를 코드로 구현 하는 방법 – 정방형으로 구성된 행렬이다. – 정점이 n개인 그래프는 n * n 인접행렬로 표현한다. ​ 위와 같이 인접 행렬에서는 연결되었다면 1, 연결되지 않으면 0으로 표시하고, 출발점과 도착점이 같은 자기 자신은 0으로 표시한다. ​ ​ ​ * 무방향 그래프에서 인접 행렬은 대각선을 기준으로 서로 대칭한다. ​ ​ ​ 무방향 그래프 G1과 방향 그래프 G3의 구현 코드 ## 무방향 그래프 G1과 방향 그래프 G3의 구현 ## ## 함수 선언 부분 ## class Graph() : # 인접 행렬을 2차원 배열로 구현 def __init__ (self, size) : self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] ## 전역 변수 선언 부분 ## G1 = None # 무방향 그래프 G3 = None # 방향 그래프 ## 메인 코드 부분 ## G1 = Graph(4) G1.graph[0][1] = 1; G1.graph[0][2] = 1; G1.graph[0][3] = 1 G1.graph[1][0] = 1; G1.graph[1][2] = 1 G1.graph[2][0] = 1; G1.graph[2][1] = 1; G1.graph[2][3] = 1 G1.graph[3][0] = 1; G1.graph[3][2] = 1 print(‘## G1 무방향 그래프 ##’) for row in range(4) : for col in range(4) : print(G1.graph[row][col], end = ‘ ‘) print() G3 = Graph(4) G3.graph[0][1] = 1; G3.graph[0][2] = 1 G3.graph[3][0] = 1; G3.graph[3][2] = 1 print(‘## G3 방향 그래프 ##’) for row in range(4) : for col in range(4) : print(G3.graph[row][col], end = ‘ ‘) print() ## 실행결과 ## ## G1 무방향 그래프 ## 0 1 1 1 1 0 1 0 1 1 0 1 1 0 1 0 ## G3 방향 그래프 ## 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 – 깊이 우선 탐색 (Depth First Serch, DFS) 구현하기 ​ – 스택을 사용해야한다. ​ – 방문한 정점을 스택에 푸시하고, 방문할 곳이 막다른 곳이라면 스택에서 팝하는 방식 이용 ​ – 기존에 방문했는지 여부를 확인하고자 방문한 곳을 기록하는 배열을 사용 ## 깊이 우선 탐색 (Depth First Serch, DFS) 구현 ## ## 클래스와 함수 선언 부분 ## class Graph() : def __init__ (self, size) : self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] ## 전역 변수 선언 부분 ## G1 = None stack = [] visitedAry = [] # 방문한 정점 ## 메인 코드 부분 ## G1 = Graph(4) G1.graph[0][2] = 1; G1.graph[0][3] = 1 G1.graph[1][2] = 1 G1.graph[2][0] = 1; G1.graph[2][1] = 1; G1.graph[2][3] = 1 G1.graph[3][0] = 1; G1.graph[3][2] = 1 print(‘## G1 무방향 그래프 ##’) for row in range(4): for col in range(4): print(G1.graph[row][col], end = ‘ ‘) print() current = 0 # 시작 정점 stack.append(current) visitedAry.append(current) while (len(stack) != 0) : next = None for vertex in range(4) : if G1.graph[current][vertex] == 1 : if vertex in visitedAry : # 방문한 적이 있는 정점이면 탈락 pass else : # 방문한 적이 없으면 다음 정점으로 지정 next = vertex break if next != None : # 다음에 방문할 정점이 있는 경우 current = next stack.append(current) visitedAry.append(current) else : # 다음에 방문할 정점이 없는 경우 current = stack.pop() print(‘방문 순서 –>’, end = ‘ ‘) for i in visitedAry : print(chr(ord(‘A’)+i), end = ‘ ‘) ## 실행 결과 ## ## G1 무방향 그래프 ## 0 0 1 1 0 0 1 0 1 1 0 1 1 0 1 0 방문 순서 –> A C B D 최소 신장 트리 ​ – 신장 트리란 최소 간선으로 그래프의 모든 정점이 연결되는 그래프이다. ​ * 신장 트리의 특징으로 간선 개수 = 정점 개수 – 1 이다. ​ ​ 그래프의 간선에 가중치가 있는 그래프를 가중치 그래프라고 한다. ​ 가중치 그래프에서 만들 수 있는 신장 트리 중 합계가 최소인 것을 최소 비용 신장 트리 라고 한다. ​ 최소 비용 신장 트리를 구현하는 방법으로 프림(Prim), 크루스컬(Kruskal) 알고리즘이 있다. 가중치 그래프 구현하기 ​ 가중치가 없는 무방향 그래프는 간선이 연결되면1, 안되면 0으로 처리한 반면에, ​ 가중치 그래프는 간선이 연결되면 1 대신 가중치 값을 넣고, 연결되지 않으면 0을 넣는다. ​ ## 최소 비용으로 자전거 도로를 건설하는 코드 ## ## 클래스와 함수 선언 부분 ## class Graph() : def __init__ (self, size) : self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] def printGraph(g) : print(‘ ‘, end = ‘ ‘) for v in range(g.SIZE) : print(nameAry[v], end = ‘ ‘) print() for row in range(g.SIZE) : print(nameAry[row], end = ‘ ‘) for col in range(g.SIZE) : print(g.graph[row][col], end = ‘ ‘) print() print() def findVertex(g, findVtx) : # 정점이 그래프에 연결되어 있는지 확인하는 함수 stack = [] visitedAry = [] # 방문한 정점 current = 0 # 시작 정점 stack.append(current) visitedAry.append(current) while (len(stack) != 0) : next = None for vertex in range(gSize) : if g.graph[current][vertex] != 0 : if vertex in visitedAry : # 방문한 적이 있는 정점이면 탈락 pass else : # 방문한 적이 없으면 다음 정점으로 지정 next = vertex break if next != None : # 다음에 방문할 정점이 있는 경우 current = next stack.append(current) visitedAry.append(current) else : # 다음에 방문할 정점이 없는 경우 current = stack.pop() if findVtx in visitedAry : return True else : return False ## 전역 변수 선언 부분 ## G1 = None nameAry = [‘춘천’, ‘서울’, ‘속초’ ,’대전’, ‘광주’, ‘부산’] 춘천, 서울, 속초, 대전, 광주, 부산 = 0, 1, 2, 3, 4, 5 ## 메인 코드 부분 ## gSize = 6 G1 = Graph(gSize) G1.graph[춘천][서울] = 10; G1.graph[춘천][속초] = 15 G1.graph[서울][춘천] = 10; G1.graph[서울][속초] = 40; G1.graph[서울][대전] = 11 G1.graph[서울][광주] = 50 G1.graph[속초][춘천] = 15; G1.graph[속초][서울] = 40; G1.graph[속초][대전] = 12 G1.graph[대전][서울] = 11; G1.graph[대전][속초] = 12; G1.graph[대전][광주] = 20 G1.graph[대전][부산] = 30 G1.graph[광주][서울] = 50; G1.graph[광주][대전] = 20; G1.graph[광주][부산] = 25 G1.graph[부산][대전] = 30; G1.graph[부산][광주] = 25 print(‘## 자전거 도로 건설을 위한 전체 연결도 ##’) printGraph(G1) ## 가중치 간선 목록 ## edgeAry = [] for i in range(gSize) : for k in range(gSize) : if G1.graph[i][k] != 0 : edgeAry.append([G1.graph[i][k], i, k]) from operator import itemgetter edgeAry = sorted(edgeAry, key=itemgetter(0), reverse=True) newAry = [] for i in range(0, len(edgeAry), 2) : newAry.append(edgeAry[i]) index = 0 while (len(newAry) > gSize-1) : # 간선 개수가 ‘점점 개수-1’ 일 때까지 반복 start = newAry[index][1] end = newAry[index][2] saveCost = newAry[index][0] G1.graph[start][end] = 0 G1.graph[end][start] = 0 startYN = findVertex(G1, start) endYN = findVertex(G1, end) if startYN and endYN : del(newAry[index]) else : G1.graph[start][end] = saveCost G1.graph[end][start] = saveCost index += 1 print(‘## 최소 비용의 자전거 도로 연결도 ##’) printGraph(G1) ## 실행 결과 ## ## 자전거 도로 건설을 위한 전체 연결도 ## 춘천 서울 속초 대전 광주 부산 춘천 0 10 15 0 0 0 서울 10 0 40 11 50 0 속초 15 40 0 12 0 0 대전 0 11 12 0 20 30 광주 0 50 0 20 0 25 부산 0 0 0 30 25 0 ## 최소 비용의 자전거 도로 연결도 ## 춘천 서울 속초 대전 광주 부산 춘천 0 10 0 0 0 0 서울 10 0 0 11 0 0 속초 0 0 0 12 0 0 대전 0 11 12 0 20 0 광주 0 0 0 20 0 25 부산 0 0 0 0 25 0 ​ 인쇄

별의 블로그 :: [Python] 그래프(Graph)

728×90

728×170

그래프(Graph)

그래프(Graph)의 기본

그래프의 개념

그래프(Graph) : 여러 노드가 서로 연결된 자료구조

: 여러 노드가 서로 연결된 자료구조 루트에서 하위 노드 방향으로만 이어지는 트리 와 달리, 여러 노드가 연결되어 있을 수 있다.

와 달리, 여러 노드가 연결되어 있을 수 있다. 트리도 그래프의 일종이지만, 트리와 그래프를 구현하는 코드 등이 확연히 다르기 때문에 이 둘은 별도로 생각하는 편이 낫다.

트리(왼쪽)와 그래프(오른쪽)

그래프의 종류

그래프는 정점을 연결하는 간선 의 방향성 여부 에 따라 방향 그래프 와 무방향 그래프 로 나눈다.

에 따라 와 로 나눈다. 간선에 가중치(Weight)를 부여하여 가중치 그래프도 만들 수 있다.

무방향 그래프

트리의 노드(Node) 에 해당하는 용어가 그래프에서는 정점(Vertex) 이다.

에 해당하는 용어가 그래프에서는 이다. 정점을 연결하는 선 은 간선(Edge)이므로 그래프는 정점과 간선의 집합으로 볼 수 있다.

무방향 그래프 형태

그래프에서 정점은 Vertex의 V 로, 간선은 Edge의 E 로, 그래프는 Graph의 G 로 표기한다.

로, 간선은 Edge의 로, 그래프는 Graph의 로 표기한다. 따라서 정점 집합을 V(G)로, 간선 집합을 E(G)로 표기한다.

V(G1) = { A, B, C, D } V(G2) = { A, B, C, D }

무방향 그래프 의 간선은 연결하는 두 정점을 ( ) 괄호 로 표현한다. 정점 A와 B의 간선을 (A, B)로 표현한다.

의 간선은 연결하는 두 정점을 로 표현한다.

E(G1) = { (A, B), (A, C), (A, D), (B, C), (C, D) } E(G2) = { (A, B), (B, D), (D, C) }

방향 그래프

방향성이 있는 그래프는 화살표로 간선 방향을 표기하고, 그래프의 정점 집합이 무방향 그래프와 같다.

방향 그래프의 형태

V(G1) = { A, B, C, D } V(G2) = { A, B, C, D }

방향 그래프의 간선은 연결하는 두 정점을 < > 로 묶어 표현한다. 정점 A와 B의 간선을 로 표현한다.

로 묶어 표현한다. 그리고 방향성이 있어 는 서로 다른 간선이다.

E(G1) = { , , , } E(G2) = { , , }

가중치 그래프

간선 마다 가중치가 다르게 부여된 그래프 를 가중치 그래프(Weight Graph)라고 한다.

무방향 가중치 그래프와 방향 가중치 그래프

깊이 우선 탐색(Depth First Search, DFS)

그래프 순회(Graph Traversal) : 그래프의 모든 정점을 한 번씩 방문하는 것

그래프의 모든 정점을 한 번씩 방문하는 것 그래프 순회 방식 깊이 우선 탐색(Depth First Search, DFS) 너비 우선 탐색(Breath First Search, BFS)

깊이 우선 탐색의 예

그래프는 트리와 달리 부모와 자식 개념이 없으므로, 어떤 정점에서 시작해도 결과는 같다.

모든 노드를 방문한 것처럼 보이지만, 그래프는 정점마다 연결된 선이 여러 개 있을 수 있기 때문에 반드시 시작 정점까지 돌아가야 한다.

그래프의 인접 행렬 표현

그래프를 코드로 구현할 때는 일반적으로 인접 행렬(Adjacent Matrix) 을 사용한다.

을 사용한다. 인접 행렬은 정방향으로 구성된 행렬 로, 정점이 4개인 그래프는 4×4 인접 행렬 로 표현한다.

로, 정점이 4개인 그래프는 로 표현한다. 출발점과 도착점이 연결되었다면 1로, 연결되지 않았다면 0으로 표시한다.

출발점과 도착점이 같은 자기 자신 은 모두 0으로 표시된다.

무방향 그래프의 인접 행렬

무방향 그래프의 인접 행렬은 대각선을 기준으로 서로 대칭 된다.

방향 그래프의 인접 행렬

방향 그래프는 무방향 그래프와 달리 대각선을 기준으로 대칭되지 않는다. 도착점과 출발점이 대칭되지 않아 와 다르다.

그래프의 구현

① 그래프의 정점 생성

class Graph : def __init__(self, size) : self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] G1 = Graph(4)

② 그래프의 정점 연결

G1.graph[0][1] = 1 # (A, B) 간선 G1.graph[0][2] = 1 # (A, C) 간선 G1.graph[0][3] = 1 # (A, D) 간선

G1.graph[1][0] = 1 # (B, A) 간선 G1.graph[1][2] = 1 # (B, C) 간선

G1.graph[2][0] = 1 # (C, A) 간선 G1.graph[2][1] = 1 # (C, B) 간선 G1.graph[2][3] = 1 # (C, D) 간선 G1.graph[3][0] = 1 # (D, A) 간선 G1.graph[3][2] = 1 # (D, C) 간선

구현 : 무방향 그래프

class Graph() : def __init__ (self, size) : self.SIZE = size self.graph = [ [0 for _ in range(size)] for _ in range(size) ] G1, G3 = None, None G1 = Graph(4) G1.graph[0][1] = 1; G1.graph[0][2] = 1; G1.graph[0][3] = 1 G1.graph[1][0] = 1; G1.graph[1][2] = 1 G1.graph[2][0] = 1; G1.graph[2][1] = 1; G1.graph[2][3] = 1 G1.graph[3][0] = 1; G1.graph[3][2] = 1 print(‘## G1 무방향 그래프 ##’) for row in range(4) : for col in range(4) : print(G1.graph[row][col], end=’ ‘) print() G3 = Graph(4) G3.graph[0][1] = 1; G3.graph[0][2] = 1 G3.graph[3][0] = 1; G3.graph[3][2] = 1 print(‘## G3 방향 그래프 ##’) for row in range(4) : for col in range(4) : print(G3.graph[row][col], end=’ ‘) print()

더보기 ## G1 무방향 그래프 ## 0 1 1 1 1 0 1 0 1 1 0 1 1 0 1 0 ## G3 방향 그래프 ## 0 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0

그래프 개선

무방향 그래프를 인접 행렬로 구성할 때 0, 1, 2, … 숫자 로 구성했다.

로 구성했다. 하지만, 실제 그래프는 사람 이름, 도시 이름 등으로 구성하기도 한다.

변수 이름을 정점 번호 로 지정하면 더 직관적이다.

로 지정하면 더 직관적이다. 변수 이름은 한글도 지원된다.

문별, 솔라, 휘인, 쯔위 = 0, 1, 2, 3 G1.graph[문별][솔라] = 1; G1.graph[문별][쯔위] = 1 G1.graph[솔라][문별] = 1; G1.graph[솔라][쯔위] = 1

주석을 추가하여 그래프를 표현할 수 있다.

nameAry = [‘문별’, ‘솔라’, ‘휘인’, ‘쯔위’, ‘선미’, ‘화사’] print(‘\t’, end = ”) for v in range(g.SIZE) : print(nameAry[v], end = ‘\t’) print() for row in range(g.SIZE) : print(nameAry[row], end = ‘\t’) for col in range(g.SIZE) : print(g.graph[row][col], end= ‘\t’) print() print()

구현 : 개선된 무방향 그래프

class Graph() : def __init__ (self, size) : self.SIZE = size self.graph = [[0 for _ in range(size)] for _ in range(size)] def printGraph(g) : print(‘\t’, end = ”) for v in range(g.SIZE) : print(nameAry[v], end = ‘\t’) print() for row in range(g.SIZE) : print(nameAry[row], end = ‘\t’) for col in range(g.SIZE) : print(g.graph[row][col], end= ‘\t’) print() print() G1 = None nameAry = [‘문별’, ‘솔라’, ‘휘인’, ‘쯔위’, ‘선미’, ‘화사’] 문별, 솔라, 휘인, 쯔위, 선미, 화사 = 0, 1, 2, 3, 4, 5 gSize = 6 G1 = Graph(gSize) G1.graph[문별][솔라] = 1; G1.graph[문별][휘인] = 1 G1.graph[솔라][문별] = 1; G1.graph[솔라][쯔위] = 1 G1.graph[휘인][문별] = 1; G1.graph[휘인][쯔위] = 1 G1.graph[쯔위][솔라] = 1; G1.graph[쯔위][휘인] = 1; G1.graph[쯔위][선미] = 1; G1.graph[쯔위][화사] = 1 G1.graph[선미][쯔위] = 1; G1.graph[선미][화사] = 1 G1.graph[화사][쯔위] = 1; G1.graph[화사][선미] = 1 print(‘## G1 무방향 그래프 ##’) printGraph(G1)

더보기 ## G1 무방향 그래프 ## 문별 솔라 휘인 쯔위 선미 화사 문별 0 1 1 0 0 0 솔라 1 0 0 1 0 0 휘인 1 0 0 1 0 0 쯔위 0 1 1 0 1 1 선미 0 0 0 1 0 1 화사 0 0 0 1 1 0

728×90

그리드형

파이썬에서 그래프(Graph) 구현하기

이번 포스팅에서는 그래프(Graph) 자료구조 포스팅(https://junwha0511.blog.me/221698233962)에 이어

그래프를 파이썬에서 구현해보는 포스팅을 하겠습니다.

그래프(Graph) 자료구조란?

Graph는 정점(Vertex, 혹은 Node)과정점들을 연결하는 간선(Edge)으로 이루어진 자료구조입니다.​

그래프 구조는 파이썬에서 딕셔너리 자료형(https://junwha0511.blog.me/221698243080)으로 구현할 수 있습니다.

바로 이중 딕셔너리를 사용하는 방법인데요,

예를 들어 A, B 정점이 있고 가중치가 5인 간선으로 이어져 있다면

weight = {‘A’:{‘B’:5}, ‘B’:{‘A’:5}}

위와 같이 A와 B의 상호 관계를 이중 딕셔너리를 사용해 표현할 수 있습니다.

하나하나 관계를 입력하는 것은 매우 시간이 오래 걸리는 작업이기 때문에

아래와 같이 ‘A-B=3’과 같은 형식으로 입력을 자동화하는 코드를 구현하였습니다.

weight = {‘A’:{}, ‘Z’:{}} flag={‘A’:True, ‘Z’:False} dist={‘A’:0, ‘Z’:float(‘inf’)} route=[‘Z’] ptd=’Z’ print(‘input Graph(like A-B=1, but always A is start node and Z is destination)’) print(‘if you like to stop, input END’) while True: while True: line = input() if line == ‘END’: break else: node1 = line[line.index(‘-‘)-1] node2 = line[line.index(‘-‘)+1] weightValue = int(line[line.index(‘=’)+1:]) if not (node1 in weight): weight[node1] = {} flag[node1] = False dist[node1] = float(‘inf’) if not (node2 in weight): weight[node2] = {} flag[node2] = False dist[node2] = float(‘inf’) weight[node1][node2] = weightValue weight[node2][node1] = weightValue print(‘Below is your graph, you like to stop? [Y/N]’) print(weight) print(flag) print(dist) isYes = input() if isYes==’Y’: break while not flag[‘Z’]: nextDist=float(‘inf’) nextNode=None for key in flag.keys(): if flag[key]: for node in weight[key]: if not flag[node]: cDist=dist[key]+weight[key][node] if cDist

Leave a Comment