그래프는 엔티티와 그들 사이의 연결관계를 표현한다. 정점의 집합과 이들을 연결하는 간선의 집합으로 표현이 가능하며, 여러가지 표현의 방법이 있다.
간선의 개수가 적은 경우 유용. 모든 인덱스를 링크드 리스트형식으로 저장.
한 정점에서 특정 정점으로 가야 할 때 하나씩 살펴봐야 한다는 단점.

배열을 사용한 인접리스트
DFS : 한 방향으로 갈 수 있는 곳을 우선으로 끝까지 탐색. 인접 정점이 더 없는 경우 이전에 지나쳤던 곳으로 돌아와 다른 방향의 정점으로 탐색을 계속 반복.
int graph[MAX_N][MAX_N];
int stack[STACK_SIZE];
int top;
void dfs(int node) {
	bool visited[MAX_N] = {false};
	top = -1;
	stack[++top] = node; // push
  while (top != -1) {  // is_empty
	  int curr = stack[top--]; // pop
		if (!visited[curr]) {
			visited[curr] = true;
			
			for (int next = 0; next < N; ++next) {
				if (!visited[next] && graph[curr][next]) {
					stack[++top] = next; // push
				}
			}
		}
	}
}
BFS : 시작점의 인접한 정점들을 먼저 차례로 방문한다. 그 뒤에 방문했던 정점으로부터 인접한 정점들을 차례로 방문한다.
int queue[QUEUE_SIZE];
int front = -1;
int rear = -1;
void enqueue(int val) {
	if (rear >= QUEUE_SIZE - 1) { overflow; }
	else {
		queue[++rear] = val;
	}
}
void dequeue() {
	if (front == rear) { 반환할 게 없음 }
	else return queue[++front];
}
rear = (rear + 1) % QUEUE_SIZE front = (front + 1) % QUEUE_SIZEint graph[MAX_N][MAX_N];
int queue[QUEUE_SIZE];
int front;
int rear;
void bfs(int node) {
	bool visited[MAX_N] = {false};
	front = rear = -1;
	visited[node] = true;
	queue[++rear] = node;
	while(front != rear) {
		int curr = queue[++front];
		do_something_with(curr);
		for (int next = 0; next < N; ++next) {
			if (!visited[next] && graph[curr][next]) {
				visited[next] = true; // queue에 집어넣었으면 방문처리하는 것이 가능하다.
				queue[++rear] = next;
			}
		}
	}
}
알아둘 점: visited를 설정하는 타이밍이 실제 방문을 했을 때가 아닌, queue에 넣었을 때라는 것을 기억하자. dfs는 스택에 중복된 원소를 넣을 수밖에 없었는데, 그 이유는 훨씬 전에 넣었던 원소가 언제 pop 할지 알 수 없었기 때문. 하지만 bfs는 중복 원소를 넣을 필요 없이 언제 그 원소가 dequeue되는지 알 수 있다.
bfs는 가중치 없는 그래프에서는 경로의 길이를 구할 수 있다는 특징이 있다.  다른 정점을 한 번씩 방문할 때마다 dist 배열의 정점번호에 인접한 인덱스의 값 + 1 해주기만 하면 된다. dist[next] = dist[curr] + 1 이러면 dist는 시작노드로부터 모든 노드까지 이어지는 길이를 갖게 된다.