우리에게 가장 익숙한 연결관계는 위치 데이터이다. 아래처럼 위치들이 연결된 것을 볼 수 있다.
아래는 사회연결망 그래프이다.
엣지 : 노드를 잇는 선은 엣지
라고 한다.
인접 : 아래처럼 영훈과 동욱이 엣지로 연결되어 있는 경우, 서로 인접
해 있다고 표현한다.
경로 : 아래처럼 지웅과 규리는 인접해 있지는 않지만, 소원을 통해 연결되므로, '지웅-소원-규리' 경로
를 통해 연결됐다고 표현한다.
최단경로 : 두 노드들 사이에 가장 거리가 작은 경로를 최단경로
라고 한다.
사이클 : 특정 노드에서 다시 그 노드로 돌아오는 경로는 사이클
이라고 한다.
차수 : 한 노드가 가지고 있는 엣지의 수를 차수
라고 한다. 현승은 세 친구와 연결되어 있으니 차수가 3이다. 차수를 이용하면 노드에 대해 유용한 데이터를 알아낼 수 있다. 이 경우에는 차수가 각 노드의 친구의 수를 나타낸다.
무방향 그래프 : 그래프에서는 방향이 있는데, 위 친구 예시에서는 양방향이므로 오히려 그냥 방향이 없다고 이야기할 수도 있다. 이런 경우를 무방향 그래프(undirected graph)
라고 한다. 이 경우 (현승, 지웅)과 (지웅, 현승)은 똑같다.
방향 그래프 : 인스타그램에서는 누군가 나를 팔로우하면 방향이 생긴다. 이런 경우, 방향 그래프
라고 하며, 방향그래프에서는 엣지가 떠나는 노드를 항상 앞에 쓰고 들어가는 노드를 뒤에 적어준다. 현승이가 지웅이를 팔로우하면, (현승, 지웅)과 같이 적는다.
방향 그래프에서는 차수를 출력차수
와 입력차수
로 나누어서 생각한다.
가중치 그래프 : 각 공항끼리 이동시간을 가중치로 나타낼 수 있는데, 이런 그래프를 가중치 그래프
라고 한다. 가중치 그래프에서의 거리는 경로에 있는 엣지의 가중치의 합이다.
무가중치 그래프 : 모든 엣지의 값이 동일한 그래프는 무 가중치 그래프
라고 부른다. 무가중치 그래프에서의 거리는 단순히 경로에 있는 엣지의 수이다.
그래프 구현에서는 노드를 구현하는 방법과 엣지를 구현하는 방법으로 나눠서 본다.
노드를 구현하는 방법 먼저 보자. 지하철 역 노드를 나타내는 클래스를 구현해보자.
class StationNode:
"""지하철 역 노드를 나타내는 클래스"""
def __init__(self, name, num_exits):
self.name = name
self.num_exits = num_exits
# 지하철 역 노드 인스턴스 생성
station_0 = StationNode('교대역', 14)
station_1 = StationNode('사당역', 14)
station_2 = StationNode('종로3가역', 16)
station_3 = StationNode('서울역', 16)
링크드리스트에는 헤드노드, 트리의 경우엔 루트노드가 존재하는데, 연결고리를 나타내는 그래프에서는 일반적으로 모든 노드가 동등한 위치에 있다.
그러니 항상 모든 노드에 바로 접근할 수 있도록 만들어준다. 이렇게 할 수 있는 두 가지 방법이 있다. 배열이나 해시테이블에 저장하는 방법이다. O(1)으로 접근할 수 있어 효율적이다.
1. 노드들을 배열 또는 동적배열에 저장한다. (파이썬에서는 자료형 리스트)
# 노드들을 파이썬 리스트에 저장
stations = [station_0, station_1, station_2, station_3]
# 인덱스로 바로 접근 가능
stations[3]
# 지하철 역 노드들을 파이썬 딕셔너리에 저장
stations = {
'교대역':station_0,
'사당역':station_1,
'종로3가역':station_2,
'서울역':station_3
}
# 교대역에 접근하고 싶으면,
node_1 = stations['교대역']
이번에는 그래프의 엣지를 구현하는 방법중 하나인 인접행렬
을 알아보자.
인접행렬은 노드들의 연결관계(엣지)를 나타내는 2차원 리스트이다.
예시를 살펴보자.
이번에는 엣지를 구현하는 또 다른 방법인 인접 리스트
를 알아보자.
아래 역 그래프에서 역 이름을 key로, 노드 인스턴스를 value로 써서 파이썬 딕셔너리에 저장했다고 해보자.
그리고 노드 클래스에 self.adjacent_stations = []
를 추가해준다.
위 예시로 보면 강남의 'adjacent_stations' 는 [stations['교대'], stations['역삼'], stations['양재']]
이다.
class StationNode:
"""지하철 역 노드를 나타내는 클래스"""
def __init__(self, name, num_exits):
self.name = name
self.num_exits = num_exits
self.adjacent_stations = []
인접리스트는 한 노드가 연결된 다른 노드들에 대한 레퍼런스를 저장한다.
보통 다른 자료구조에서 데이터 수를 이라고 하고, 이를 이용해 효율성을 나타내곤 했는데, 그래프에서는 다른 기호들을 사용한다.
란?
란?
와 의 관계
노드를 저장하는 공간
인접행렬이 차지하는 공간
“총 노드의 수 x 총 노드의 수”
만큼의 행렬을 만드므로, 의 공간을 사용한다.인접리스트가 차지하는 공간
인접 행렬에서 인접한 노드들을 가지고 오려면 항상 길이가 인 배열(파이썬 리스트)를 돌아야 한다. 인접 리스트를 쓰면 인접한 노드만 들어 있는 배열을 돌아서 가지고 올 수 있죠. 대부분의 경우 이 리스트는 길이가 보다 짧다. 그렇기 때문에 인접한 노드들을 가지고 올 때는 인접 리스트를 사용하는 게 더 효율적이다.