CS/자료구조

[자료구조] #21 Graph(1)

taeyeoxn 2024. 6. 12. 00:04

• 그래프

 

임의의 두 개체의 연결 관계를 표현하는 자료구조이다.

추천 시스템, 검색 시스템, 지식 표현 및 추출에 사용될 수 있다.

 

• 그래프의 정의

 

그래프는 정점 집합과 간선 집합의 쌍으로 정의한다

 G = (V, E)

 

V(정점) : 하나의 노드는 고유한 key로 식별된다.

E(간선) : E = {(u, v) | u, v ∈ V}

 

• 그래프의 종류

 

방향 그래프

 

간선에 방향이 있어서 한쪽 방향으로만 갈 수 있다.

(u → v)로 표현한다.

(u → v) ≠ (v → u)

 

무방향 그래프

 

간선을 통해 양방향으로 갈 수 있다.

(u, v) 또는 (u ↔ v)로 표현한다.

(u, v) = (v, u)

 

가중치 그래프

 

간선에 비용(cost) 또는 가중치(weight)가 할당된 그래프이다.

 

• 그래프 용어

 

인접 정점

하나의 정점에서 간선에 의해 직접 연결된 정점들

 

G1에서 정점 A의 인접 정점 NA = {B, C, D}

 

방향 그래프에서는 방향에 따라 이웃의 개념이 다르다.


정점 차수

정점 차수 = 이웃 노드의 수


경로

그래프에서 간선을 따라 갈 수 있는 길

시작 정점이 s이고 도착 정점이 t인 하나의 경로이다. (여러 경로 존재 가능)

 

경로의 길이

경로를 구성하는데 사용된 간선의 수이다.


단순 경로

경로 중에서 반복되는 간선이 없는 경로이다.

{B, A, C, D}는 단순 경로이다.

 

(단순) 사이클

시작 정점과 도착 정점이 동일한 단순 경로이다.

{B, A, C, B}는 사이클이다.


연결 그래프

모든 정점 쌍에 대해 임의의 경로가 존재한다.

G1은 연결 그래프, G2는 비연결 그래프이다.

 

트리 (=uncycling connected graph)

사이클을 가지지 않는 연결 그래프

그래프의 특수한 형태이다.


완전 그래프

모든 정점이 연결되어 있는 무방향 그래프이다.

 

정점의 개수를 n이라고 하자.

 

하나의 정점은 n-1개의 간선을 가진다.

모든 정점에 대해 n(n-1)의 간선으로 카운트 된다.(중복 포함)

간선의 수(중복 제외) : n(n-1)/2

 

• 그래프의 ADT

 

insert_node(u) : 그래프에 key u를 가지는 노드 (노드 u)를 삽입한다.

insert_edge(u, v, w=1) : 간선 (u, v)를 삽입한다.(w = 가중치)

neighbors(u) : 노드 u에 인접하느 모든 정점의 집합을 반환한다.

remove_node(u) : 노드 u를 삭제한다.

*노드가 삭제될 때는 u와 연결된 간선도 모두 삭제한다.

 

• 그래프 표현 방법

 

인접 행렬

n x n의 정사각 행렬 A (n은 노드 수)

 

A[u][v] = 1 or true : 간선 (u, v)가 그래프에 존재하는 경우

A[u][v] = 0 or false : 존재하지 않는 경우

간선 가중치를 저장하는 경우 1대신 w로 대치한다.

 

인접 리스트

각 노드가 버킷을 가지며 버킷에 이웃 노드들을 저장한다.

 

• 그래프 클래스 구현

 

인접 행렬 기반 그래프의 구조

각 노드마다 index가 있으며 인접 행렬의 index에 대응한다.

index는 integer이고 0 ≤ index ≤ n-1의 범위를 가진다 가정한다.

인접 행렬은 2차원 배열로 표현된다.

 

사용 시나리오

일반적으로 인접 행렬 기반의 그래프를 구성할 때는 노드의 수 n을 미리 알고 정점의 수는 고정된다고 가정한다.

inset_node, remove_node 연산은 지원하지 않는데, 구현이 굉장히 복잡하기 때문이다.

 

따라서 정점은 고정이고 간선만 삽입/삭제할 때 주로 사용된다.

 

실제 코드로 살펴보도록 하자.

#include <iostream>
#include <vector>
#include <list>

using namespace std;

// 그래프의 종류를 나타내는 열거형
enum GraphType {
    UNDIRECTED,           // 무방향 그래프
    DIRECTED,             // 방향 그래프
    UNDIRECTED_WEIGHTED,  // 가중치를 가진 무방향 그래프
    DIRECTED_WEIGHTED     // 가중치를 가진 방향 그래프
};

// 인접 행렬 기반의 그래프 클래스
class AdjMatGraph {
private:
    vector<vector<double>> A;  // 인접 행렬을 저장할 2차원 벡터
    int n;                     // 그래프의 노드 수
    GraphType type;            // 그래프의 종류

public:
    // 생성자: 그래프의 종류와 노드 수를 받아 초기화
    AdjMatGraph(GraphType type, int n) {
        this->type = type;
        for (int i = 0; i < n; i++)
            A.push_back(vector<double>(n));  // n x n 크기의 2차원 벡터 초기화
        this->n = n;
    }

 

처음 생성할 때는 시간이 오래 걸린다. (O(n²) time & space)

// 간선 삽입 함수: 두 노드와 가중치를 받아 간선을 삽입
    void insert_edge(int u, int v, double weight = 1.0) {
        if ((type == UNDIRECTED || type == DIRECTED) && weight != 1.0)
            throw "Error: edge weight is not allowable...";  
            // 무방향 또는 방향 그래프에서 가중치가 1이 아닌 경우 예외 발생

        A[u][v] = weight;  // u에서 v로의 간선 설정
        if (type == UNDIRECTED || type == UNDIRECTED_WEIGHTED)
            A[v][u] = weight;  // 무방향 그래프의 경우 반대 방향 간선도 설정
            
// 간선 제거 함수: 두 노드를 받아 간선을 제거
    void remove_edge(int u, int v) {
        A[u][v] = 0.0;  // u에서 v로의 간선을 제거 (0으로 설정)
        if (type == UNDIRECTED || type == UNDIRECTED_WEIGHTED)
            A[v][u] = 0.0;  // 무방향 그래프의 경우 반대 방향 간선도 제거
    }

    // 그래프 출력 함수: 인접 행렬을 출력
    void display() {
        for (int u = 0; u < n; u++) {
            for (int v = 0; v < n; v++)
                cout << A[u][v] << " ";  // 각 노드 간의 간선 유무 출력
            cout << endl;
        }
    }

 

insert_edge와 remove_edge는 O(1) time이 소요된다.

// 주어진 노드 u의 입차 이웃 노드 목록을 반환
    vector<int> in_neighbors(int u) {
        vector<int> neighbors;
        for (int v = 0; v < this->n; v++)
            if (A[v][u] != 0.0)
                neighbors.push_back(v);  // u로 들어오는 간선이 있는 노드를 추가
        return neighbors;
    }

    // 주어진 노드 u의 이웃 노드 목록을 반환 (무방향 그래프일 때만)
    vector<int> neighbors(int u) {
        if (type == DIRECTED || type == DIRECTED_WEIGHTED)
            throw "neighbors() is not defined on directed graphs";  // 방향 그래프에서는 정의되지 않음
        return out_neighbors(u);  // 무방향 그래프에서는 out_neighbors()와 동일
    }
};

 

vector<int> out(in)_neighbors는 O(n) time이 소요된다.

int main() {
    // 무방향 그래프 생성
    AdjMatGraph graph(GraphType::UNDIRECTED, 4);
    
    // 간선 삽입
    graph.insert_edge(0, 1);
    graph.insert_edge(0, 3);
    graph.insert_edge(1, 2);
    graph.insert_edge(1, 3);
    graph.insert_edge(2, 3);
    
    // 그래프 출력
    graph.display();
    
    // 노드 0의 이웃 노드 출력
    for (int v : graph.neighbors(0))
        cout << v << " ";
    cout << endl;
    
    return 0;
}

'CS > 자료구조' 카테고리의 다른 글

[자료구조] #20 Disjoint Set  (0) 2024.06.11
[자료구조] #19 Heap(2)  (0) 2024.06.08
[자료구조] #18 Heap(1)  (0) 2024.06.06
[자료구조] #17 Binary Search Tree(2)  (0) 2024.06.02
[자료구조] #16 Binary Search Tree(1)  (0) 2024.06.02