비선형구조의 탐색
비선형 구조란, i번째 원소를 탐색한 다음 그 원소와 연결된 다른 원소를 탐색하려고 할 때, 다음에 탐색할 수 있는 원소가 여러 개 존재하는 구조. 일반적으로 트리나 그래프 형태로 자료를 구성할 수 있을 때 해당됩니다.
이러한 트리나 그래프 형태의 모든 정점(값/데이터)을 탐색하는 것을 비선형구조의 탐색이라고 생각할 수 있습니다.
비선형구조는 탐색해야할 데이터가 순차적으로 구성되어있지 않습니다. 단순한 반복으로 탐색할 수가 없기 때문에 스택(stack)이나 큐(queue)와 같은 자료구조를 활용해 탐색할 방법과 순서를 만들어 탐색하는 것이 일반적입니다.
비선형구조의 대표적인 예 : 트리, 그래프
데이터가 저장되어있는 곳: 정점(vertex) / 노드(node)
데이터들을 연결시키는 사이의 선: 간선(edge) / 링크(link)
데이터들을 연결시키는 간선들은 화살표의 유무에 따라 양/단방향으로 표현되고, 간선들에는 어떤 가중치(weight)가 있을수도 있습니다.
어떤 임의의 정점 s에서 다른 정점 t까지 이동한다고 가정할 때, s에서 t로 이동하는데 사용한 간선들의 순서로 만들어지는 집합을 경로(path)라고 하며, 회로(cycle)는 그 경로에 순환이 생기는 경우를 의미합니다.
자기자신을 연결하는 간선: 자기간선(loop)
동일한 다른 정점과 여러 간선이 연결되는 경우: 다중간선(multi edge)
한 정점에서 다른 정점으로 연결되는 모든 간선의 수: 차수(degree)
그래프의 표현은 실제 세상에서의 많은 문제들을 간단하면서도 효과적으로 표현할 수 있는 방법으로, 프로그래밍을 활용한 문제해결에서 매우 자주 활용됩니다.
그래프 형태로 표현되는 문제 상황 또는 데이터 구조
각 정점들의 연결관계를 표현하기 위해 배열 구조를 사용하는 인접행렬(adjacency matrix)이나 리ㅏ스트 구조를 사용하는 인접리스트(adjacency list)로 구현할 수 있습니다.
입력 형식 |
입력 데이터의 예 |
첫번째 줄에 정점의 수 n과 간선의 수 m이 공백으로 구분되어 입력된다. 두번째 줄부터 m개의 줄에 걸쳐서 간선으로 연결된 두 정점의 번호와 가중치가 공백으로 구분되어 입력된다. |
7 11 1 2 47 1 3 69 2 4 57 2 5 124 3 4 37 3 5 59 3 6 86 4 6 27 4 7 94 5 7 21 6 7 40 |
1. 인접행렬: 배열 구조의 정점의 데이터와 연결관계를 저장하는 가장 간단한 그래프 구현 방법
<일반 연결 관계>
|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
1 |
|
1 |
1 |
|
|
|
|
2 |
1 |
|
1 |
1 |
|
|
|
3 |
1 |
|
1 |
1 |
1 |
|
|
4 |
|
1 |
1 |
|
|
1 |
1 |
5 |
|
1 |
1 |
|
|
|
1 |
6 |
|
|
1 |
1 |
|
|
1 |
7 |
|
|
|
1 |
1 |
1 |
|
<가중치 연결 관계>
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1 |
| 47 | 69 |
|
|
|
|
2 | 47 |
| 57 | 124 |
|
| |
3 | 69 |
| 37 | 59 | 86 |
| |
4 |
| 57 | 37 |
|
| 27 | 94 |
5 |
| 124 | 59 |
|
|
| 21 |
6 |
|
| 86 | 27 |
|
| 40 |
7 |
|
|
| 94 | 21 | 40 |
|
2차원 배열을 이용해 그래프를 인접행렬의 형태로 저장하는 코드의 예
#include <stdio.h> int n, m, G[101][101]; int main() { scanf("%d %d", &n, &m); for(int i=0; i<m; i++) { int a, b, w; scanf("%d %d %d", &a, &b, &w); G[a][b] = G[b][a] = w; } }
3: 그래프를 저장하기 위한 G[][]준비
12: 양방향 그래프인 경우의 저장, 가중치 w를 양방향으로 기록
2. 인접리스트: 어떤 정점에 연결된 다른 정점만, 리스트로 연결하는 그래프 구현 방법.
STL 라이브러리의 <vector>에 정의되어있는 std::vector 구조를 활용하는 것이 매우 편리
#include <vector> std::vector<int> G[101]; int main() { ... G[i].push_back(x); ... }
1 |
2 |
47 |
3 |
69 |
|
|
|
|
2 |
1 |
47 |
4 |
57 |
5 |
124 |
||
3 |
1 |
69 |
4 |
37 |
5 |
59 |
6 |
86 |
4 |
2 |
57 |
3 |
37 |
6 |
27 |
7 |
94 |
5 |
2 |
124 |
3 |
59 |
7 |
21 |
|
|
6 |
3 |
86 |
4 |
27 |
7 |
40 |
|
|
7 |
4 |
94 |
5 |
21 |
6 |
40 |
|
|
인접리스트로 구현하면 인접행렬보다 탐색 시간을 줄일 수 있습니다. 인접행렬은 실제로 연결되지 않은 부분까지 저장해둬야하고 전체 할당된 행렬전체 범위를 탐색해야만 합니다. 하지만 인접리스트의 경우에는 연결된 부분밖에 없기때문에 이부분만 연결되 순서대로 따라가면서 탐색하면 됩니다.
인접행렬의 경우 : O(v*e)
인접리스트의 경우 : O(v+e)
비선형 구조는 어떤 데이터에 연결되어 이동할 수 있는 경우가 2개 이상으로 만들어질 수 있는 구조를 말하며 대표적으로 트리, 그래프가 있습니다. 그래프 구조를 저장하기 위해서는 배열을 이용한 인접행렬 방법이나 리스트(벡터 등)을 이용한 인접리스트 방법을 사용할 수 있습니다.
참고로 트리구조는 1차원배열에 순차적으로 저장하는 방법을 사용할 수 있습니다. 너비우선 탐색 저장 큐방식 처럼 저장하면 됩니다. 어떠한 트리에서 노드가 n번일때, 왼쪽 자식노드는 2*n 오른쪽 자식노드는 2*n + 1의 관계를 가지게 되기 때문입니다.
'프로그래밍(TA, AA) > 알고리즘' 카테고리의 다른 글
[수학] 수학의 기본 정석 정리 (0) | 2017.06.09 |
---|---|
[수학] 인수분해 정리 (0) | 2017.06.08 |
[알고리즘] 깊이우선 탐색, 너비우선 탐색 (0) | 2017.05.19 |
[알고리즘] Lower bound, Upper bound (1) | 2017.05.18 |
[알고리즘] 정보과학과 문제, 선형구조의 탐색 (0) | 2017.05.18 |