[C++ STL] 2.6 그래프(graph)

2024. 6. 21. 02:08·Programming Language(Sub)/C++

그래프(graph)

  • 트리는 원형 또는 순환적인 종속성을 표현할 수 없다.
    • 하나의 노드에서 다른 노드로 이동하는 경로가 하나만 존재하기 때문에
      • 도로망을 생각해보자. 특정 노드에서 다른 노드로 이동하기 위한 다양한 경로가 존재할 수 있다.
  • 이러한 경우에는 그래프(graph) 구조를 사용해야 한다

그래프 구분 - weight

  • 그래프는 노드 데이터뿐만 아니라 노드 사이의 에지 데이터도 저장해야 한다.
    • 도로망을 예로 들면 각각의 노드에 다른 어떤 노드들이 연결되어 있는지에 대한 정보를 가지고 있어야 한다.
      • 이런 방법으로 필요한 모든 노드와 에지가 있는 그래프를 만들 수 있다.
        • 이러한 그래프를 비가중 그래프(unweighted graph)라고 한다.
  • 에지에 가중치 또는 더 많은 정보를 부여할 수 있다.
    • 도로망에서 에지에 노드와 노드 사이의 거리를 저장할 수 있다.
      • 이러한 형태의 그래프를 가중 그래프(weighted graph) 라고 한다
    • 최단 거리 경로 탐색 같은 문제에서 이 그래프를 활용할 수 있다.

그래프 구분 - 방향성

  • 그래프는 방향성이 있는 그래프와 방향성이 없는 그래프로 구분할 수 있다.
  • 무방향 그래프(undirected graph)는 에지가 양방향임을 의미한다.
    • 양방향이라 함은 대칭적인, 또는 상호 교환적인 속성을 나타낸다
  • 이와 반대되는 개념으로는 방향 그래프(directed graph)가 있다.
    • 어떤 도로가 일방통행으로 제한되어 있는 경우를 생각해보자
    • 방향 그래프에서 양방향 에지를 표현하려면, A->B, B->A 두 개의 에지를 사용해야 한다
  • 무방향 그래프의 구조와 순회방법은 방향 그래프에도 적용할 수 있다.
    • 다만 그래프에 에지를 추가하는 방법만 달라짐

그래프 표현

  • 그래프는 순환적 에지를 가질 수 있고, 특정 노드에서 다른 노드로 이동하는 방법이 여러 개 있을 수 있다.
    • 따라서 각 노드를 고유하게 식별해야 한다.
    • 이를 위해 각 노드에 고유한 ID를 부여할 수 있다.
  • 그래프의 데이터를 표현하기 위해 반드시 트리의 노드 같은 구조를 사용해야 하는 것은 아니다.
    • 실제로 STL 컨테이너를 이용하여 그래프를 표현할 수 있다.

인접 행렬로 그래프 표현하기

  • 노드가 N개 있을 경우, 이 그래프는 N X N 크기의 2차원 배열로 표현할 수 있다.
  • 이 배열에서 특정 원소는 해당 원소 인덱스에 해당하는 노드 사이의 거리를 표현한다.
    • data[1][2]는 1번 노드와 2번 노드를 잇는 에지의 가중치를 나타낸다
  • 이러한 방식으로 그래프를 표현하는 방법을 인접 행렬(adjacency matrix)라고 한다
  • 두 노드 사이에 에지가 존재하지 않으면 해당 원소에 -1을 설정한다

Example) 인접 행렬로 도시 네트워크 그래프 구성하기

#include <iostream>
#include <vector>

enum class city : int
{
    MOSCOW,
    LONDON,
    SEOUL,
    SEATTLE,
    DUBAI,
    SYDNEY
};

std::ostream& operator<<(std::ostream& os, const city c)
{
    switch(c)
    {
        case city::LONDON:
            os << "런던";
            return os;
        case city::MOSCOW:
            os << "모스크바";
            return os;
        case city::SEOUL:
            os << "서울";
            return os;
        case city::SEATTLE:
            os << "시애틀";
            return os;
        case city::DUBAI:
            os << "두바이";
            return os;
        case city::SYDNEY:
            os << "시드니";
            return os;
        default:
            return os;
    }
}

struct graph
{
    std::vector<std::vector<int>> data;

    graph(int n)
    {
        data.reserve(n);
        std::vector<int> row(n);
        std::fill(row.begin(), row.end(), -1);

        for(int i = 0; i < n; i++)
        {
            data.push_back(row);
        }
    }

    // 에지 추가 함수로 두 개의 도시와 소이 사이의 거리(가중치)를 인자로 받음
    void addEdge(const city c1, const city c2, int dis)
    {
        std::cout << "에지 추가 : " << c1 << "-" << c2 << "-" << dis << std::endl;

        auto n1 = static_cast<int>(c1);
        auto n2 = static_cast<int>(c2);

        data[n1][n2] = dis;
        data[n2][n1] = dis;
    }

    // 에지 제거 함수
    void removeEdge(const city c1, const city c2)
    {
        std::cout << "에지 삭제 : " << c1 << "-" << c2 << std::endl;

        auto n1 = static_cast<int>(c1);
        auto n2 = static_cast<int>(c2);

        data[n1][n2] = -1;
        data[n2][n1] = -1;
    }
};

using namespace std;

int main()
{
    graph g(6);

    g.addEdge(city::LONDON, city::MOSCOW, 2500);
    g.addEdge(city::LONDON, city::SEOUL, 9000);
    g.addEdge(city::LONDON, city::DUBAI, 5500);
    g.addEdge(city::SEOUL, city::MOSCOW, 6600);
    g.addEdge(city::SEOUL, city::SEATTLE, 8000);
    g.addEdge(city::SEOUL, city::DUBAI, 7000);
    g.addEdge(city::SEOUL, city::SYDNEY, 8000);
    g.addEdge(city::SEATTLE, city::MOSCOW, 8400);
    g.addEdge(city::SEATTLE, city::SYDNEY, 12000);
    g.addEdge(city::DUBAI, city::SYDNEY, 1200);

    g.addEdge(city::SEATTLE, city::LONDON, 8000);
    g.removeEdge(city::SEATTLE, city::LONDON);

    return 0;
}
  • 벡터의 벡터를 이용하여 데이터를 저장했다.
  • 노드 개수가 V라면, 전체 V X V 크기의 메모리 공간을 사용하게 된다

인접 리스트로 그래프 표현하기

  • 행렬을 이용하여 그래프를 표현할 때 가장 큰 문제는, 노드 개수의 제곱에 비례하는 메모리를 사용한다는 점이다
    • 행렬을 사용할 경우 두 노드가 서로 연결되어 있지 않더라도 모든 노드 사이의 에지 정보를 저장해야 한다
  • 대신 각 노드에 직접 연결되어 있는 노드의 ID만 저장하는 방식으로 그래프를 표현할 수 있다.
    • 이러한 방식을 인접 리스트(adjacency list)라고 한다

Example) 인접 리스트로 도시 네트워크 그래프 구성하기

#include <iostream>
#include <vector>
#include <algorithm>

enum class city : int
{
    MOSCOW,
    LONDON,
    SEOUL,
    SEATTLE,
    DUBAI,
    SYDNEY
};

std::ostream& operator<<(std::ostream& os, const city c)
{
    switch(c)
    {
        case city::LONDON:
            os << "런던";
            return os;
        case city::MOSCOW:
            os << "모스크바";
            return os;
        case city::SEOUL:
            os << "서울";
            return os;
        case city::SEATTLE:
            os << "시애틀";
            return os;
        case city::DUBAI:
            os << "두바이";
            return os;
        case city::SYDNEY:
            os << "시드니";
            return os;
        default:
            return os;
    }
}

struct graph
{
    std::vector<std::vector<std::pair<int, int>>> data;

    graph(int n)
    {
        data = std::vector<std::vector<std::pair<int, int>>>(n, std::vector<std::pair<int, int>>());
    }

    // 에지 추가 함수로 두 개의 도시와 소이 사이의 거리(가중치)를 인자로 받음
    void addEdge(const city c1, const city c2, int dis)
    {
        std::cout << "에지 추가 : " << c1 << "-" << c2 << "=" << dis << std::endl;

        auto n1 = static_cast<int>(c1);
        auto n2 = static_cast<int>(c2);

        data[n1].push_back({n2, dis});
        data[n2].push_back({n1, dis});
    }

    // 에지 제거 함수
    void removeEdge(const city c1, const city c2)
    {
        std::cout << "에지 삭제 : " << c1 << "-" << c2 << std::endl;

        auto n1 = static_cast<int>(c1);
        auto n2 = static_cast<int>(c2);

        std::remove_if(data[n1].begin(), data[n1].end(), [n2](const auto& pair) {
            return pair.first == n2;
        });
        std::remove_if(data[n2].begin(), data[n2].end(), [n1](const auto& pair) {
            return pair.first == n1;
        });
    }
};

using namespace std;

int main()
{
    graph g(6);

    g.addEdge(city::LONDON, city::MOSCOW, 2500);
    g.addEdge(city::LONDON, city::SEOUL, 9000);
    g.addEdge(city::LONDON, city::DUBAI, 5500);
    g.addEdge(city::SEOUL, city::MOSCOW, 6600);
    g.addEdge(city::SEOUL, city::SEATTLE, 8000);
    g.addEdge(city::SEOUL, city::DUBAI, 7000);
    g.addEdge(city::SEOUL, city::SYDNEY, 8000);
    g.addEdge(city::SEATTLE, city::MOSCOW, 8400);
    g.addEdge(city::SEATTLE, city::SYDNEY, 12000);
    g.addEdge(city::DUBAI, city::SYDNEY, 1200);

    g.addEdge(city::SEATTLE, city::LONDON, 8000);
    g.removeEdge(city::SEATTLE, city::LONDON);

    return 0;
}
  • 각 노드에 인접한 노드를 리스트에 저장하기 때문에 인접 리스트라고 한다
  • 데이터 저장을 위해 벡터의 벡터를 사용한다.
  • 다만 안쪽 벡터의 크기가 전체 노드 개수가 아니라 해당 노드에 연결된 노드 개수와 같다.
    • 인접 리스트에 의한 그래프 표현은 전체 에지 개수 E에 비례한 크기의 메모리를 사용한다.
  • 그래프를 탐색해야 하는 경우에는 BFS(너비 우선 탐색)과 DFS(깊이 우선 탐색)과 같은 방법을 사용할 수 있다.
    • 알고리즘 파트에서 따로 다룰 예정

'Programming Language(Sub) > C++' 카테고리의 다른 글

[C++] 이동 시맨틱(Move Semantics): 이동 생성자, 이동 대입 연산자  (0) 2024.06.27
[C++ STL] 3.4 C++ 해시 테이블  (0) 2024.06.23
[C++ STL] 2.5 힙(Heap)  (0) 2024.06.21
[C++ STL] 1.8 컨테이너 어댑터(Container Adaptor)  (0) 2024.06.15
[C++ STL] 1.7 std::dequeue  (0) 2024.06.15
'Programming Language(Sub)/C++' 카테고리의 다른 글
  • [C++] 이동 시맨틱(Move Semantics): 이동 생성자, 이동 대입 연산자
  • [C++ STL] 3.4 C++ 해시 테이블
  • [C++ STL] 2.5 힙(Heap)
  • [C++ STL] 1.8 컨테이너 어댑터(Container Adaptor)
lumana
lumana
배움을 나누는 공간 https://github.com/bebeis
  • lumana
    Brute force Study
    lumana
  • 전체
    오늘
    어제
    • 분류 전체보기 (456)
      • Software Development (27)
        • Performance (0)
        • TroubleShooting (1)
        • Refactoring (0)
        • Test (8)
        • Code Style, Convetion (0)
        • DDD (0)
        • Software Engineering (18)
      • Java (71)
        • Basic (5)
        • Core (21)
        • Collection (7)
        • 멀티스레드&동시성 (13)
        • IO, Network (8)
        • Reflection, Annotation (3)
        • Modern Java(8~) (12)
        • JVM (2)
      • Spring (53)
        • Framework (12)
        • MVC (23)
        • Transaction (3)
        • AOP (11)
        • Boot (0)
        • AI (0)
      • DB Access (1)
        • Jdbc (1)
        • JdbcTemplate (0)
        • JPA (14)
        • Spring Data JPA (0)
        • QueryDSL (0)
      • Computer Science (129)
        • Data Structure (27)
        • OS (14)
        • Database (10)
        • Network (21)
        • 컴퓨터구조 (5)
        • 시스템 프로그래밍 (23)
        • Algorithm (29)
      • HTTP (8)
      • Infra (1)
        • Docker (1)
      • 프로그래밍언어론 (15)
      • Programming Language(Sub) (77)
        • Kotlin (1)
        • Python (25)
        • C++ (51)
        • JavaScript (0)
      • FE (11)
        • HTML (1)
        • CSS (9)
        • React (0)
        • Application (1)
      • Unix_Linux (0)
        • Common (0)
      • PS (13)
        • BOJ (7)
        • Tip (3)
        • 프로그래머스 (0)
        • CodeForce (0)
      • Book Review (4)
        • Clean Code (4)
      • Math (3)
        • Linear Algebra (3)
      • AI (7)
        • DL (0)
        • ML (0)
        • DA (0)
        • Concepts (7)
      • 프리코스 (4)
      • Project Review (6)
      • LegacyPosts (11)
      • 모니터 (0)
      • Diary (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
lumana
[C++ STL] 2.6 그래프(graph)
상단으로

티스토리툴바