ํŽœ์œ… ํŠธ๋ฆฌ (Fenwick Tree / Binary Indexed Tree)

ํŽœ์œ… ํŠธ๋ฆฌ (Fenwick Tree / Binary Indexed Tree)

๊ฐœ์š”

ํŽœ์œ… ํŠธ๋ฆฌ(BIT: Binary Indexed Tree)๋Š” ๊ตฌ๊ฐ„ ํ•ฉ ์ฟผ๋ฆฌ์™€ ์  ์—…๋ฐ์ดํŠธ๋ฅผ O(log n)์— ์ฒ˜๋ฆฌํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค. ์„ธ๊ทธ๋จผํŠธ ํŠธ๋ฆฌ๋ณด๋‹ค ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค.


๋ชฉ์ฐจ

  1. ํŽœ์œ… ํŠธ๋ฆฌ ๊ฐœ๋…
  2. ๊ธฐ๋ณธ ๊ตฌํ˜„
  3. ์—ฐ์‚ฐ
  4. ์‘์šฉ
  5. 2D ํŽœ์œ… ํŠธ๋ฆฌ
  6. ์—ฐ์Šต ๋ฌธ์ œ

1. ํŽœ์œ… ํŠธ๋ฆฌ ๊ฐœ๋…

1.1 ๊ตฌ์กฐ

ํŽœ์œ… ํŠธ๋ฆฌ: 1-indexed ๋ฐฐ์—ด ๊ธฐ๋ฐ˜

ํ•ต์‹ฌ ์•„์ด๋””์–ด: i๋ฒˆ์งธ ์›์†Œ๊ฐ€ ๋‹ด๋‹นํ•˜๋Š” ๊ตฌ๊ฐ„์€
              i์˜ ์ตœํ•˜์œ„ ๋น„ํŠธ(lowbit)์— ์˜ํ•ด ๊ฒฐ์ •

lowbit(i) = i & (-i)

์˜ˆ์‹œ (n=8):
์ธ๋ฑ์Šค:    1    2    3    4    5    6    7    8
lowbit:    1    2    1    4    1    2    1    8
๋‹ด๋‹น๊ตฌ๊ฐ„: [1,1][1,2][3,3][1,4][5,5][5,6][7,7][1,8]

tree[1] = arr[1]
tree[2] = arr[1] + arr[2]
tree[3] = arr[3]
tree[4] = arr[1] + arr[2] + arr[3] + arr[4]
tree[5] = arr[5]
tree[6] = arr[5] + arr[6]
tree[7] = arr[7]
tree[8] = arr[1] + ... + arr[8]

1.2 lowbit ์‹œ๊ฐํ™”

์ธ๋ฑ์Šค๋ฅผ ์ด์ง„์ˆ˜๋กœ ํ‘œํ˜„ํ–ˆ์„ ๋•Œ:

1  = 0001 โ†’ lowbit = 1   โ†’ tree[1] = A[1]
2  = 0010 โ†’ lowbit = 2   โ†’ tree[2] = A[1..2]
3  = 0011 โ†’ lowbit = 1   โ†’ tree[3] = A[3]
4  = 0100 โ†’ lowbit = 4   โ†’ tree[4] = A[1..4]
5  = 0101 โ†’ lowbit = 1   โ†’ tree[5] = A[5]
6  = 0110 โ†’ lowbit = 2   โ†’ tree[6] = A[5..6]
7  = 0111 โ†’ lowbit = 1   โ†’ tree[7] = A[7]
8  = 1000 โ†’ lowbit = 8   โ†’ tree[8] = A[1..8]

ํŒจํ„ด:
- ํ™€์ˆ˜ ์ธ๋ฑ์Šค: lowbit = 1 (์ž๊ธฐ ์ž์‹ ๋งŒ)
- 2์˜ ๊ฑฐ๋“ญ์ œ๊ณฑ: lowbit = ์ธ๋ฑ์Šค (1๋ถ€ํ„ฐ ์ž์‹ ๊นŒ์ง€)

1.3 ์‹œ๊ฐ„/๊ณต๊ฐ„ ๋ณต์žก๋„

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ์—ฐ์‚ฐ             โ”‚ ์‹œ๊ฐ„        โ”‚ ์„ค๋ช…         โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ ๊ตฌ์„ฑ             โ”‚ O(n)        โ”‚ ๋˜๋Š” O(nlogn)โ”‚
โ”‚ ์  ์—…๋ฐ์ดํŠธ      โ”‚ O(log n)    โ”‚ ๊ฐ’ ๋ณ€๊ฒฝ      โ”‚
โ”‚ ํ”„๋ฆฌํ”ฝ์Šค ํ•ฉ      โ”‚ O(log n)    โ”‚ [1, i] ํ•ฉ    โ”‚
โ”‚ ๊ตฌ๊ฐ„ ํ•ฉ          โ”‚ O(log n)    โ”‚ [l, r] ํ•ฉ    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๊ณต๊ฐ„: O(n) - ์„ธ๊ทธ๋จผํŠธ ํŠธ๋ฆฌ์˜ 1/2~1/4

2. ๊ธฐ๋ณธ ๊ตฌํ˜„

2.1 Python ๊ตฌํ˜„

class FenwickTree:
    def __init__(self, n):
        """ํฌ๊ธฐ n์˜ ํŽœ์œ… ํŠธ๋ฆฌ (1-indexed)"""
        self.n = n
        self.tree = [0] * (n + 1)

    def update(self, i, delta):
        """arr[i] += delta"""
        while i <= self.n:
            self.tree[i] += delta
            i += i & (-i)  # ๋‹ค์Œ ๋…ธ๋“œ๋กœ ์ด๋™

    def prefix_sum(self, i):
        """arr[1] + arr[2] + ... + arr[i]"""
        total = 0
        while i > 0:
            total += self.tree[i]
            i -= i & (-i)  # ์ด์ „ ๋…ธ๋“œ๋กœ ์ด๋™
        return total

    def range_sum(self, l, r):
        """arr[l] + arr[l+1] + ... + arr[r]"""
        return self.prefix_sum(r) - self.prefix_sum(l - 1)


# ์‚ฌ์šฉ ์˜ˆ์‹œ
n = 8
bit = FenwickTree(n)

# ๋ฐฐ์—ด [0, 1, 2, 3, 4, 5, 6, 7, 8] ๊ตฌ์„ฑ (1-indexed)
for i in range(1, n + 1):
    bit.update(i, i)

print(bit.prefix_sum(4))   # 10 (1+2+3+4)
print(bit.range_sum(2, 5)) # 14 (2+3+4+5)

bit.update(3, 5)  # arr[3] += 5
print(bit.range_sum(2, 5)) # 19 (2+8+4+5)

2.2 ๋ฐฐ์—ด๋กœ ์ดˆ๊ธฐํ™”

class FenwickTreeFromArray:
    def __init__(self, arr):
        """๋ฐฐ์—ด๋กœ ํŽœ์œ… ํŠธ๋ฆฌ ๊ตฌ์„ฑ - O(n)"""
        self.n = len(arr)
        self.tree = [0] * (self.n + 1)

        # O(n) ์ดˆ๊ธฐํ™”
        for i in range(1, self.n + 1):
            self.tree[i] += arr[i - 1]  # arr์€ 0-indexed
            j = i + (i & (-i))
            if j <= self.n:
                self.tree[j] += self.tree[i]

    def update(self, i, delta):
        while i <= self.n:
            self.tree[i] += delta
            i += i & (-i)

    def prefix_sum(self, i):
        total = 0
        while i > 0:
            total += self.tree[i]
            i -= i & (-i)
        return total

    def range_sum(self, l, r):
        return self.prefix_sum(r) - self.prefix_sum(l - 1)


# ์‚ฌ์šฉ ์˜ˆ์‹œ
arr = [1, 2, 3, 4, 5, 6, 7, 8]
bit = FenwickTreeFromArray(arr)
print(bit.range_sum(1, 4))  # 10

2.3 C++ ๊ตฌํ˜„

#include <vector>
using namespace std;

class FenwickTree {
private:
    vector<long long> tree;
    int n;

public:
    FenwickTree(int n) : n(n), tree(n + 1, 0) {}

    FenwickTree(const vector<int>& arr) : n(arr.size()), tree(arr.size() + 1, 0) {
        for (int i = 1; i <= n; i++) {
            tree[i] += arr[i - 1];
            int j = i + (i & (-i));
            if (j <= n) tree[j] += tree[i];
        }
    }

    void update(int i, long long delta) {
        while (i <= n) {
            tree[i] += delta;
            i += i & (-i);
        }
    }

    long long prefixSum(int i) {
        long long total = 0;
        while (i > 0) {
            total += tree[i];
            i -= i & (-i);
        }
        return total;
    }

    long long rangeSum(int l, int r) {
        return prefixSum(r) - prefixSum(l - 1);
    }
};

3. ์—ฐ์‚ฐ

3.1 ์—…๋ฐ์ดํŠธ ๊ณผ์ • ์‹œ๊ฐํ™”

update(3, 5): arr[3] += 5

์ธ๋ฑ์Šค ์ด๋™: 3 โ†’ 4 โ†’ 8 (โ†’ 16 ์ดˆ๊ณผ)

3  = 0011 โ†’ tree[3] += 5
     0011 + 0001 = 0100
4  = 0100 โ†’ tree[4] += 5
     0100 + 0100 = 1000
8  = 1000 โ†’ tree[8] += 5
     1000 + 1000 = 10000 (> n, ์ข…๋ฃŒ)

์˜ํ–ฅ๋ฐ›๋Š” ๋…ธ๋“œ: tree[3], tree[4], tree[8]

3.2 ์ฟผ๋ฆฌ ๊ณผ์ • ์‹œ๊ฐํ™”

prefix_sum(7): arr[1] + ... + arr[7]

์ธ๋ฑ์Šค ์ด๋™: 7 โ†’ 6 โ†’ 4 โ†’ 0

7  = 0111 โ†’ total += tree[7]   (arr[7])
     0111 - 0001 = 0110
6  = 0110 โ†’ total += tree[6]   (arr[5..6])
     0110 - 0010 = 0100
4  = 0100 โ†’ total += tree[4]   (arr[1..4])
     0100 - 0100 = 0000
0  = 0000 (์ข…๋ฃŒ)

๊ฒฐ๊ณผ: tree[7] + tree[6] + tree[4] = arr[1..7]

3.3 ์  ์ฟผ๋ฆฌ (Point Query)

class FenwickTreePointQuery:
    """๊ตฌ๊ฐ„ ์—…๋ฐ์ดํŠธ + ์  ์ฟผ๋ฆฌ"""

    def __init__(self, n):
        self.n = n
        self.tree = [0] * (n + 1)

    def range_update(self, l, r, delta):
        """arr[l..r] += delta"""
        self._update(l, delta)
        if r + 1 <= self.n:
            self._update(r + 1, -delta)

    def _update(self, i, delta):
        while i <= self.n:
            self.tree[i] += delta
            i += i & (-i)

    def point_query(self, i):
        """arr[i] ๊ฐ’"""
        total = 0
        while i > 0:
            total += self.tree[i]
            i -= i & (-i)
        return total


# ์˜ˆ์‹œ
bit = FenwickTreePointQuery(8)
bit.range_update(2, 5, 3)  # arr[2..5] += 3
print(bit.point_query(3))  # 3
print(bit.point_query(6))  # 0

3.4 k๋ฒˆ์งธ ์›์†Œ ์ฐพ๊ธฐ

def find_kth(bit, k):
    """
    ํ”„๋ฆฌํ”ฝ์Šค ํ•ฉ์ด k ์ด์ƒ์ธ ์ตœ์†Œ ์ธ๋ฑ์Šค ์ฐพ๊ธฐ
    (bit[i] = 1 if ์›์†Œ ์กด์žฌ)
    ์‹œ๊ฐ„: O(log n)
    """
    n = bit.n
    pos = 0
    total = 0

    # ์ตœ์ƒ์œ„ ๋น„ํŠธ๋ถ€ํ„ฐ ํƒ์ƒ‰
    log_n = n.bit_length()
    for i in range(log_n - 1, -1, -1):
        next_pos = pos + (1 << i)
        if next_pos <= n and total + bit.tree[next_pos] < k:
            total += bit.tree[next_pos]
            pos = next_pos

    return pos + 1  # k๋ฒˆ์งธ ์›์†Œ์˜ ์ธ๋ฑ์Šค


# ์˜ˆ์‹œ: ๋™์  k๋ฒˆ์งธ ์›์†Œ
class DynamicKth:
    def __init__(self, max_val):
        self.bit = FenwickTree(max_val)

    def add(self, x):
        """์›์†Œ x ์ถ”๊ฐ€"""
        self.bit.update(x, 1)

    def remove(self, x):
        """์›์†Œ x ์ œ๊ฑฐ"""
        self.bit.update(x, -1)

    def kth(self, k):
        """k๋ฒˆ์งธ๋กœ ์ž‘์€ ์›์†Œ"""
        return find_kth(self.bit, k)

    def count_less(self, x):
        """x๋ณด๋‹ค ์ž‘์€ ์›์†Œ ๊ฐœ์ˆ˜"""
        return self.bit.prefix_sum(x - 1)


# ์‚ฌ์šฉ
dk = DynamicKth(100)
dk.add(5)
dk.add(10)
dk.add(3)
dk.add(7)
print(dk.kth(2))  # 5 (์ •๋ ฌ: 3, 5, 7, 10)
print(dk.count_less(7))  # 2 (3, 5)

4. ์‘์šฉ

4.1 ์—ญ์ˆœ ์Œ ๊ฐœ์ˆ˜ (Inversion Count)

def count_inversions(arr):
    """
    ์—ญ์ˆœ ์Œ: i < j์ด๊ณ  arr[i] > arr[j]์ธ (i, j) ๊ฐœ์ˆ˜
    ์‹œ๊ฐ„: O(n log n)
    """
    # ์ขŒํ‘œ ์••์ถ•
    sorted_arr = sorted(set(arr))
    rank = {v: i + 1 for i, v in enumerate(sorted_arr)}
    max_rank = len(sorted_arr)

    bit = FenwickTree(max_rank)
    count = 0

    # ์˜ค๋ฅธ์ชฝ์—์„œ ์™ผ์ชฝ์œผ๋กœ ์ฒ˜๋ฆฌ
    for val in reversed(arr):
        r = rank[val]
        # r๋ณด๋‹ค ์ž‘์€ ๊ฐ’์˜ ๊ฐœ์ˆ˜ (์ด๋ฏธ ์ฒ˜๋ฆฌ๋œ = ์˜ค๋ฅธ์ชฝ์— ์žˆ๋˜)
        count += bit.prefix_sum(r - 1)
        bit.update(r, 1)

    return count


# ์˜ˆ์‹œ
arr = [7, 5, 6, 4]
print(count_inversions(arr))  # 5: (7,5), (7,6), (7,4), (5,4), (6,4)

4.2 ๊ตฌ๊ฐ„ ์—…๋ฐ์ดํŠธ + ๊ตฌ๊ฐ„ ์ฟผ๋ฆฌ

class FenwickTreeRURQ:
    """
    Range Update, Range Query
    ๋‘ ๊ฐœ์˜ BIT ์‚ฌ์šฉ
    """
    def __init__(self, n):
        self.n = n
        self.bit1 = [0] * (n + 2)
        self.bit2 = [0] * (n + 2)

    def _update(self, bit, i, delta):
        while i <= self.n:
            bit[i] += delta
            i += i & (-i)

    def _query(self, bit, i):
        total = 0
        while i > 0:
            total += bit[i]
            i -= i & (-i)
        return total

    def range_update(self, l, r, delta):
        """arr[l..r] += delta"""
        self._update(self.bit1, l, delta)
        self._update(self.bit1, r + 1, -delta)
        self._update(self.bit2, l, delta * (l - 1))
        self._update(self.bit2, r + 1, -delta * r)

    def prefix_sum(self, i):
        """arr[1] + ... + arr[i]"""
        return self._query(self.bit1, i) * i - self._query(self.bit2, i)

    def range_sum(self, l, r):
        """arr[l] + ... + arr[r]"""
        return self.prefix_sum(r) - self.prefix_sum(l - 1)


# ์˜ˆ์‹œ
bit = FenwickTreeRURQ(8)
bit.range_update(2, 5, 3)  # arr[2..5] += 3
print(bit.range_sum(1, 4))  # 9 (0+3+3+3)
print(bit.range_sum(3, 6))  # 12 (3+3+3+3)

4.3 ์˜คํ”„๋ผ์ธ ์ฟผ๋ฆฌ ์ฒ˜๋ฆฌ

def offline_range_sum(arr, queries):
    """
    ์ฟผ๋ฆฌ: (l, r, type)
    type 1: arr[l] += r
    type 2: arr[l..r] ํ•ฉ ๋ฐ˜ํ™˜
    """
    n = len(arr)
    bit = FenwickTreeFromArray(arr)
    results = []

    for query in queries:
        if query[0] == 1:
            _, idx, val = query
            bit.update(idx, val)
        else:
            _, l, r = query
            results.append(bit.range_sum(l, r))

    return results

5. 2D ํŽœ์œ… ํŠธ๋ฆฌ

5.1 ๊ตฌํ˜„

class FenwickTree2D:
    def __init__(self, rows, cols):
        self.rows = rows
        self.cols = cols
        self.tree = [[0] * (cols + 1) for _ in range(rows + 1)]

    def update(self, x, y, delta):
        """arr[x][y] += delta"""
        i = x
        while i <= self.rows:
            j = y
            while j <= self.cols:
                self.tree[i][j] += delta
                j += j & (-j)
            i += i & (-i)

    def prefix_sum(self, x, y):
        """arr[1..x][1..y] ํ•ฉ"""
        total = 0
        i = x
        while i > 0:
            j = y
            while j > 0:
                total += self.tree[i][j]
                j -= j & (-j)
            i -= i & (-i)
        return total

    def range_sum(self, x1, y1, x2, y2):
        """arr[x1..x2][y1..y2] ํ•ฉ"""
        return (self.prefix_sum(x2, y2)
                - self.prefix_sum(x1 - 1, y2)
                - self.prefix_sum(x2, y1 - 1)
                + self.prefix_sum(x1 - 1, y1 - 1))


# ์˜ˆ์‹œ
bit2d = FenwickTree2D(4, 4)
matrix = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
    [13, 14, 15, 16]
]

# ์ดˆ๊ธฐํ™”
for i in range(4):
    for j in range(4):
        bit2d.update(i + 1, j + 1, matrix[i][j])

print(bit2d.range_sum(2, 2, 3, 3))  # 34 (6+7+10+11)

5.2 C++ 2D ๊ตฌํ˜„

class FenwickTree2D {
private:
    vector<vector<long long>> tree;
    int rows, cols;

public:
    FenwickTree2D(int r, int c) : rows(r), cols(c) {
        tree.assign(r + 1, vector<long long>(c + 1, 0));
    }

    void update(int x, int y, long long delta) {
        for (int i = x; i <= rows; i += i & (-i)) {
            for (int j = y; j <= cols; j += j & (-j)) {
                tree[i][j] += delta;
            }
        }
    }

    long long prefixSum(int x, int y) {
        long long total = 0;
        for (int i = x; i > 0; i -= i & (-i)) {
            for (int j = y; j > 0; j -= j & (-j)) {
                total += tree[i][j];
            }
        }
        return total;
    }

    long long rangeSum(int x1, int y1, int x2, int y2) {
        return prefixSum(x2, y2) - prefixSum(x1 - 1, y2)
               - prefixSum(x2, y1 - 1) + prefixSum(x1 - 1, y1 - 1);
    }
};

6. ์—ฐ์Šต ๋ฌธ์ œ

์ถ”์ฒœ ๋ฌธ์ œ

๋‚œ์ด๋„ ๋ฌธ์ œ ํ”Œ๋žซํผ ์œ ํ˜•
โญโญโญ ๊ตฌ๊ฐ„ ํ•ฉ ๊ตฌํ•˜๊ธฐ ๋ฐฑ์ค€ ๊ธฐ๋ณธ
โญโญโญ Range Sum Query - Mutable LeetCode ๊ธฐ๋ณธ
โญโญโญ ๋ฒ„๋ธ” ์†ŒํŠธ ๋ฐฑ์ค€ ์—ญ์ˆœ ์Œ
โญโญโญโญ Count of Smaller Numbers LeetCode ์—ญ์ˆœ ์Œ
โญโญโญโญ ์ง์‚ฌ๊ฐํ˜• ํ•ฉ ๋ฐฑ์ค€ 2D BIT

์„ธ๊ทธ๋จผํŠธ ํŠธ๋ฆฌ vs ํŽœ์œ… ํŠธ๋ฆฌ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ๊ธฐ์ค€           โ”‚ ์„ธ๊ทธ๋จผํŠธ ํŠธ๋ฆฌ โ”‚ ํŽœ์œ… ํŠธ๋ฆฌ     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ ๊ณต๊ฐ„           โ”‚ O(4n)        โ”‚ O(n)         โ”‚
โ”‚ ๊ตฌํ˜„ ๋ณต์žก๋„    โ”‚ ์ค‘๊ฐ„         โ”‚ ๊ฐ„๋‹จ โœ“       โ”‚
โ”‚ ์ƒ์ˆ˜ ๊ณ„์ˆ˜      โ”‚ ํผ           โ”‚ ์ž‘์Œ โœ“       โ”‚
โ”‚ ์  ์ฟผ๋ฆฌ        โ”‚ โœ“            โ”‚ โœ“            โ”‚
โ”‚ ๊ตฌ๊ฐ„ ์ฟผ๋ฆฌ      โ”‚ โœ“            โ”‚ โœ“            โ”‚
โ”‚ ๊ตฌ๊ฐ„ ์—…๋ฐ์ดํŠธ  โ”‚ Lazy ํ•„์š”    โ”‚ 2๊ฐœ BIT ํ•„์š” โ”‚
โ”‚ ๋ฒ”์šฉ์„ฑ         โ”‚ ๋†’์Œ โœ“       โ”‚ ๋‚ฎ์Œ (ํ•ฉ๋งŒ)   โ”‚
โ”‚ ์ตœ์†Œ/์ตœ๋Œ€      โ”‚ โœ“            โ”‚ โœ—            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๊ฒฐ๋ก :
- ๊ตฌ๊ฐ„ ํ•ฉ๋งŒ ํ•„์š” โ†’ ํŽœ์œ… ํŠธ๋ฆฌ
- ์ตœ์†Œ/์ตœ๋Œ€/๋ณต์žกํ•œ ์—ฐ์‚ฐ โ†’ ์„ธ๊ทธ๋จผํŠธ ํŠธ๋ฆฌ

๋‹ค์Œ ๋‹จ๊ณ„


์ฐธ๊ณ  ์ž๋ฃŒ

to navigate between lessons