본문 바로가기
개발일지/자료구조

그래프(Graph): 복잡한 관계를 표현하고 처리하는 자료구조

by Peter.JH 2024. 11. 17.
728x90
반응형

 

그래프(Graph)는 노드(Node)와 이들을 연결하는 간선(Edge)으로 구성된 자료구조로, 복잡한 관계를 표현하고 분석하는 데 사용됩니다. 그래프는 다양한 형태로 존재하며, 방향성(Directionality)과 가중치(Weight)에 따라 여러 유형으로 분류됩니다.

 

현대 사회에서는 사람, 도시, 인터넷, 소셜 네트워크 등 다양한 시스템 간의 복잡한 관계를 모델링할 필요가 있습니다. 이러한 복잡한 관계를 효과적으로 표현하고 분석하기 위해 그래프 자료구조가 개발되었습니다. 그래프는 관계형 데이터뿐만 아니라 네트워크 분석, 최단 경로 찾기, 순회 문제 등 다양한 알고리즘의 기반이 됩니다. 그래프는 소셜 네트워크 분석, 추천 시스템, 네트워크 라우팅, 지리 정보 시스템(GIS), 바이오 인포매틱스 등 다양한 실무 분야에서 핵심적으로 사용됩니다. 

 

 


 

그래프(Graph)의 개념

 

그래프는 정점(Vertex)간선(Edge)으로 구성된 자료구조입니다. 정점은 객체나 데이터를 나타내며, 간선은 정점 간의 관계를 나타냅니다. 그래프는 방향성(Directionality)과 가중치(Weight)에 따라 다양한 유형으로 분류됩니다.

 

그래프의 유형

  1. 무방향 그래프(Undirected Graph):
    • 간선에 방향성이 없는 그래프입니다.
    • 간선은 두 정점 간의 상호 관계를 나타냅니다.
  2. 방향 그래프(Directed Graph, Digraph):
    • 간선에 방향성이 있는 그래프입니다.
    • 간선은 출발 정점에서 도착 정점으로의 단방향 관계를 나타냅니다.
  3. 가중치 그래프(Weighted Graph):
    • 간선에 가중치가 부여된 그래프입니다.
    • 가중치는 정점 간의 관계의 강도나 비용 등을 나타냅니다.
  4. 비가중치 그래프(Unweighted Graph):
    • 간선에 가중치가 없는 그래프입니다.
    • 모든 간선은 동일한 중요성을 가집니다.

 

그래프의 특징

  • 정점(Vertex)의 개수: 그래프를 구성하는 정점의 총 개수입니다.
  • 간선(Edge)의 개수: 그래프를 구성하는 간선의 총 개수입니다.
  • 밀도(Density): 그래프의 간선 개수가 정점 개수에 비해 얼마나 많은지를 나타냅니다.
  • 연결성(Connectivity): 그래프가 얼마나 잘 연결되어 있는지를 나타냅니다.

 

그래프의 표현 방법

그래프는 주로 다음과 같은 방법으로 표현됩니다:

  1. 인접 행렬(Adjacency Matrix):
    • 정점 간의 연결 여부를 행렬로 표현합니다.
    • 공간 복잡도가 높지만, 간선의 존재 여부를 빠르게 확인할 수 있습니다.
  2. 인접 리스트(Adjacency List):
    • 각 정점에 인접한 정점들의 리스트를 저장합니다.
    • 공간 효율성이 높고, 간선 목록을 순회하기 용이합니다.

 


그래프의 실제 사용 사례

 

소셜 네트워크 분석

소셜 네트워크에서 사람들은 정점으로, 친구 관계는 간선으로 표현됩니다. 이를 통해 친구 추천, 영향력 분석, 커뮤니티 탐지 등의 작업을 수행할 수 있습니다.

네트워크 라우팅

컴퓨터 네트워크에서 라우터는 정점으로, 연결된 네트워크 링크는 간선으로 표현됩니다. 최단 경로 알고리즘을 통해 데이터 패킷의 효율적인 전송 경로를 찾습니다.

추천 시스템

전자 상거래나 콘텐츠 플랫폼에서 사용자와 상품을 정점으로, 사용자-상품 간의 상호 작용을 간선으로 표현하여 사용자에게 맞춤형 추천을 제공합니다.

지리 정보 시스템(GIS)

도시, 도로, 지점 등을 정점과 간선으로 표현하여 최단 경로 찾기, 네트워크 분석 등을 수행합니다.

바이오 인포매틱스

유전자, 단백질, 생물 종 등을 정점으로, 이들 간의 상호 작용을 간선으로 표현하여 생물학적 네트워크를 분석합니다.

 

 

 


 

그래프 구현하기

 

Python으로 그래프 구현하기

class Graph:
    def __init__(self):
        self.graph = {}  # 인접 리스트를 딕셔너리로 초기화

    def add_vertex(self, vertex):
        if vertex not in self.graph:
            self.graph[vertex] = []  # 새로운 정점 추가 시 빈 리스트로 초기화

    def add_edge(self, vertex1, vertex2, directed=False, weight=1):
        if vertex1 not in self.graph:
            self.add_vertex(vertex1)
        if vertex2 not in self.graph:
            self.add_vertex(vertex2)

        self.graph[vertex1].append((vertex2, weight))  # vertex1에서 vertex2로 간선 추가
        if not directed:
            self.graph[vertex2].append((vertex1, weight))  # 무방향 그래프일 경우 반대 방향 간선도 추가

    def display_graph(self):
        for vertex in self.graph:
            print(f"{vertex} -> {self.graph[vertex]}")

# 그래프 사용 예시
if __name__ == "__main__":
    g = Graph()
    g.add_edge('A', 'B')
    g.add_edge('A', 'C')
    g.add_edge('B', 'D', weight=2)
    g.add_edge('C', 'D')
    g.add_edge('D', 'E')
    g.add_edge('E', 'F', directed=True)

    g.display_graph()
    # 출력:
    # A -> [('B', 1), ('C', 1)]
    # B -> [('A', 1), ('D', 2)]
    # C -> [('A', 1), ('D', 1)]
    # D -> [('B', 2), ('C', 1), ('E', 1)]
    # E -> [('D', 1), ('F', 1)]
    # F -> []

코드 설명

  1. Graph 클래스:
    • init 메서드:
      • self.graph는 그래프를 인접 리스트 형태로 저장하는 딕셔너리입니다.
      • 각 키는 정점(Vertex)을 나타내며, 값은 해당 정점에 인접한 정점들의 리스트입니다.
    • add_vertex 메서드:
      • 새로운 정점을 그래프에 추가합니다.
      • 정점이 이미 존재하면 아무 작업도 수행하지 않습니다.
    • add_edge 메서드:
      • 두 정점 간에 간선을 추가합니다.
      • directed 매개변수가 False이면 무방향 그래프로, True이면 방향 그래프로 간선을 추가합니다.
      • weight 매개변수는 간선의 가중치를 나타냅니다.
      • 간선을 추가하기 전에 해당 정점들이 그래프에 존재하는지 확인하고, 없으면 add_vertex를 호출하여 추가합니다.
      • 무방향 그래프일 경우 반대 방향의 간선도 추가합니다.
    • display_graph 메서드:
      • 그래프의 인접 리스트를 출력합니다.
      • 각 정점과 그에 인접한 정점들의 목록을 출력하여 그래프의 구조를 시각적으로 확인할 수 있습니다.
  2. 그래프 사용 예시:
    • 그래프 객체 g를 생성하고, 여러 간선을 추가합니다.
    • display_graph 메서드를 호출하여 그래프의 구조를 출력합니다.
    • 무방향 그래프와 방향 그래프의 예제를 함께 보여줍니다.

 

JavaScript으로 그래프 구현하기

 

class Graph {
    constructor() {
        this.graph = {}; // 인접 리스트를 객체로 초기화
    }

    addVertex(vertex) {
        if (!this.graph[vertex]) {
            this.graph[vertex] = []; // 새로운 정점 추가 시 빈 배열로 초기화
        }
    }

    addEdge(vertex1, vertex2, directed = false, weight = 1) {
        if (!this.graph[vertex1]) {
            this.addVertex(vertex1);
        }
        if (!this.graph[vertex2]) {
            this.addVertex(vertex2);
        }

        this.graph[vertex1].push({ vertex: vertex2, weight: weight }); // vertex1에서 vertex2로 간선 추가
        if (!directed) {
            this.graph[vertex2].push({ vertex: vertex1, weight: weight }); // 무방향 그래프일 경우 반대 방향 간선도 추가
        }
    }

    displayGraph() {
        for (let vertex in this.graph) {
            let edges = this.graph[vertex].map(edge => `${edge.vertex}(${edge.weight})`).join(', ');
            console.log(`${vertex} -> [${edges}]`);
        }
    }
}

// 그래프 사용 예시
const g = new Graph();
g.addEdge('A', 'B');
g.addEdge('A', 'C');
g.addEdge('B', 'D', true, 2);
g.addEdge('C', 'D');
g.addEdge('D', 'E');
g.addEdge('E', 'F', true, 1);

g.displayGraph();
// 출력:
// A -> [B(1), C(1)]
// B -> [D(2)]
// C -> [D(1)]
// D -> [E(1)]
// E -> [F(1)]
// F -> []

코드 설명

  1. Graph 클래스:
    • constructor 메서드:
      • this.graph는 그래프를 인접 리스트 형태로 저장하는 객체입니다.
      • 각 키는 정점(Vertex)을 나타내며, 값은 해당 정점에 인접한 정점들의 배열입니다.
    • addVertex 메서드:
      • 새로운 정점을 그래프에 추가합니다.
      • 정점이 이미 존재하면 아무 작업도 수행하지 않습니다.
    • addEdge 메서드:
      • 두 정점 간에 간선을 추가합니다.
      • directed 매개변수가 false이면 무방향 그래프로, true이면 방향 그래프로 간선을 추가합니다.
      • weight 매개변수는 간선의 가중치를 나타냅니다.
      • 간선을 추가하기 전에 해당 정점들이 그래프에 존재하는지 확인하고, 없으면 addVertex를 호출하여 추가합니다.
      • 무방향 그래프일 경우 반대 방향의 간선도 추가합니다.
    • displayGraph 메서드:
      • 그래프의 인접 리스트를 출력합니다.
      • 각 정점과 그에 인접한 정점들의 목록을 출력하여 그래프의 구조를 시각적으로 확인할 수 있습니다.
  2. 그래프 사용 예시:
    • 그래프 객체 g를 생성하고, 여러 간선을 추가합니다.
    • displayGraph 메서드를 호출하여 그래프의 구조를 출력합니다.
    • 무방향 그래프와 방향 그래프의 예제를 함께 보여줍니다.

 

 


 

 

그래프 응용 예제

최단 경로 찾기 (다익스트라 알고리즘)

다익스트라 알고리즘은 가중치가 있는 그래프에서 특정 정점에서 다른 모든 정점으로의 최단 경로를 찾는 알고리즘입니다. 이 알고리즘은 우선순위 큐를 사용하여 효율적으로 동작합니다.

import heapq

def dijkstra(graph, start):
    pq = []  # 우선순위 큐 초기화
    heapq.heappush(pq, (0, start))  # 시작 정점과 거리 0 추가
    distances = {vertex: float('inf') for vertex in graph}  # 모든 정점의 거리를 무한대로 초기화
    distances[start] = 0  # 시작 정점의 거리는 0
    visited = set()  # 방문한 정점 집합

    while pq:
        current_distance, current_vertex = heapq.heappop(pq)  # 현재 정점과 거리 추출

        if current_vertex in visited:
            continue
        visited.add(current_vertex)

        for neighbor, weight in graph[current_vertex]:
            distance = current_distance + weight  # 현재 정점을 거쳐서 이웃 정점까지의 거리
            if distance < distances[neighbor]:
                distances[neighbor] = distance  # 더 짧은 경로 발견 시 거리 업데이트
                heapq.heappush(pq, (distance, neighbor))  # 우선순위 큐에 이웃 정점 추가

    return distances

# 그래프 정의 (인접 리스트)
graph = {
    'A': [('B', 1), ('C', 4)],
    'B': [('C', 2), ('D', 5)],
    'C': [('D', 1)],
    'D': [],
    'E': [('F', 3)],
    'F': []
}

# 최단 거리 계산
distances = dijkstra(graph, 'A')
print(distances)  # 출력: {'A': 0, 'B': 1, 'C': 3, 'D': 4, 'E': inf, 'F': inf}
코드 설명
  1. 다익스트라 알고리즘 구현:
    • heapq 모듈: Python의 힙 큐 알고리즘을 사용하여 우선순위 큐를 구현합니다.
    • dijkstra 함수:
      • graph: 인접 리스트 형태의 그래프.
      • start: 시작 정점.
      • pq: 우선순위 큐로, 현재까지의 거리를 기준으로 정렬됩니다.
      • distances: 각 정점까지의 최단 거리를 저장하는 딕셔너리. 초기값은 무한대(inf)로 설정됩니다.
      • visited: 이미 최단 거리가 확정된 정점을 저장하는 집합.
      • 알고리즘 동작:
        1. 시작 정점과 거리를 우선순위 큐에 추가합니다.
        2. 큐에서 현재 가장 짧은 거리의 정점을 추출합니다.
        3. 해당 정점이 이미 방문한 정점이라면 무시하고, 아니라면 방문 집합에 추가합니다.
        4. 현재 정점의 이웃 정점들을 탐색하여, 더 짧은 경로가 발견되면 거리를 업데이트하고 큐에 추가합니다.
        5. 큐가 빌 때까지 반복합니다.
      • 결과: 모든 정점까지의 최단 거리를 반환합니다.
  2. 그래프 정의 및 사용 예시:
    • 그래프는 인접 리스트 형태로 정의됩니다.
    • 다익스트라 알고리즘을 사용하여 정점 'A'에서 다른 모든 정점까지의 최단 거리를 계산하고 출력합니다.

 

 


 

그래프의 장단점

 

그래프(Graph)의 장점

  • 유연한 관계 표현: 다양한 유형의 관계를 효율적으로 표현할 수 있습니다.
  • 다양한 알고리즘 적용: 최단 경로, 최소 신장 트리, 네트워크 플로우 등 다양한 알고리즘을 적용할 수 있습니다.
  • 복잡한 데이터 모델링: 사회 네트워크, 지리 정보 시스템, 컴퓨터 네트워크 등 복잡한 데이터 구조를 효과적으로 모델링할 수 있습니다.

그래프(Graph)의 단점

  • 복잡성: 트리와 달리 순환이 있을 수 있어 구현과 이해가 더 복잡합니다.
  • 메모리 사용: 인접 행렬을 사용할 경우 정점 수가 많아지면 메모리 사용량이 급격히 증가할 수 있습니다.
  • 알고리즘의 복잡성: 그래프 알고리즘은 구현이 복잡하고, 시간 복잡도가 높을 수 있습니다.

 

 


 

 

그래프(Graph)는 복잡한 관계를 효과적으로 표현하고 분석할 수 있는 강력한 자료구조입니다. 소셜 네트워크 분석, 네트워크 라우팅, 추천 시스템 등 다양한 문제를 효율적으로 해결하는 데 필수적인 도구입니다. 다음 포스트에서는 힙(Heap) 자료구조에 대해 심도 있게 알아보겠습니다.

 

 

 

 

참고 자료

https://ko.wikipedia.org/wiki/%EA%B7%B8%EB%9E%98%ED%94%84_(%EC%9E%90%EB%A3%8C_%EA%B5%AC%EC%A1%B0)

 

그래프 (자료 구조) - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 3개의 꼭짓점과 3개의 변으로 이루어진 그래프. 그래프(graph)는 vertex와 edge로 구성된 한정된 자료구조를 의미한다. vertex는 정점, edge는 정점과 정점을 연결하는

ko.wikipedia.org

https://www.geeksforgeeks.org/graph-data-structure-and-algorithms/

 

Graph Algorithms - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

728x90
반응형