방향 그래프의 일종으로 정점을 가리키는 간선이 하나 밖에 없는 구조를 가지고 있다.
트리의 특징
이진 트리
이진 트리의 특징
- 정점 N개인 이진 트리는 최악의 경우 높이가 N이 될 수 있다.
- 정점이 N개인 포화 또는 완전 이진 트리의 높이는 log N이다.
- 높이가 h인 포화 이진트리는 -1개의 정점을 가진다.
- 일반적인 이진 트리를 사용하는 경우는 많지 않다. (이진 탐색 트리, 힙, AVL트리, 레드 블랙 트리)
이진 트리의 구현 방법
- 배열 혹은 요소에 링크가 2개 존재하는 연결 리스트로 구현할 수 있다.
이진 트리(Array)
// 0번 인덱스는 편의를 위해 비워둔다.
// Left = Index * 2
// Right = Index * 2 + 1
// Parent = floor(Index / 2)
const tree = [
undefined,
9,
3, 8,
2, 5, undefined, 7
undefined, undefined, undefined, 4
이진 트리 (Linked List)
class Node {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
class Tree {
constructor(node) {
this.root = node;
}
display() {
const queue = new Queue();
queue.enqueue(this.root);
while (queue.size) {
const currentNode = queue.dequeue();
if (currentNode.left) queue.enqueue(currentNode.left);
if (currentNode.right) queue.enqueue(currentNode.right);
}
}
}
우선순위 큐 : FIFO인 큐와 달리 우선 순위가 높은 요소가 먼저 나가는 큐
힙
- 이진 트리 형태를 가지며 우선순위가 높은 요소가 먼저 나가기 위해 요소가 삽입, 삭제 될 때 바로 정렬되는 특징을 가지고 있다.
힙의 특징
- 우선순위가 높은 요소가 먼저 나가는 특징을 가진다.
- 루트가 가장 큰 값이 되는 최대 힙(Max Heap)과 루트가 가장 작은 값이 되는 최소 힙(Min-Heap)이 있다.
힙 요소 추가 알고리즘
힙 요소 제거 알고리즘
힙 요소 추가
class MaxHeap {
constructor() {
this.heap = [null];
}
push(value) {
this.heap.push(value);
let currentIndex = this.heap.length - 1;
let parentIndex = Math.floor(currentIndex / 2);
while (parentIndex !== 0 && this.heap[parentIndex] < value) {
const temp = this.heap[parentIndex];
this.heap[parentIndex] = value;
this.heap[currentIndex] = temp;
currentIndex = parentIndex;
parentIndex = Math.floor(currentIndex / 2);
}
}
pop() {
const returnValue = this.heap[1];
const.heap[1] = this.heap.pop();
let currentIndex = 1;
let leftIndex = 2;
let rightIndex = 3;
while (
this.heap[currentIndex] < this.heap[leftIndex] ||
this.heap[currentIndex] < this.heap[rightIndex]
) {
if (this.heap[leftIndex] < this.heap[rightIndex]) {
const temp = this.heap[currentIndex];
this.heap[currentIndex] = this.heap[rightIndex];
this.heap[rightIndex] = temp;
currentIndex = rightIndex;
} else {
const temp = this.heap[currentIndex];
this.heap[currentIndex] = this.haep[leftIndex];
this.heap[leftIndex] = temp;
currentIndex = leftIndex;
}
leftIndex = currentIndex * 2;
rightIndex = currentIndex * 2 + 1;
}
return returnValue;
}
}
- 문자열을 저장하고 효율적으로 탐색하기 위한 트리 형태의 자료구조
트라이의 특징
트라이 구조
class Node {
constructor(value = "") {
this.value = value;
this.children = new Map();
}
}
class Tree {
constructor() {
this.root = new Map();
}
insert(string) {
let currentNode = this.root;
for (const char of string) {
if (!currentNode.children.has(char)) {
currentNode.children.set(
char, new Node(currentNode.value + char);
)
}
currentNode = currentNode.children.get(char);
}
}
has(string) {
let currentNode = this.root;
for (const chcar of string) {
if (!currentNode.children.has(char)) {
return false;
}
currentNode = currentNode.children.get(char);
}
return true;
}
}
요소들을 일정한 순서대로 열거하는 알고리즘
정렬의 특징
버블 정렬
- 서로 인접한 두 요소를 검사하여 정렬하는 알고리즘
- 시간 복잡도를 가진다.
선택 정렬
- 선택한 요소와 가장 우선순위가 높은 요소를 교호나하는 정렬 알고리즘
- 시간 복잡도를 가진다.
삽입 정렬
- 선택한 요소를 삽일할 수 있는 위치를 찾아 삽입하는 방식의 정렬 알고리즘
- 시간 복잡도를 가진다.
합병 정렬
- 최선과 최악이 같은 안정적인 정렬 알고리즘
- O(nlogn) 시간 복잡도를 가진다.
퀵 정렬
- 매우 빠르지만 최악의 경우가 존재하는 불안정 정렬
- O(nlogn) 시간 복잡도를 가진다.
const array = [5, 9, 10, 3, 8, 3, 2];
// 다음과 같이 그냥 정렬하면 ASCII 문자 순서로 정렬되어 우리가 원하는 숫자 크기대로 정렬되지 않는다.
array.sort();
console.log(array); // 10, 2, 3, 4, 8, 9
array.sort((a, b) => a - b) // 오름차순 정렬
console.log(array); // 2, 3, 3, 4, 8, 9, 10
array.sort((a, b) => b - a); // 내림차순 정렬
console.log(array); // 10, 9, 8, 4, 3, 3, 2
- 정렬되어 있는 요소들을 반씩 제외하며 찾는 알고리즘
- O(logn)만큼 시간복잡도가 걸린다.
이진 탐색의 특징
이진 탐색 트리
이진 탐색 트리의 문제점
Array
const array = [1, 1, 5, 124, 400, 599, 1004, 2876, 8712];
function binarySearch(array, findValue) {
let left = 0;
let right = array.length - 1;
let mid = Math.floor((left + right) / 2);
while (left < right) {
if (array[mid] === findValue) {
return mid;
}
if (array[mid] < findValue) {
left = mid + 1;
} else {
right = mid - 1;
}
mid = Math.floor((left + right) / 2);
}
return -1;
}
Binary Search Tree
class Node {
constructor(value) {
this.value = value;
this.left = null;
this.right = null;
}
}
class BinarySearchTree {
constructor() {
this.root = null;
}
insert(value) {
const newNode = new Node(value);
if (this.root === null) {
this.root = newNode;
return;
}
let currentNode = this.root;
while (currentNode !== null) {
if (currentNode.value < value) {
if (currentNode.right === null) {
currentNode.right = newNode;
break;
}
currentNode = currentNode.right;
} else {
if (currentNode.left === null) {
currentNode.left = newNode;
break;
}
currentNode = currentNode.left;
}
}
}
has(value) {
let currentNode = this.root;
while (currentNode !== null) {
if (currentNode.value ===value) {
return true;
}
if (currentNode.value < value) {
currentNode = currentNode.right;
} else {
currentNode = currentNode.left;
}
}
return false;
}
}