1ilsang

Developer who will be a Legend

Data Structure in JavaScript

2019-09-15 1ilsangData-Structure & Algorithm

μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ 자료ꡬ쑰

cover image

μ‹œμž‘ν•˜λ©°

β€‚λΉ„μ§€λ‹ˆμŠ€ 둜직이 점점더 λ°±λ‹¨μ—μ„œ ν”„λ‘ νŠΈλ‘œ 이동함에 따라 ν”„λ‘ νŠΈ μ—”μ§€λ‹ˆμ–΄λ§ 전문지식이 λ”μš± μ€‘μš”ν•΄μ§€κ²Œ λ˜μ—ˆλ‹€.
ν”„λ‘ νŠΈ μ—”μ§€λ‹ˆμ–΄λ‘œμ¨, μš°λ¦¬λŠ” λ¦¬μ—‘νŠΈ 같은 μƒμ‚°μ„±μžˆλŠ” λ·° λΌμ΄λΈŒλŸ¬λ¦¬μ— μ˜μ‘΄ν•œλ‹€. λ·° λΌμ΄λΈŒλŸ¬λ¦¬λ“€μ€ κ²°κ΅­ 데이터 관리λ₯Ό μœ„ν•΄ λ¦¬λ•μŠ€μ™€ 같은 μƒνƒœ λΌμ΄λΈŒλŸ¬λ¦¬μ— μ˜μ‘΄ν•œλ‹€.

β€‚λ¦¬μ—‘νŠΈμ™€ λ¦¬λ•μŠ€λŠ” UI μ—…λ°μ΄νŠΈκ°€ 데이터 변경에 λ°˜μ‘ν•˜λŠ” λ°˜μ‘ν˜• ν”„λ‘œκ·Έλž˜λ° νŒ¨λŸ¬λ‹€μž„μ— ν•¨κ»˜ κ°€μž…ν–ˆλ‹€. 점점 더, λ°±μ—”λ“œλŠ” λ‹¨μˆœνžˆ API μ„œλ²„λ‘œ μž‘λ™ν•˜μ—¬ 데이터λ₯Ό κ²€μƒ‰ν•˜κ³  μ—…λ°μ΄νŠΈν•˜λŠ” μ—”λ“œν¬μΈνŠΈλ§Œ μ œκ³΅ν•œλ‹€. 사싀상, λ°±μ—”λ“œλŠ” 단지 λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό ν”„λ‘ νŠΈλ‘œ β€œν¬μ›Œλ”©β€ ν•˜κ³ , ν”„λ‘ νŠΈ μ—”μ§€λ‹ˆμ–΄κ°€ λͺ¨λ“  λ‘œμ§μ„ μ»¨νŠΈλ‘€ν•˜κΈΈ κΈ°λŒ€ν•œλ‹€. λ§ˆμ΄ν¬λ‘œμ„œλΉ„μŠ€μ™€ GraphQL의 인기 μƒμŠΉμ΄ μ΄λŸ¬ν•œ 증가 μΆ”μ„Έλ₯Ό 증λͺ…ν•œλ‹€.

β€‚μ΄μ œ ν”„λ‘ νŠΈ μ—”μ§€λ‹ˆμ–΄λ“€μ€ HTMLκ³Ό CSS에 λŒ€ν•œ 미적 μ΄ν•΄λΏλ§Œ μ•„λ‹ˆλΌ, μžλ°”μŠ€ν¬λ¦½νŠΈλ₯Ό μ•„μ£Ό 잘 닀루어야 ν•œλ‹€. ν΄λΌμ΄μ–ΈνŠΈμ˜ λ°μ΄ν„°μŠ€ν† μ–΄κ°€ μ„œλ²„μ˜ λ°μ΄ν„°λ² μ΄μŠ€μ— β€œλ³΅μ œβ€ λ˜λ©΄μ„œ, κ΄€μš© μžλ£Œκ΅¬μ‘°μ— λŒ€ν•œ μΉœλ°€ν•œ 지식이 쀑심적 역할을 ν•˜κ²Œ λ˜μ—ˆλ‹€. μ‹€μ œλ‘œ μ—”μ§€λ‹ˆμ–΄μ˜ κ²½ν—˜ μˆ˜μ€€μ€ νŠΉμ • 자료ꡬ쑰λ₯Ό μ–Έμ œ, μ™œ μ‚¬μš©ν•΄μ•Όν•˜λŠ”μ§€ ꡬ별할 수 μžˆλŠ” λŠ₯λ ₯으둜 μœ μΆ”ν• μˆ˜μžˆλ‹€.

μ΄ˆκΈ‰ ν”„λ‘œκ·Έλž˜λ¨ΈλŠ” μ½”λ“œμ— λŒ€ν•΄ κ±±μ •ν•˜μ§€λ§Œ κ³ κΈ‰ ν”„λ‘œκ·Έλž˜λ¨ΈλŠ” μžλ£Œκ΅¬μ‘°μ™€ κ·Έλ“€μ˜ 관계λ₯Ό κ±±μ •ν•œλ‹€.

- Linux, Git μ°½μ‹œμž λ¦¬λˆ„μŠ€ ν† λ°œμ¦ˆ

큰 ν‹€λ‘œ, 기본적인 3κ°€μ§€ μœ ν˜•μ˜ μžλ£Œκ΅¬μ‘°κ°€ μžˆλ‹€.
  1. Array: Stack κ³Ό Queue λŠ” 였직 μ•„μ΄ν…œμ„ μ–΄λ–»κ²Œ μ‚½μž…/μ‚­μ œ ν•˜λŠ”κ°€ 만 λ‹€λ₯Έ λ°°μ—΄ 같은 μžλ£Œκ΅¬μ‘°λ“€μ΄λ‹€.
  2. LinkedList: λ§ν¬λ“œ 리슀트, 트리 그리고 κ·Έλž˜ν”„λŠ” λ‹€λ₯Έ λ…Έλ“œμ— λŒ€ν•œ μ°Έμ‘°λ₯Ό μœ μ§€ν•˜λŠ” λ…Έλ“œλ‘œ 이루어진 ꡬ쑰체이닀.
  3. Hash: ν•΄μ‰¬ν…Œμ΄λΈ”μ€ 데이터λ₯Ό μ €μž₯ν•˜κ³  μ°ΎκΈ° μœ„ν•΄ 해쉬 ν•¨μˆ˜μ— μ˜μ‘΄ν•œλ‹€.

 볡합성 μΈ‘λ©΄μ—μ„œ, Stackκ³Ό QueueλŠ” κ°€μž₯ κ°„λ‹¨ν•˜λ©° Linked List둜 ꡬ성할 수 μžˆλ‹€. Tree 와 Graph λŠ” λ§ν¬λ“œ 리슀트의 κ°œλ…μ„ ν™•μž₯μ‹œν‚¨ 것이기 λ•Œλ¬Έμ— κ°€μž₯ λ³΅μž‘ν•˜λ‹€. Hash Table 은 이런 μžλ£Œκ΅¬μ‘°λ“€μ„ ν™œμš©ν•˜μ—¬ μ•ˆμ •μ μœΌλ‘œ λŒμ•„κ°€μ•Όν•œλ‹€.
β€‚νš¨μœ¨μ„± μΈ‘λ©΄μ—μ„œ, Linked ListλŠ” λ°μ΄ν„°μ˜ 기둝 및 μ €μž₯에 κ°€μž₯ μ΅œμ ν™”λ˜μ–΄μžˆμœΌλ©° Hash Table은 λ°μ΄ν„°μ˜ 탐색과 검색에 κ°€μž₯ νš¨κ³Όμ μ΄λ‹€.

이유λ₯Ό μ„€λͺ…ν•˜κ³  μ‹œκΈ°λ₯Ό μ„€λͺ…ν•˜κΈ° μœ„ν•΄, 이 글은 μœ„μ—μ„œ μ–ΈκΈ‰ν•œ μ˜μ‘΄μ„±μ˜ μˆœμ„œλ₯Ό λ”°λ₯Ό 것이닀. μ‹œμž‘ν•΄λ³΄μž!

Stack

β€‚μ˜μ‹¬μ˜ 여지없이 μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ κ°€μž₯ μ€‘μš”ν•œ Stack은 μš°λ¦¬κ°€ μ‹€ν–‰ν•˜λŠ” νŽ‘μ…˜μ˜ μŠ€μ½”ν”„λ₯Ό ν‘Έμ‰¬ν•˜λŠ” call stack 일 것이닀. ν”„λ‘œκ·Έλž˜λ°μ μœΌλ‘œ μŠ€νƒμ€ λ‹¨μˆœνžˆ push와 pop의 두 κ°€μ§€ 정책을 κ°€μ§„ 배열이닀. PushλŠ” λ°°μ—΄μ˜ 탑에 μ›μ†Œλ₯Ό μΆ”κ°€ν•œλ‹€. 반면 Pop은 같은 μœ„μΉ˜μ—μ„œ μ›μ†Œλ₯Ό μ œκ±°ν•œλ‹€. 즉, Stacks 은 β€œLast In, First Out” 정책을 λ”°λ₯Έλ‹€(LIFO).

β€‚μ•„λž˜λŠ” Stack μ½”λ“œμ˜ μ˜ˆλ‹€. μš°λ¦¬λŠ” μŠ€νƒμ˜ μˆœμ„œλ₯Ό 뒀집을 수 μžˆλ‹€λŠ” 점에 μœ μ˜ν•΄μ•Όν•œλ‹€. 바텀이 νƒ‘μœΌλ‘œκ°€κ³  탑이 바텀에 κ°„λ‹€λŠ” λœ»μ΄λ‹€.(μ—­μž - μ €μžλŠ” μŠ€νƒμ˜ reverse 도 μ—Όλ‘ν•œλ“―.)

class Stack {
    constructor(...items) {
        this.reverse = false;
        this.stack = [...items];
    }

    push(...items) {
        return this.reverse ? this.stack.unshift(...items) : this.stack.push(...items);
    }

    pop(...items) {
        return this.reverse ? this.stack.shift() : this.stack.pop();
    }
}

(μ—­μž - λ³Έ 글은 β€˜μžλ°”μŠ€ν¬λ¦½νŠΈ λ‚΄λΆ€μ—μ„œ μ“°μ΄λŠ” μžλ£Œκ΅¬μ‘°β€™ κ°œλ…μ„ μ΄ν•΄ν•˜λŠ” ν¬μŠ€ν„°λΌ API κ΅¬ν˜„κΉŒμ§€λŠ” 닀루지 μ•ŠλŠ”λ‹€.)

β€‚μ•„μ΄ν…œμ˜ μˆ«μžκ°€ 증가함에 따라, push/pop은 unshift/shift보닀 더 μ„±λŠ₯이 μ’‹μ•„μ§€κ²Œ λœλ‹€. κ·Έ μ΄μœ λŠ” ν›„μžμ˜ 경우 λͺ¨λ“  μ•„μ΄ν…œμ΄ λ‹€μ‹œ 인덱싱을 ν•΄μ•Όν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.(push/pop 은 μ΅œμƒλ‹¨ index 만 λ³€ν•˜κΈ° λ•Œλ¬Έ)

Queue

β€‚μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” non-blocking을 μ§€μ›ν•˜λŠ” event-driven λ°©μ‹μ˜ ν”„λ‘œκ·Έλž˜λ° 언어이닀. λ‚΄λΆ€μ μœΌλ‘œ, λΈŒλΌμš°μ €λŠ” ν•˜λ‚˜μ˜ μ“°λ ˆλ“œλ§Œ κ΄€λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— λΈŒλΌμš°μ €λŠ” μžλ°”μŠ€ν¬λ¦½νŠΈ μ½”λ“œ 전체λ₯Ό μ‹€ν–‰ν•˜κΈ° μœ„ν•΄ 였직 ν•˜λ‚˜μ˜ μ“°λ ˆλ“œλ§Œ κ΄€λ¦¬ν•˜λ©°, event queueλ₯Ό μ‚¬μš©ν•˜μ—¬ listenerλ₯Ό 등둝(enqueue) ν•˜κ³  event loop둜 λ“±λ‘λœ event듀을 listen ν•œλ‹€.
 싱글 μ“°λ ˆλ“œ ν™˜κ²½μ—μ„œ 비동기λ₯Ό μ§€μ›ν•˜κΈ° μœ„ν•΄(CPU μžμ›κ³Ό μ›Ή κ²½ν—˜μ˜ ν–₯상을 μœ„ν•΄μ„œ), listener function듀은 call stack이 λΉ„μ–΄μžˆμ„ λ•Œλ§Œ 제거(dequeue) 되며 μ‹€ν–‰λœλ‹€. Promise 듀은 이 event-driven architecture에 μ˜μ‘΄ν•˜μ—¬ λ‹€λ₯Έ μž‘μ—…μ„ μ°¨λ‹¨ν•˜μ§€ μ•ŠλŠ” 비동기 μ½”λ“œμ˜ β€œλ™κΈ°μ‹β€ 싀행을 ν—ˆλ½ν•œλ‹€.

β€‚ν”„λ‘œκ·Έλž˜λ°μ μœΌλ‘œ, QueueλŠ” λ‘κ°œμ˜ λ‹¨μˆœν•œ μž‘μ—…(unshift and pop)이 μžˆλŠ” 배열에 λΆˆκ³Όν•˜λ‹€. UnshiftλŠ” μ•„μ΄ν…œλ“€μ„ λ°°μ—΄μ˜ 끝 에 등둝(enqueue) ν•˜λŠ” 반면, Pop은 λ°°μ—΄μ˜ μ‹œμž‘ μ—μ„œ 제거(dequeue)ν•œλ‹€. 즉, QueueλŠ” β€œFirst In, First Out”(FIFO) 정책을 λ”°λ₯Έλ‹€. λ§Œμ•½ λ°©ν–₯이 λ’€μ§‘μ–΄μ§€λ©΄, μš°λ¦¬λŠ” unshift와 pop을 각각 push와 shift둜 λŒ€μΉ˜ν•  수 μžˆλ‹€.

μ•„λž˜λŠ” Queue μ½”λ“œμ˜ μ˜ˆμ΄λ‹€.

class Queue {
    constructor(...items) {
        this.reverse = false;
        this.queue = [...items];
    }

    enqueue(...items) {
        return this.reverse ? this.queue.push(...items) : this.queue.unshift(...items);
    }

    dequeue() {
        return this.reverse ? this.queue.shift() : this.queue.pop();
    }
}

Linked List

 Linked List λŠ” 배열같이 데이터듀을 순차적으둜 μ €μž₯ν•œλ‹€. λ§ν¬λ“œ λ¦¬μŠ€νŠΈλŠ” 인덱슀λ₯Ό μœ μ§€ν•˜λŠ” λŒ€μ‹  λ‹€λ₯Έ μ›μ†Œλ₯Ό κ°€λ¦¬ν‚€λŠ” 포인터 λ₯Ό κ°€μ§€κ³  μžˆλ‹€. 첫 번째 λ…Έλ“œλŠ” head라고 λΆˆλ¦¬λŠ” 반면 λ§ˆμ§€λ§‰ λ…Έλ“œλŠ” tail이라 λΆˆλ¦°λ‹€. singly-linked list μ—μ„œ 각 λ…Έλ“œλŠ” λ‹€μŒλ…Έλ“œλ₯Ό κ°€λ¦¬ν‚€λŠ” 였직 ν•˜λ‚˜μ˜ ν¬μΈν„°λ§Œ κ°€μ§€κ³  μžˆλ‹€. μ—¬κΈ°μ„œ headλŠ” μš°λ¦¬κ°€ 리슀트의 λ‚˜λ¨Έμ§€λ“€λ‘œ κ±Έμ–΄κ°€κΈ° μœ„ν•œ μ‹œμž‘μ μ΄λ‹€. doubly-linked list의 경우 이전(previous) λ…Έλ“œλ‘œ κ°€λŠ” ν¬μΈνŠΈλ„ κ°€μ§€κ³  μžˆλ‹€. κ·ΈλŸ¬λ―€λ‘œ μš°λ¦¬λŠ” tailμ—μ„œ μ‹œμž‘ν•΄ β€œλ’€μ—μ„œβ€ μ•žμœΌλ‘œ 갈 수 μžˆλ‹€.

β€‚λ§ν¬λ“œ λ¦¬μŠ€νŠΈλŠ” μ‚½μž…κ³Ό μ‚­μ œμ‹œ μƒμˆ˜μ‹œκ°„ O(1)을 κ°€μ§„λ‹€ μ™œλƒν•˜λ©΄ λ‹¨μˆœνžˆ ν¬μΈν„°λ“€λ§Œ λ°”κΎΈλ©΄ 되기 λ•Œλ¬Έμ΄λ‹€. λ°°μ—΄μ—μ„œ 같은 μž‘λ™μ„ ν•˜κΈ° μœ„ν•΄μ„  μ„ ν˜• μ‹œκ°„ O(N)이 ν•„μš”ν•˜λ‹€ μ™œλƒλ©΄ μ΄ν›„μ˜ μ•„μ΄ν…œλ“€μ΄ ν•œ μΉΈμ”© λ°€λ €λ‚˜μ•Ό ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. λ˜ν•œ, λ§ν¬λ“œλ¦¬μŠ€νŠΈλŠ” 곡간(λ©”λͺ¨λ¦¬)이 μžˆλŠ” ν•œ λ„“νž 수 μžˆλ‹€. κ·ΈλŸ¬λ‚˜, μžλ™μœΌλ‘œ 리사이징을 ν•˜λŠ” β€œλ™μ β€ 배열도 μƒκ°μ™Έλ‘œ λΉ„μ‹Ό 값을 μΉ˜λ€„μ•Ό ν•  수 μžˆλ‹€. λ¬Όλ‘ , 이것듀은 항상 λ“±κ°€κ΅ν™˜μ΄λ‹€. λ§ν¬λ“œ 리슀트의 μ›μ†Œλ₯Ό λ³€κ²½ν•˜κ±°λ‚˜ μ°Ύμ•„λ³΄μž. μš°λ¦¬λŠ” μ•„λ§ˆλ„ μ„ ν˜• μ‹œκ°„μœΌλ‘œ 전체 길이λ₯Ό 탐색해야 ν•  μˆ˜λ„ μžˆλ‹€. 반면 인덱슀 λ°°μ—΄μ—μ„œλŠ” λ°”λ‘œ μ°Ύμ•„κ°ˆ 수 μžˆλ‹€.

β€‚λ°°μ—΄μ²˜λŸΌ, λ§ν¬λ“œ λ¦¬μŠ€νŠΈλ„ stack 같이 μ‘°μž‘ν•  수 μžˆλ‹€. head λ₯Ό μ‚½μž…, μ‚­μ œν•˜λŠ” 곡간이 ν•˜λ‚˜μ΄λ―€λ‘œ μ‰½κ²Œ κ΅¬ν˜„ν•  수 μžˆλ‹€. λ˜ν•œ λ§ν¬λ“œλ¦¬μŠ€νŠΈλŠ” queue와 같이 μž‘λ™ ν•  수 μžˆλ‹€. μ΄λŠ” doubly-linked list 둜 이루어 질 수 μžˆλŠ”λ°, tailμ—μ„œ μ‚½μž…μ΄ μΌμ–΄λ‚˜κ±°λ‚˜ headμ—μ„œ μ‚­μ œκ°€ 일어날 λ•Œ ν˜Ήμ€ λ°˜λŒ€μΌ λ•Œ κ°€λŠ₯ν•˜λ‹€. λ§Žμ€ μˆ«μžλ“€μ΄ μ‘΄μž¬ν•˜λŠ” μ›μ†Œμ—μ„œ 이 κ΅¬ν˜„ 방법은 배열을 μ‚¬μš©ν•΄ 큐λ₯Ό κ΅¬ν˜„ν•˜λŠ” 것보닀 더 쒋은 μ„±λŠ₯을 보인닀. μ΄μœ λŠ” λ°°μ—΄μ—μ„œ shift 와 unshift μž‘λ™μ΄ 일어날 λ•Œ 인덱슀λ₯Ό μž¬λ°°μ—΄ ν•˜κΈ° μœ„ν•΄ μ„ ν˜• μ‹œκ°„μ΄ ν•„μš”ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

β€‚λ§ν¬λ“œλ¦¬μŠ€νŠΈλŠ” ν΄λΌμ΄μ–ΈνŠΈμ™€ μ„œλ²„μ—μ„œ λͺ¨λ‘ μ“Έλͺ¨μžˆλ‹€. ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ Redux 같은 μƒνƒœκ΄€λ¦¬ λΌμ΄λΈŒλŸ¬λ¦¬λ“€μ€ 미듀웨어 λ‘œμ§μ„ λ§ν¬λ“œ 리슀트 λ°©μ‹μœΌλ‘œ κ΅¬ν˜„ν•œλ‹€. action이 λ””μŠ€νŒ¨μΉ˜ 될 λ•Œ, λͺ¨λ‘ reducer에 도달할 λ•ŒκΉŒμ§€ ν•˜λ‚˜μ˜ λ―Έλ“€μ›¨μ–΄μ—μ„œ λ‹€μŒ λ―Έλ“€μ›¨μ–΄λ‘œ μ—°κ²°λœλ‹€. μ„œλ²„μ˜ 경우 Express 같은 μ›Ή ν”„λ ˆμž„μ›Œν¬λ„ λΉ„μŠ·ν•œ 미듀웨어 λ‘œμ§μ„ κ°€μ§€κ³  μžˆλ‹€. request λ₯Ό λ°›μ•˜μ„ λ•Œ, 이것듀은 reponse 받을 λ•Œ κΉŒμ§€ 미듀웨어λ₯Ό νƒ€κ³ λ‹€λ‹Œλ‹€.

μ•„λž˜λŠ” Doubly-Linked List 의 μ½”λ“œ μ˜ˆμ΄λ‹€.

class Node {
    constructor(value, next, prev) {
        this.value = value;
        this.next = next;
        this.prev = prev;
    }
}

class LinkedList {
    constructor() {
        this.head = null;
        this.tail = null;
    }

    addToHead(value) {
        const node = new Node(value, null, this.head);
        if (this.head) this.head.next = node;
        else this.tail = node;
        this.head = node;
    }

    addToTail(value) {
        const node = new Node(value, this.tail, null);
        if (this.tail) this.tail.prev = node;
        else this.head = node;
        this.tail = node;
    }

    removeHead() {
        if (!this.head) return null;
        const value = this.head.value;
        this.head = this.head.prev;
        if (this.head) this.head.next = null;
        else this.tail = null;
        return value;
    }

    removeTail() {
        if (!this.tail) return null;
        const value = this.tail.value;
        this.tail = this.tail.next;
        if (this.tail) this.tail.prev = null;
        else this.head = null;
        return value;
    }

    search(value) {
        let current = this.head;
        while (current) {
            if (current.value === value) return value;
            current = current.prev;
        }
        return null;
    }

    indexOf(value) {
        const indexes = [];
        let current = this.tail;
        let index = 0;
        while (current) {
            if (current.value === value) indexes.push(index);
            current = current.next;
            index++;
        }
        return indexes;
    }
}

Tree

 TreeλŠ” λ§Žμ€ child nodes을 μœ μ§€ν•˜κ³  μžˆλŠ” 계측적 κ΅¬μ‘°λΌλŠ” 것을 μ œμ™Έν•˜κ³€ linked listκ°™λ‹€. 즉, 각 λ…Έλ“œλŠ” ν•œ λͺ… μ΄μƒμ˜ λΆ€λͺ¨λ₯Ό κ°€μ§ˆ 수 μ—†λ‹€. Document Object Model(DOM)이 이 ꡬ쑰이닀. root λŠ” html λ…Έλ“œμ΄λ©° μžμ‹λ“€λ‘œ head와 bodyλ…Έλ“œκ°€ μžˆλ‹€. 이듀은 μΉœμˆ™ν•œ λͺ¨λ“  html νƒœκ·Έ 둜 λ”μš± μ„ΈλΆ„ν™”λœλ‹€. μ‹€μ œλ‘œ λ¦¬μ—‘νŠΈ μ»΄ν¬λ„ŒνŠΈ λ˜ν•œ prototype 상속과 ꡬ성 의 트리 ꡬ쑰λ₯Ό μƒμ„±ν•œλ‹€. λ¬Όλ‘  DOM의 인메λͺ¨λ¦¬ ν‘œν˜„μΈ λ¦¬μ—‘νŠΈμ˜ 가상DOM λ˜ν•œ 트리 ꡬ쑰이닀.

 Binary Search TreeλŠ” 각 λ…Έλ“œκ°€ 두 개의 μžμ‹ 이상을 κ°€μ§ˆ 수 μ—†κΈ° λ•Œλ¬Έμ— νŠΉλ³„ν•˜λ‹€. left child의 값은 λ°˜λ“œμ‹œ λΆ€λͺ¨μ˜ 값보닀 μž‘κ±°λ‚˜ κ°™μ•„μ•Όν•œλ‹€ 반면 right childλŠ” λ°˜λ“œμ‹œ λΆ€λͺ¨μ˜ 값보닀 μ»€μ•Όν•œλ‹€. 이와같이 κ΅¬μ‘°ν™”λ˜κ³  κ· ν˜•μ΄ λ§žμΆ°μ‘Œλ‹€λ©΄, μš°λ¦¬λŠ” μ–΄λ– ν•œ 값이든 search μ‹œ 둜그 μ‹œκ°„λ§Œμ— 찾을 수 μžˆλ‹€. μ΄μœ λŠ” 각 브랜치λ₯Ό μˆœνšŒμ‹œ μž‘μ—…μ˜ λ°˜μ„ λ¬΄μ‹œν•  수 있기 λ•Œλ¬Έμ΄λ‹€. μ‚½μž…κ³Ό μ‚­μ œ λ˜ν•œ λ‘œκ·Έμ‹œκ°„μ— κ°€λŠ₯ν•˜λ‹€. κ²Œλ‹€κ°€, κ°€μž₯ μ™Όμͺ½, κ°€μž₯ 였λ₯Έμͺ½ λ…Έλ“œκ°€ 각각 μ΅œμ†Œ ν˜Ήμ€ μ΅œλŒ€ κ°’μ΄λ―€λ‘œ 값을 μ‰½κ²Œ 찾을 수 μžˆλ‹€.

β€‚νŠΈλ¦¬μ˜ μˆœνšŒλŠ” 수직 λ˜λŠ” μˆ˜ν‰μ˜ 절차둜 λ°œμƒν•  수 μžˆλ‹€. μ„Έλ‘œ λ°©ν–₯의 Depth-First Traversal(DFT: DFS) μ—μ„œ μž¬κ·€ μ•Œκ³ λ¦¬μ¦˜μ€ λ°˜λ³΅λ¬Έλ³΄λ‹€ 더 μš°μ•„ν•˜λ‹€. λ…Έλ“œλ“€μ€ pre-order, in-order, post-order 으둜 μˆœνšŒλœλ‹€. λ§Œμ•½ λ¦¬ν”„λ…Έλ“œλ₯Ό κ²€μ‚¬ν•˜κΈ° 전에 λ£¨νŠΈλ…Έλ“œλ₯Ό 탐색해야 ν•œλ‹€λ©΄, μš°λ¦¬λŠ” pre-order λ₯Ό 선택할 것이닀. κ·ΈλŸ¬λ‚˜ λ§Œμ•½ λ£¨νŠΈμ „μ— λ¦¬ν”„λ…Έλ“œλΆ€ν„° 탐색해야 ν•œλ‹€λ©΄ post-orderλ₯Ό 선택해야 ν•œλ‹€. in-order λŠ” 이름이 μ•”μ‹œν•˜λ“―, λ…Έλ“œλ“€μ„ 순차적으둜 μˆœνšŒν•  수 μžˆλ‹€. 이 속성듀은 μ΄μ§„νƒμƒ‰νŠΈλ¦¬λ₯Ό 정렬에 μ΅œμ ν™” 되게 λ§Œλ“ λ‹€.

β€‚μˆ˜ν‰ λ°©ν–₯의 Breadth-First Traversal(BFT: BFS)μ—μ„œ λ°˜λ³΅λ¬Έμ€ μž¬κ·€λ³΄λ‹€ 더 μš°μ•„ν•˜λ‹€. 이것은 각 μˆœνšŒμ— λͺ¨λ“  μžμ‹ λ…Έλ“œλ“€μ„ νŠΈλž˜ν‚Ήν•˜κΈ° μœ„ν•΄μ„œ queue의 μ‚¬μš©μ„ μš”κ΅¬ν•œλ‹€. 이 큐에 ν•„μš”ν•œ λ©”λͺ¨λ¦¬λŠ” μ‚¬μ†Œν•˜μ§€ μ•Šμ„ 수 μžˆλ‹€. ν•˜μ§€λ§Œ 트리의 λͺ¨μ–‘이 κΉŠμ΄λ³΄λ‹€ λ„“λ‹€λ©΄, BFTκ°€ DFT 보닀 더 λ‚˜μ€ 선택이닀. λ˜ν•œ, BFT κ°€ 두 λ…Έλ“œ 사이λ₯Ό μ΄λ™ν•˜λŠ” κ²½λ‘œλŠ” κ°€λŠ₯ν•œ κ°€μž₯ 짧은 거리이닀.

μ•„λž˜λŠ” Binary Search Tree의 μ½”λ“œ μ˜ˆμ΄λ‹€.

class Tree {
    constructor(value) {
        this.value = value;
        this.left = null;
        this.right = null;
    }

    insert(value) {
        if (value <= this.value) {
            if (!this.left) this.left = new Tree(value);
            else this.left.insert(value);
        } else {
            if (!this.right) this.right = new Tree(value);
            else this.right.insert(value);
        }
    }

    contains(value) {
        if (value === this.value) return true;
        if (value < this.value) {
            if (!this.left) return false;
            else return this.left.contains(value);
        } else {
            if (!this.right) return false;
            else return this.right.contains(value);
        }
    }

    depthFirstTraverse(order, callback) {
        order === "pre" && callback(this.value);
        this.left && this.left.depthFirstTraverse(order, callback);
        order === "in" && callback(this.value);
        this.right && this.right.depthFirstTraverse(order, callback);
        order === "post" && callback(this.value);
    }

    breadthFirstTraverse(callback) {
        const queue = [this];
        while (queue.length) {
            const root = queue.shift();
            callback(root.value);
            root.left && queue.push(root.left);
            root.right && queue.push(root.right);
        }
    }

    getMinValue() {
        if (this.left) return this.left.getMinValue();
        return this.value;
    }

    getMaxValue() {
        if (this.right) return this.right.getMaxValue();
        return this.value;
    }
}

mocha.setup("bdd");
const { assert } = chai;

const tree = new Tree(5);
for (const value of [3, 6, 1, 7, 8, 4, 10, 2, 9]) tree.insert(value);

/*
  5
 3 6
1 4 7
 2   8
          10
         9  
*/

describe("Binary Search Tree", () => {
    it("Should implement insert", () => {
        assert.equal(tree.value, 5);
        assert.equal(tree.left.value, 3);
        assert.equal(tree.right.value, 6);
        assert.equal(tree.left.left.value, 1);
        assert.equal(tree.right.right.value, 7);
        assert.equal(tree.right.right.right.value, 8);
        assert.equal(tree.left.right.value, 4);
        assert.equal(tree.right.right.right.right.value, 10);
        assert.equal(tree.left.left.right.value, 2);
        assert.equal(tree.right.right.right.right.left.value, 9);
    });

    it("Should implement contains", () => {
        assert.equal(tree.contains(2), true);
        assert.equal(tree.contains(9), true);
        assert.equal(tree.contains(0), false);
        assert.equal(tree.contains(11), false);
    });

    it("Should implement depthFirstTraverse", () => {
        const _pre = [];
        const _in = [];
        const _post = [];
        tree.depthFirstTraverse("pre", value => _pre.push(value));
        tree.depthFirstTraverse("in", value => _in.push(value));
        tree.depthFirstTraverse("post", value => _post.push(value));
        assert.deepEqual(_pre, [5, 3, 1, 2, 4, 6, 7, 8, 10, 9]);
        assert.deepEqual(_in, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
        assert.deepEqual(_post, [2, 1, 4, 3, 9, 10, 8, 7, 6, 5]);
    });

    it("Should implement breadthDepthTraverse", () => {
        const result = [];
        tree.breadthFirstTraverse(value => result.push(value));
        assert.deepEqual(result, [5, 3, 6, 1, 4, 7, 2, 8, 10, 9]);
    });

    it("Should implement getMinValue", () => {
        assert.equal(tree.getMinValue(), 1);
    });

    it("Should implement getMaxValue", () => {
        assert.equal(tree.getMaxValue(), 10);
    });
});

Graph

β€‚λ§Œμ•½ 트리의 λΆ€λͺ¨κ°€ ν•˜λ‚˜λ³΄λ‹€ 더 많이 κ°€μ§ˆ 수 μžˆλ‹€λ©΄, 이것이 λ°”λ‘œ Graph 이닀. κ·Έλž˜ν”„μ—μ„œ λ…Έλ“œλ“€μ„ μ΄μ–΄μ£ΌλŠ” EdgeλŠ” λ…Έλ“œλ“€μ΄ λ°©ν–₯, 무방ν–₯, κ°€μ€‘μΉ˜, λΉ„κ°€μ€‘μΉ˜ λ₯Ό κ°€μ§€κ²Œ ν•œλ‹€. Edge 듀은 vector와 μœ μ‚¬ν•œ λ°©ν–₯κ³Ό κ°€μ€‘μΉ˜λ₯Ό κ°€μ§„λ‹€.

 

μΆ”κ°€ μ˜ˆμ •

κ°μ‚¬ν•©λ‹ˆλ‹€.