Graph?
그래프는 정점(또는 노드)과 간선으로 이루어진 비선형 자료구조입니다. 각 간선은 두 개의 정점을 연결하며, 방향이 있을 수도(유방향 그래프) 없을 수도(무방향 그래프) 있습니다. 그래프는 소셜 네트워크, 도로 지도, 컴퓨터 네트워크 같은 네트워크를 표현할 때 자주 사용됩니다.
그래프의 구성 요소
- 정점(노드): 객체나 개체를 나타냅니다.
- 간선: 정점 간의 연결을 나타내며, 간선은 유방향 또는 무방향일 수 있습니다.
그래프는 간선의 가중치 여부에 따라 다음과 같이 나뉩니다:
- 가중치 그래프: 각 간선에 가중치(비용)가 있습니다.
- 비가중치 그래프: 모든 간선의 가중치가 동일합니다.
그래프의 사용 예시
- 소셜 네트워크: 사용자들을 정점으로, 사용자 간의 관계(친구)를 간선으로 나타냅니다.
- 도로망: 도시나 교차로를 정점으로, 도로를 간선으로 나타냅니다 (거리를 가중치로 할 수 있음).
- 인터넷/컴퓨터 네트워크: 컴퓨터나 라우터를 정점으로, 네트워크 연결을 간선으로 나타냅니다.
- 의존성 그래프: 작업 간의 의존성을 나타낼 때 사용합니다 (예: 프로젝트에서의 작업 흐름).
- 추천 시스템: 제품이나 항목을 정점으로, 유사성을 간선으로 나타냅니다.
그래프 표현 방법
그래프는 두 가지 일반적인 방식으로 표현됩니다:
- 인접 행렬: 2D 배열로,
matrix[i][j]
는 정점i
와 정점j
사이에 간선이 있는지를 나타냅니다 (가중치가 있을 경우 가중치를 나타냄). - 인접 리스트: 리스트의 리스트(또는 맵)로, 각 인덱스는 정점을 나타내고, 그 정점과 연결된 정점들의 리스트를 포함합니다.
JavaScript로 구현한 그래프
다음은 JavaScript로 인접 리스트를 사용해 구현한 간단한 그래프 예시입니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Graph {
constructor() {
this.adjacencyList = new Map();
}
// 그래프에 정점 추가
addVertex(vertex) {
if (!this.adjacencyList.has(vertex)) {
this.adjacencyList.set(vertex, []);
}
}
// 두 정점 간에 간선 추가 (무방향 그래프)
addEdge(vertex1, vertex2) {
this.adjacencyList.get(vertex1).push(vertex2);
this.adjacencyList.get(vertex2).push(vertex1);
}
// 인접 리스트 출력
printGraph() {
for (let [vertex, edges] of this.adjacencyList) {
console.log(`${vertex} -> ${edges.join(", ")}`);
}
}
// 깊이 우선 탐색(DFS)
dfs(start) {
let visited = new Set();
this._dfsHelper(start, visited);
}
_dfsHelper(vertex, visited) {
if (visited.has(vertex)) return;
visited.add(vertex);
console.log(vertex);
for (let neighbor of this.adjacencyList.get(vertex)) {
this._dfsHelper(neighbor, visited);
}
}
}
// 사용 예시
let graph = new Graph();
graph.addVertex("A");
graph.addVertex("B");
graph.addVertex("C");
graph.addEdge("A", "B");
graph.addEdge("A", "C");
graph.addEdge("B", "C");
graph.printGraph(); // 출력: A -> B, C, B -> A, C, C -> A, B
graph.dfs("A"); // 출력: A B C
설명:
- 그래프는 JavaScript의
Map
을 사용하여 인접 리스트로 구현되었습니다. addVertex
메서드는 그래프에 새 정점을 추가합니다.addEdge
메서드는 두 정점을 연결합니다(무방향 그래프의 경우 두 방향 모두 추가).- 깊이 우선 탐색(DFS)을 재귀적으로 구현했습니다.
Rust로 구현한 그래프
다음은 Rust에서 인접 리스트를 사용해 구현한 무방향 그래프 예시입니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use std::collections::{HashMap, HashSet};
struct Graph {
adjacency_list: HashMap<String, Vec<String>>,
}
impl Graph {
// 새로운 그래프 생성
fn new() -> Graph {
Graph {
adjacency_list: HashMap::new(),
}
}
// 그래프에 정점 추가
fn add_vertex(&mut self, vertex: String) {
self.adjacency_list.entry(vertex).or_insert(Vec::new());
}
// 두 정점 간에 간선 추가 (무방향 그래프)
fn add_edge(&mut self, vertex1: String, vertex2: String) {
self.adjacency_list.get_mut(&vertex1).unwrap().push(vertex2.clone());
self.adjacency_list.get_mut(&vertex2).unwrap().push(vertex1.clone());
}
// 인접 리스트 출력
fn print_graph(&self) {
for (vertex, edges) in &self.adjacency_list {
println!("{} -> {:?}", vertex, edges);
}
}
// 깊이 우선 탐색(DFS)
fn dfs(&self, start: &String) {
let mut visited = HashSet::new();
self._dfs_helper(start, &mut visited);
}
fn _dfs_helper(&self, vertex: &String, visited: &mut HashSet<String>) {
if visited.contains(vertex) {
return;
}
visited.insert(vertex.clone());
println!("{}", vertex);
for neighbor in &self.adjacency_list[vertex] {
self._dfs_helper(neighbor, visited);
}
}
}
fn main() {
let mut graph = Graph::new();
graph.add_vertex("A".to_string());
graph.add_vertex("B".to_string());
graph.add_vertex("C".to_string());
graph.add_edge("A".to_string(), "B".to_string());
graph.add_edge("A".to_string(), "C".to_string());
graph.add_edge("B".to_string(), "C".to_string());
graph.print_graph(); // 출력: A -> ["B", "C"], B -> ["A", "C"], C -> ["A", "B"]
graph.dfs(&"A".to_string()); // 출력: A B C
}
설명:
- 그래프는
HashMap
을 사용하여 인접 리스트로 구현되었습니다. 각 정점은 키로, 연결된 정점들의 리스트는 값으로 저장됩니다. add_vertex
메서드는 새 정점을 추가하고,add_edge
메서드는 두 정점 간에 무방향 간선을 추가합니다.- DFS 탐색은 재귀를 통해 간단하게 구현되었습니다.
핵심 요점:
- 그래프는 객체 간의 관계 또는 연결을 모델링하는 강력한 자료구조입니다.
- 소셜 네트워크, 네트워크 경로, 경로 문제 해결 등 현실 세계의 많은 문제에 널리 사용됩니다.
- DFS, BFS, 다익스트라, 크루스칼 등 다양한 알고리즘을 그래프에서 사용하여 최단 경로, 연결성 등을 해결할 수 있습니다.
Graph ?
A graph is a non-linear data structure that consists of nodes (or vertices) and edges. Each edge connects two vertices and can be directed (having a direction) or undirected (no direction). Graphs are commonly used to represent networks like social networks, roadmaps, or computer networks.
Components of a Graph
- Vertices (Nodes): These represent entities or objects.
- Edges: These represent the connections between vertices. Edges can be directed (one-way) or undirected (two-way).
Graphs can also be classified based on edge weights:
- Weighted Graph: Each edge has a weight (cost) associated with it.
- Unweighted Graph: All edges have equal weight.
Use Cases of Graphs
- Social Networks: Users as vertices, and connections (friendships) as edges.
- Road Networks: Cities or intersections as vertices, roads as edges (with distances as weights in a weighted graph).
- Internet/Computer Networks: Computers/routers as vertices, and network connections as edges.
- Dependency Graphs: Represent dependencies between tasks (e.g., in a project).
- Recommendation Systems: Products or items are vertices, with edges representing similarities between them.
Graph Representation
Graphs can be represented in two common ways:
- Adjacency Matrix: A 2D array where
matrix[i][j]
represents the presence (and possibly weight) of an edge between vertexi
and vertexj
. - Adjacency List: A list of lists (or map) where each index represents a vertex, and each entry contains a list of adjacent vertices (or edges).
Graph Implementation in JavaScript
Here’s a simple implementation of a graph using an adjacency list in JavaScript:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Graph {
constructor() {
this.adjacencyList = new Map();
}
// Add a vertex to the graph
addVertex(vertex) {
if (!this.adjacencyList.has(vertex)) {
this.adjacencyList.set(vertex, []);
}
}
// Add an edge between two vertices (for undirected graph)
addEdge(vertex1, vertex2) {
this.adjacencyList.get(vertex1).push(vertex2);
this.adjacencyList.get(vertex2).push(vertex1);
}
// Print the adjacency list
printGraph() {
for (let [vertex, edges] of this.adjacencyList) {
console.log(`${vertex} -> ${edges.join(", ")}`);
}
}
// Depth-First Search (DFS)
dfs(start) {
let visited = new Set();
this._dfsHelper(start, visited);
}
_dfsHelper(vertex, visited) {
if (visited.has(vertex)) return;
visited.add(vertex);
console.log(vertex);
for (let neighbor of this.adjacencyList.get(vertex)) {
this._dfsHelper(neighbor, visited);
}
}
}
// Example Usage
let graph = new Graph();
graph.addVertex("A");
graph.addVertex("B");
graph.addVertex("C");
graph.addEdge("A", "B");
graph.addEdge("A", "C");
graph.addEdge("B", "C");
graph.printGraph(); // Output: A -> B, C, B -> A, C, C -> A, B
graph.dfs("A"); // Output: A B C
Explanation:
- The graph is implemented using an adjacency list with
Map
in JavaScript. - The
addVertex
method adds a new vertex to the graph. - The
addEdge
method connects two vertices (for an undirected graph, it adds both directions). - Depth-First Search (DFS) is implemented as a traversal algorithm.
Graph Implementation in Rust
Below is a similar implementation of an undirected graph using an adjacency list in Rust:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use std::collections::{HashMap, HashSet};
struct Graph {
adjacency_list: HashMap<String, Vec<String>>,
}
impl Graph {
// Create a new graph
fn new() -> Graph {
Graph {
adjacency_list: HashMap::new(),
}
}
// Add a vertex to the graph
fn add_vertex(&mut self, vertex: String) {
self.adjacency_list.entry(vertex).or_insert(Vec::new());
}
// Add an edge between two vertices (undirected)
fn add_edge(&mut self, vertex1: String, vertex2: String) {
self.adjacency_list.get_mut(&vertex1).unwrap().push(vertex2.clone());
self.adjacency_list.get_mut(&vertex2).unwrap().push(vertex1.clone());
}
// Print the graph's adjacency list
fn print_graph(&self) {
for (vertex, edges) in &self.adjacency_list {
println!("{} -> {:?}", vertex, edges);
}
}
// Depth-First Search (DFS)
fn dfs(&self, start: &String) {
let mut visited = HashSet::new();
self._dfs_helper(start, &mut visited);
}
fn _dfs_helper(&self, vertex: &String, visited: &mut HashSet<String>) {
if visited.contains(vertex) {
return;
}
visited.insert(vertex.clone());
println!("{}", vertex);
for neighbor in &self.adjacency_list[vertex] {
self._dfs_helper(neighbor, visited);
}
}
}
fn main() {
let mut graph = Graph::new();
graph.add_vertex("A".to_string());
graph.add_vertex("B".to_string());
graph.add_vertex("C".to_string());
graph.add_edge("A".to_string(), "B".to_string());
graph.add_edge("A".to_string(), "C".to_string());
graph.add_edge("B".to_string(), "C".to_string());
graph.print_graph(); // Output: A -> ["B", "C"], B -> ["A", "C"], C -> ["A", "B"]
graph.dfs(&"A".to_string()); // Output: A B C
}
Explanation:
- The graph is represented using a
HashMap
where each key is a vertex and the value is a list of its neighbors. - The
add_vertex
method adds a vertex, andadd_edge
adds an undirected edge between two vertices. - A simple DFS traversal is implemented using recursion.
Key Points:
- A graph is a powerful structure used to model relationships or connections between objects.
- It is widely used in real-world applications such as networking, social graphs, and routing problems.
- Various algorithms, like DFS, BFS, Dijkstra’s, and Kruskal’s, are used with graphs to solve problems like shortest paths, connectivity, and more.