그냥 게임개발자

인접리스트 본문

내 개인적인 공부/알고리즘

인접리스트

sudoju 2024. 4. 22. 22:43

우리는 저번 포스팅에 인접행렬을 배웠습니다.

2차원 배열이죠

그렇다면 인접리스트는 무엇일까요?

연결리스트 여러개 묶은 것이 인접리스트입니다.

 

이러한 그래프가 있습니다.

그렇다면 이것을 인접리스트로 어떻게 표현할까요?

각 인덱스의 정점을 넣는겁니다.

list0[1, 4]
list1[0, 2, 3, 4]
list2[1, 3]
list3[1, 2, 4]
list4[0, 1, 3]

이런식으로 말이죠.

 

그렇다면 진짜 코드를 짜보죠.

#include <iostream>
#include <vector>

using namespace std;

const int VERTEX = 5;

vector<int> adj[VERTEX];

int main()
{
    adj[0].push_back(1);
    adj[0].push_back(4);
    
    adj[1].push_back(0);
    adj[1].push_back(2);
    adj[1].push_back(3);
    adj[1].push_back(4);
    
    adj[2].push_back(1);
    adj[2].push_back(3);
    
    adj[3].push_back(1);
    adj[3].push_back(2);
    adj[3].push_back(4);
    
    adj[4].push_back(0);
    adj[4].push_back(1);
    adj[4].push_back(3);
}

 

이게 인접리스트입니다.

간단하죠?

그냥 인접배열은 2차원 배열로 0을 포함한 배열들인데

인접리스트는 정말 정점과 정점끼리 연결된 애들끼리만 모아놓은 리스트입니다.

 

그렇다면 이것을 확인해봅시다.

 

#include <iostream>
#include <vector>

using namespace std;

const int VERTEX = 5;

vector<int> adj[VERTEX];

int main()
{
    adj[0].push_back(1);
    adj[0].push_back(4);
    
    adj[1].push_back(0);
    adj[1].push_back(2);
    adj[1].push_back(3);
    adj[1].push_back(4);
    
    adj[2].push_back(1);
    adj[2].push_back(3);
    
    adj[3].push_back(1);
    adj[3].push_back(2);
    adj[3].push_back(4);
    
    adj[4].push_back(0);
    adj[4].push_back(1);
    adj[4].push_back(3);
    
    for (int i = 0; i < VERTEX; ++i)
    {
        cout << "list" << i << " ";
        for (int there : adj[i])
        {
            cout << there << " ";
        }
        
        cout << '\n';
    }
}

 

이런식으로 확인이 가능합니다.

결과물을 확인해보죠.

list0[1, 4]
list1[0, 2, 3, 4]
list2[1, 3]
list3[1, 2, 4]
list4[0, 1, 3]

 

어때요 결과물이 같죠?

 

근데 왜 인접 리스트인데 list를 사용하지 않고 vector를 사용했을까요.

물론 list도 가능하지만 이것은 시간 복잡도의 차이를 보면 알 수 있습니다.

 

  n번째 삽입, 삭제 마지막 요소에 삽입, 삭제 특정 요소 탐색 n번째 요소 참조
list O(1) O(1) O(n) O(n)
vector O(n) O(1) O(n) O(1)

 

여기서 보면 삽입 삭제는 list가 더 빠른데 참조는 vector가 더 빠릅니다.

list는 선형구조이며 순차접근입니다.

vector는 비선형구조이며 랜덤접근입니다.

 

그래서 사실 아무거나 사용해도 되긴한데 참조가 더 빨라서 그냥 저는 vector를 주로 사용합니다.

 

그러면 인접리스트를 통한 방문을 체크하는 재귀함수를 사용해보죠.

 

#include <iostream>
#include <vector>

using namespace std;

const int V = 10;

vector<int> adj[V];
int visited[V];

void solve(int _idx)
{
    // 방문
    visited[_idx] = 1;
    
    for (int there : adj[_idx])
    {
        if (visited[there])
            continue;
        solve(there);
    }
}

int main()
{
    adj[0].push_back(1);
    adj[0].push_back(4);
    
    adj[1].push_back(0);
    adj[1].push_back(2);
    adj[1].push_back(3);
    adj[1].push_back(4);
    
    adj[2].push_back(1);
    adj[2].push_back(3);
    
    adj[3].push_back(1);
    adj[3].push_back(2);
    adj[3].push_back(4);
    
    adj[4].push_back(0);
    adj[4].push_back(1);
    adj[4].push_back(3);
    
    for (int i = 0; i < V; ++i)
    {
        // adj 벡터의 사이즈가 0이 아니라면
        // 어떠한 값이 있다는 것 즉 연결된 정점들이 들어있다라는 뜻
        if (adj[i].size() && visited[i] == false)
            solve(i);
    }

    return 0;
}

 

끄으으읕

'내 개인적인 공부 > 알고리즘' 카테고리의 다른 글

맵과 방향 벡터  (0) 2024.04.22
인접행렬과 인접리스트의 차이  (0) 2024.04.22
인접행렬(2)  (0) 2024.04.22
인접 행렬(1)  (0) 2024.04.21
이진 트리와 이진 탐색 트리  (1) 2024.04.21