λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

λ―Έκ΅­μœ ν•™/Data Structure and Algorithm

Feb 13th(Thu) DSA

πŸ“Œ T(n) = T(1) + log n * C = C1 + C * log n → Θ(log n) μ„€λͺ…!

이 μˆ˜μ‹μ€ BST(Binary Search Tree)μ—μ„œ 탐색(검색, μ‚½μž…, μ‚­μ œ) μ—°μ‚°μ˜ μ‹œκ°„ λ³΅μž‘λ„λ₯Ό κ΅¬ν•˜λŠ” 과정을 λ‚˜νƒ€λ‚Έ κ±°μ•Ό.
μ•„λž˜ λ‹¨κ³„λ³„λ‘œ λΆ„μ„ν•΄λ³Όκ²Œ. πŸš€


1️⃣ 점화식 λ‹€μ‹œ 보기

BSTμ—μ„œ 탐색을 ν•  λ•Œ, κ° λ‹¨κ³„λ§ˆλ‹€ 문제 크기가 절반으둜 쀄어듦.
이걸 μ ν™”μ‹μœΌλ‘œ ν‘œν˜„ν•˜λ©΄:

T(n)=T(n/2)+C

μ—¬κΈ°μ„œ:

  • T(n/2): 절반 크기의 νŠΈλ¦¬μ—μ„œ λ‹€μ‹œ 탐색
  • C: ν˜„μž¬ λ…Έλ“œμ—μ„œ λΉ„κ΅ν•˜λŠ” λΉ„μš© (μƒμˆ˜ μ‹œκ°„, O(1))

2️⃣ 점화식 ν™•μž₯ν•˜κΈ° (반볡 적용)

점화식을 ν•œ 단계 더 ν™•μž₯ν•΄λ³΄μž.

T(n)=T(n/2)+CT(n/2)=T(n/4)+CT(n/4)=T(n/8)+C

...

이걸 계속 ν™•μž₯ν•˜λ©΄:

T(n/2k)=T(1)+kC

3️⃣ k κ΅¬ν•˜κΈ°

점화식이 μ’…λ£Œλ˜λŠ” 쑰건은 νƒμƒ‰ν•  λ…Έλ“œκ°€ 1개 λ‚¨μ•˜μ„ λ•Œ (T(1)).
즉, n/2^k = 1을 λ§Œμ‘±ν•˜λŠ” k κ°’을 찾자.

n2k=1

양변에 2^kλ₯Ό κ³±ν•˜λ©΄:

n=2k

양변에 log_2λ₯Ό μ·¨ν•˜λ©΄:

log⁑2n=k

즉, BST의 탐색 깊이(μ΅œλŒ€ 비ꡐ 횟수)λŠ” k = log_2 n.


4️⃣ 점화식에 k λŒ€μž…ν•˜κΈ°

μœ„ κ²°κ³Όλ₯Ό 점화식에 λ„£μœΌλ©΄:

T(n)=T(1)+kCT(n)=T(1)+log⁑2n⋅C

μ—¬κΈ°μ„œ:

  • T(1) = C1 (Base Case, 즉 1개의 λ…Έλ“œλ₯Ό νƒμƒ‰ν•˜λŠ” μ‹œκ°„)
  • CλŠ” 각 λ‹¨κ³„μ—μ„œ λΉ„κ΅ν•˜λŠ” λΉ„μš© (μƒμˆ˜)

λ”°λΌμ„œ:

T(n)=C1+C⋅log⁑2n

5️⃣ Θ(log n)이 λ‚˜μ˜€λŠ” 이유

Big-Theta(Θ) ν‘œκΈ°λ²•μ—μ„œ κ°€μž₯ 큰 ν•­λ§Œ κ³ λ €ν•˜κ³ , μƒμˆ˜ κ³„μˆ˜λ₯Ό λ¬΄μ‹œν•œλ‹€.

T(n)=C1+Clog⁑2n
  • C1은 μƒμˆ˜μ΄λ―€λ‘œ λ¬΄μ‹œ κ°€λŠ₯
  • C \log_2 nμ—μ„œ CλŠ” μƒμˆ˜μ΄λ―€λ‘œ λ¬΄μ‹œ κ°€λŠ₯
  • μ΅œμ’…μ μœΌλ‘œ Θ(log n)

즉, BST 탐색(검색, μ‚½μž…, μ‚­μ œ)의 μ‹œκ°„ λ³΅μž‘λ„λŠ” Θ(log n)이닀! πŸš€


πŸ“Œ 6️⃣ μ‹€μ œ μ½”λ“œμ—μ„œ 확인해보기

java
CopyEdit
class BSTNode { int data; BSTNode left, right; public BSTNode(int data) { this.data = data; this.left = this.right = null; } } class BST { BSTNode root; public BST() { root = null; } public void insert(int key) { root = insertRec(root, key); } private BSTNode insertRec(BSTNode root, int key) { if (root == null) return new BSTNode(key); if (key < root.data) root.left = insertRec(root.left, key); else root.right = insertRec(root.right, key); return root; } // BST 탐색 μ—°μ‚° (μž¬κ·€μ ) public boolean search(int key) { return searchRec(root, key); } private boolean searchRec(BSTNode root, int key) { if (root == null) return false; // Base Case if (root.data == key) return true; return key < root.data ? searchRec(root.left, key) : searchRec(root.right, key); } public static void main(String[] args) { BST tree = new BST(); int[] values = {50, 30, 70, 20, 40, 60, 80}; for (int v : values) tree.insert(v); System.out.println("Search 40: " + tree.search(40)); // true System.out.println("Search 100: " + tree.search(100)); // false } }

βœ… BSTμ—μ„œ 탐색을 μˆ˜ν–‰ν•˜λ©΄ μ΅œλŒ€ log_2 n λ²ˆ λΉ„κ΅ν•˜λ―€λ‘œ Θ(log n) μ‹œκ°„
βœ… νƒμƒ‰ν•  λ•Œλ§ˆλ‹€ μ ˆλ°˜μ”© μ€„μ–΄λ“œλŠ” 것을 확인 κ°€λŠ₯!


πŸ“Œ μ΅œμ’… 정리

1️⃣ 점화식 풀이

  1. BSTμ—μ„œ νƒμƒ‰ν•˜λŠ” 과정은 문제λ₯Ό μ ˆλ°˜μ”© μ€„μž„ → T(n) = T(n/2) + C
  2. 반볡 μ μš©ν•˜λ©΄ T(n/2^k) = T(1) + kC
  3. n/2^k = 1이 λ˜λŠ” k μ°ΎκΈ° → k = log_2 n
  4. 점화식 λŒ€μž… → T(n) = C1 + C log_2 n
  5. Big-Theta ν‘œκΈ°λ²•μœΌλ‘œ ν‘œν˜„ → Θ(log n)

2️⃣ BST νƒμƒ‰μ˜ μ‹œκ°„ λ³΅μž‘λ„

  • μ΅œμ„ μ˜ 경우: O(1) (탐색할 값이 λ£¨νŠΈμ— μžˆμ„ λ•Œ)
  • 평균적인 경우: O(log n)
  • μ΅œμ•…μ˜ 경우: O(n) (편ν–₯ νŠΈλ¦¬μ—μ„œ 탐색할 λ•Œ)
  • **κ· ν˜• BST (AVL, Red-Black Tree λ“±)**μ—μ„œλŠ” 항상 Θ(log n)

πŸ˜† μ΄μ œ 점화식과 Θ(log n)이 λ‚˜μ˜€λŠ” 원리 μ™„λ²½ν•˜κ²Œ 이해됐지? πŸš€πŸ”₯

 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

πŸ“Œ 효율적인 κ±°λ“­μ œκ³± 계산 (A Better Power Function)

주어진 ν•¨μˆ˜λŠ” κ±°λ“­μ œκ³±(power) 계산을 효율적으둜 μˆ˜ν–‰ν•˜λŠ” μž¬κ·€ ν•¨μˆ˜μž…λ‹ˆλ‹€.
일반적인 κ±°λ“­μ œκ³± 계산보닀 ν›¨μ”¬ λΉ λ₯΄κ²Œ 계산할 수 μžˆμ–΄μš”. πŸš€


1️⃣ 기본적인 power(x, n) κ³„μ‚° 방법

일반적으둜 κ±°λ“­μ œκ³±μ„ κ³„μ‚°ν•˜λŠ” κ°€μž₯ 직관적인 방법은 xλ₯Ό n번 κ³±ν•˜λŠ” 것:

java
CopyEdit
int power(int x, int n) { int result = 1; for (int i = 0; i < n; i++) { result *= x; } return result; }

ν•˜μ§€λ§Œ 이 λ°©μ‹μ˜ μ‹œκ°„ λ³΅μž‘λ„λŠ” O(n) μ΄λ―€λ‘œ, n이 컀지면 맀우 λΉ„νš¨μœ¨μ !


2️⃣ 효율적인 방법: λΆ„ν•  정볡 (Divide & Conquer)

주어진 μ½”λ“œλŠ” λΆ„ν•  정볡(Divide & Conquer)을 ν™œμš©ν•œ κ±°λ“­μ œκ³± κ³„μ‚°μ΄μ—μš”.

java
CopyEdit
int power(int x, int n) { if (n == 0) return 1; // Base Case: x^0 = 1 if (n == 1) return x; // Base Case: x^1 = x if (n % 2 == 0) return power(x * x, n / 2); // 짝수일 λ•Œ else return power(x * x, n / 2) * x; // ν™€μˆ˜μΌ λ•Œ }

βœ… μ§μˆ˜ μ§€μˆ˜(n이 짝수일 λ•Œ):

xn=(x2)n/2

즉, ν•œ 번 μ œκ³±ν•΄μ„œ μ§€μˆ˜λ₯Ό 반으둜 μ€„μ—¬μ„œ 계산!

βœ… ν™€μˆ˜ μ§€μˆ˜(n이 ν™€μˆ˜μΌ λ•Œ):

xn=(x2)n/2×x

짝수의 경우처럼 μ€„μ΄μ§€λ§Œ, λ§ˆμ§€λ§‰μ— xλ₯Ό ν•œ 번 더 κ³±ν•΄μ€Œ.


3️⃣ μ½”λ“œ μ‹€ν–‰ κ³Όμ • 뢄석

πŸ“Œ 예제 1: power(2, 10)

μš°λ¦¬λŠ” 2^10을 κ³„μ‚°ν•˜κ³  μ‹ΆμŒ.

  1. n = 10 (짝수) → power(2 * 2, 10 / 2) ν˜ΈμΆœ → power(4, 5)
  2. n = 5 (ν™€μˆ˜) → power(4 * 4, 5 / 2) * 4 ν˜ΈμΆœ → power(16, 2) * 4
  3. n = 2 (짝수) → power(16 * 16, 2 / 2) ν˜ΈμΆœ → power(256, 1)
  4. n = 1 (Base Case) → return 256
  5. κ°’ λ°˜ν™˜:
    • power(256, 1) = 256
    • power(16, 2) = 256
    • power(4, 5) = 256 * 4 = 1024
    • power(2, 10) = 1024

βœ… κ²°κ³Ό: 2^10 = 1024
βœ… μ—°μ‚° 횟수: O(log n) λ²ˆλ§Œ μž¬κ·€ 호좜됨!


4️⃣ μ‹œκ°„ λ³΅μž‘λ„ 뢄석

이 방법은 맀 λ‹¨κ³„μ—μ„œ μ§€μˆ˜λ₯Ό μ ˆλ°˜μ”© μ€„μ΄λ―€λ‘œ μ‹œκ°„ λ³΅μž‘λ„κ°€ O(log n) μ΄λ‹€.

πŸ“Œ λΉ„ꡐ

  • 일반적인 방법: O(n)
  • λΆ„ν•  정볡 방법: O(log n)

μ˜ˆμ‹œ: power(2, 1024)

  • 일반적인 방법 → 1024번 κ³±ν•˜κΈ° μˆ˜ν–‰
  • λΆ„ν•  정볡 방법 → log_2(1024) = 10번 κ³±ν•˜κΈ° μˆ˜ν–‰ (훨씬 빠름!)

 

 

 

 

 

 

 

 

public void deleteIterative(int elem) {
    if (root == null) { // nothing to delete
        return;
    }
    BSTNode prev = null;
    BSTNode current = root;

    // Find current that contains the elem value
    // Keep track of prev (parent)
    while (current != null) {
        if (elem == current.data)
            break;
        else if (elem > current.data) {
            prev = current;
            current = current.right;
        }
        else {
            prev = current;
            current = current.left;
        }
    }
    //System.out.println(current.data);
    if (current == null)
        return; // element not in the tree, nothing to delete

    // Removing a node with one child
    // The root also needs to change if we delete the root
    if (current.left == null) { // no left child
        if (prev == null) {
            // removing the root with one right child
            root = root.right;
            return;
        }
        // Removing a node without the left child, but it is not a root
        if (prev.left == current)
            prev.left = current.right; // since current has not left child
        else { //current is the right child of prev
            prev.right = current.right;
        }
    }
    else if (current.right == null){ // removing a node that does not have the right child
        if (prev == null) {
            // removing the root with one left child
            root = root.left;
            return;
        }
        // Removing a node without the right child, but it is not a root
        if (prev.left == current)
            prev.left = current.left;
        else { //current is the right child of prev
            prev.right = current.left;
        }
    }
    else { // has both children
        // Find the smallest value in the right subtree
        BSTNode rightRoot = current.right;
        BSTNode prevBeforeRightRoot = current;
        while (rightRoot.left != null ) {
            prevBeforeRightRoot = rightRoot;
            rightRoot = rightRoot.left;
        }
        System.out.println("smallest in the right subtree " + rightRoot.data);

        int smallest = rightRoot.data;
        current.data = smallest;
        // Remove smallest
        if (prevBeforeRightRoot.right == rightRoot) {
            prevBeforeRightRoot.right  = rightRoot.right;
        }
        else
            prevBeforeRightRoot.left = rightRoot.right;
    }
}

 

'λ―Έκ΅­μœ ν•™ > Data Structure and Algorithm' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

2.20 λͺ©μš”일 DSA  (0) 2025.02.21
2.14 FRI DSA  (0) 2025.02.15
2.11 DSA  (0) 2025.02.12
linkedlist - reverse  (0) 2025.02.11
Iterable, Linkedlist, Queues, Stack, Trees  (0) 2025.02.09