노드(Node)와 노드를 연결하는 간선(Edge)을 하나로 모아놓은 자료 구조이다.

정점을 연결하는 선에 방향이 있는 그래프

n개의 정점으로 구성된 방향 그래프의 최대 간선 수 : n(n-1)
정점을 연결하는 선에 방향이 없는 그래프

n개의 정점으로 구성된 무방향 그래프의 최대 간선 수 : n(n-1)/2
두 정점을 이동할때 비용이 드는 그래프

모든 정점이 간선으로 연결되어 있는 그래프

| 용어 | 설명 |
|---|---|
| 정점(vertice) | 노드(node)라고도 하며 데이터가 저장된다. |
| 간선(edge) | 링크(link)라고도 하며 노드간의 관계를 나타낸다. |
| 인접정점(adjacent vertex) | 선에 의해 연결된 정점 |
| 단순경로(simple-path) | 경로 중 반복되는 정점이 없는 경로 |
| 차수(degree) | 무방향 그래프에서 하나의 정점에 인접한 정점의 수 |
| 진출차수(out-degree) | 방향 그래프에서 외부로 향하는 간선의 수 |
| 진입차수(in-degree) | 방향 그래프에서 외부에서 들어오는 간선의 수 |
그래프의 노드를 2차원 배열로 만든 것

완성된 배열의 모양은 정점을 연결하는 노드에 다른 노드들이 인접 정점이라면 1, 아니면 0을 넣어준다.
| 장점 | 단점 |
|---|---|
| 2차원 배열 안에 모든 정점들의 간선 정보를 담기 때문에 배열의 위치를 확인하면 두 점에 대한 연결 정보를 조회할 때 O(1) 의 시간 복잡도면 가능하다. |
모든 정점에 대해 간선 정보를 대입해야 하므로 O(n²) 의 시간복잡도가 소요된다. |
| 구현이 비교적 간편하다. | 무조건 2차원 배열이 필요하기에 필요 이상의 공간이 낭비된다. |
그래프의 노드들을 리스트로 표현한것

주로 정점의 리스트 배열을 만들어 관계를 설정해줌으로써 구현한다.
| 장점 | 단점 |
|---|---|
| 정점들의 연결 정보를 탐색할 때 O(n) 의 시간이면 가능하다. (n: 간선의 갯수) |
특정 두 점이 연결되었는지 확인하려면 인접행렬에 비해 시간이 오래 걸린다. (배열보다 search 속도느림) |
| 필요한 만큼의 공간만 사용하기때문에 공간의 낭비가 적다. | 구현이 비교적 어렵다. |
class Graph {
// nodes {
// node[키형태] 1 : edge[배열형태]
// node[키형태] 2 : edge[배열형태]
// }
constructor() {
this.nodes = {};
}
addNode(node) { // 노드를 추가
this.nodes[node] = this.nodes[node] || [];
// 그래프 안에 노드를 찾는데 찾고자하는 노드가 원래 있다면 그대로두고 없다면 빈배열로 추가해줘라;
}
contains(node) { // 노드 여부 검사
// 노드안에 찾으려는 노드가 있으면 true 없으면 false
}
removeNode(node) { // 노드를 삭제
// 전체 노드 순회하면서 삭제하려는 노드는 연결해제해준다.
// 삭제하려는 노드를 그냥 delete하면 된다.
}
hasEdge(fromNode , toNode) { // 엣지의 포함여부를 확인
// 찾고자하는 노드조차 없으면 처음부터 false;
// 찾고자하는 노드에 엣지가 있으면 true, 없으면 false
}
addEdge(fromNode , toNode ) { // 엣지를 추가
// 시작노드에 끝노드를 엣지로 추가
// 끝노드에 시작노드를 엣지로 추가
}
removeEdge(fromNode, toNode) { // 엣지를 제거
// 시작노드에 들어가서 엣지(끝노드)를 찾아 제거한다.
// 끝노드에 들어가서 엣지(시작노드)를 찾아 제거한다.
}
}
노드로 구성된 계층적 자료구조로 최상위 노드(루트)를 만들고, 루트 노드의 child를 추가하고, 그 child에 또 child를 추가하는 방식으로 트리 구조를 구현할 수 있다.

| 용어 | 설명 |
|---|---|
| 루트 노드(root node) | 부모가 없는 노드, 트리는 하나의 루트 노드만을 가진다. |
| 단말 노드(leaf node) | 자식이 없는 노드, ‘말단 노드’ 또는 ‘잎 노드’라고도 부른다. |
| 내부(internal) 노드 | 단말 노드가 아닌 노드 |
| 간선(edge) | 노드를 연결하는 선 (link, branch 라고도 부름) |
| 형제(sibling) | 같은 부모를 가지는 노드 |
| 노드의 크기(size) | 자신을 포함한 모든 자손 노드의 개수 |
| 노드의 깊이(depth) | 루트에서 어떤 노드에 도달하기 위해 거쳐야 하는 간선의 수 |
| 노드의 레벨(level) | 트리의 특정 깊이를 가지는 노드의 집합 |
| 노드의 차수(degree) | 하위 트리 개수 / 간선 수 (degree) = 각 노드가 지닌 가지의 수 |
| 트리의 차수(degree of tree) | 트리의 최대 차수 |
| 트리의 높이(height) | 루트 노드에서 가장 깊숙히 있는 노드의 깊이 |
class TreeNode {
constructor(value) {
this.value = value; // 찾고자하는 value
this.children = []; // 배열
}
insertNode(value) { // 노드추가
}
contains(value) {
// 플래그 추가
// 배열안에서 순회
// 찾으려는 value가 있으면 true로 탈출
// true로 바뀜
// 배열안에 배열이 또 있으면 계속 파고들면서 찾는다.
search(this.children); // 아래 주석처리의 모양을 갖는다.
// {
// value:0,
// children: [{value:0,children: []},
// {value:0,
// children: [{value:0, children: []},
// {value:0, children: []}]
// }]
// }
}
}
| 그래프 | 트리 |
|---|---|
| 2개 이상의 경로가 가능하며 노드들 사이에 무방향/방향에서 양방향 경로를 가질 수 있다. | 두 개의 정점 사이에는 반드시 1개의 경로만을 가진다. |
| self-loop 뿐 아니라 loop/circuit 모두 가능하다. | loop나 circuit이 없으니 self-loop도 없다. |
| 루트 노드라는 개념이 없다. | 한 개의 루트 노드만이 존재하며 모든 자식노드는 한 개의 부모노드 만을 가진다. |
| 부모-자식 관계라는 개념이 없다. | 부모-자식 관계이므로 흐름은 top-bottom 아니면 bottom-top으로 이루어진다. |
| 순회는 DFS나 BFS로 이루어진다. | 순회는 Pre-order, In-order 아니면 Post-order로 이루어지며 이 3가지 모두 DFS/BFS안에 있다. |
| Cyclic 혹은 Acyclic이다. | DAG(Directed Acyclic Graphs, 사이클이 없는 방향 그래프)의 한 종류이다. |
| 방향 그래프, 무방향 그래프 | 이진트리, 이진탐색트리, AVL 트리, 힙 |
| 간선의 유무는 그래프에 따라 다르다. | 간선은 항상 정점의 개수 - 1 만큼을 가진다. |
| 네트워크 모델 | 계층 모델 |
루트 노드(혹은 다른 임의의 노드)에서 시작해서 다음 분기(branch)로 넘어가기 전에 해당 분기를 완벽하게 탐색하는 방법으로 넓게(wide) 탐색하기 전에 깊게(deep) 탐색하는 것이다.

| 장점 | 단점 |
|---|---|
| 현재 경로상의 노드들만 기억하면 되므로, 저장 공간의 수요가 비교적 적음 | 단순 검색 속도는 너비 우선 탐색(BFS) 보다 느림 |
| 목표 노드가 깊은 단계에 있는 경우 해를 빨리 구할 수 있음 | 해가 없는 경우에 빠질 가능성이 있음 (사전에 임의의 깊이를 지정한 후 탐색하고, 목표 노드를 발견하지 못할 경우 다음 경로를 탐색하도록 함) |
| 구현이 너비 우선 탐색(BFS) 보다 간단함 | 깊이 우선 탐색은 해를 구하면 탐색이 종료되므로, 구한 해가 최단 경로가 된다는 보장이 없음 (목표에 이르는 경로가 다수인 경우 구한 해가 최적이 아닐 수 있음) |
루트 노드(혹은 다른 임의의 노드)에서 시작해서 인접한 노드를 먼저 탐색하는 방법으로 깊게(deep) 탐색하기 전에 넓게(wide) 탐색하는 것이다.
주로 두 노드 사이의 최단 경로 혹은 임의의 경로를 찾고 싶을 때 이 방법을 선택한다.

| 장점 | 단점 |
|---|---|
| 노드의 수가 적고 깊이가 얕은 경우 빠르게 동작할 수 있다. | 재귀호출의 DFS와는 달리 큐에 다음에 탐색할 정점들을 저장해야 하므로 저장공간이 많이 필요하다. |
| 단순 검색 속도가 깊이 우선 탐색(DFS)보다 빠름 | 노드의 수가 늘어나면 탐색해야하는 노드 또한 많아지기에 비현실적이다. |
| 너비를 우선 탐색하기에 답이 되는 경로가 여러개인 경우에도 최단경로임을 보장한다. | |
| 최단경로가 존재한다면 어느 한 경로가 무한히 깊어진다해도 최단경로를 반드시 찾을 수 있다. |
자식노드를 최대 2개 가지며 모든 노드가 왼쪽 자식노드가 루트노드보다 작고, 오른쪽 자식노드가 루트노드보다 큰 속성이 있는 이진 트리

| 구분 | 개념도 | 순회방법 |
|---|---|---|
| 전위 순회 (Pre-order Traversal) |
![]() |
루트 -> 왼쪽 -> 오른쪽 |
| 중위 순회 (In-order Traversal) |
![]() |
왼쪽 -> 루트 -> 오른쪽 |
| 후위 순회 (Post-order Traversal) |
![]() |
왼쪽 -> 오른쪽 -> 루트 |
| 구분 | 개념도 | 설명 |
|---|---|---|
| 정 이진 트리 (Full Binary Tree) |
![]() |
모든 노드의 차수가 0 또는 2인 트리 |
| 완전 이진 트리 (Complete Binary Tree) |
![]() |
왼쪽 자식노드부터 채워지며 마지막 레벨을 제외하고는 모든 자식노드가 채워져있는 트리 |
| 포화 이진 트리 (Perfect Binary Tree) |
![]() |
모든 레벨에서 0개 혹은 2개의 자식노드를 가진 노드가 꽉 채워진 트리 |
class BinarySearchTreeNode {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
insert(value) { // 삽입
// 임시노드만들기
// 삽입하려는 값이 기존값보다 작으면
// 왼쪽이 null이면
// 왼쪽값에 임시노드할당
// null 아니면
// 왼쪽노드로 이동하여 삽입실행
// 삽입하려는 값이 기존값보다 크면
// 오른쪽이 null이면
// 오른쪽값에 임시노드할당
// null 아니면
// 오른쪽노드로 이동하여 삽입실행
}
contains(value) { // value존재 여부확인
// 들어온 값이 현재 노드의 value 라면
// true를 리턴하면된다.
// 만약에 왼쪽이나 오른쪽 둘다 null이면
// false
// 만약에 들어온값이 현재의 노드의 값보다 작으면?
// 현재값에서 왼쪽으로 이동해서 검색 실행
// 반대면?
// 현재값에서 오른쪽으로 이동해서 검색실행
}
inorder(callback) { // 중위순위(왼쪽 -> 부모 -> 오른쪽)
// 노드가 있다 = 값이있다.
// 왼쪽에 노드가 있으면 왼쪽으로 이동해서 실행
// 함수에 현재노드의 value를 넣어서 실행시키는 부분
// 오른쪽에 노드가 있으면 오른쪽 이동해서 실행
}
}
대단하십니다!!