Skip to content

Latest commit

Β 

History

History
92 lines (65 loc) Β· 3.65 KB

File metadata and controls

92 lines (65 loc) Β· 3.65 KB

The Tortoise and Hare Algorithm

물둠이죠! μ•„λž˜λŠ” TIL λ¬Έμ„œλ‘œ μ‚¬μš©ν•  수 μžˆλ„λ‘ μ •λ¦¬ν•œ λ‚΄μš©μž…λ‹ˆλ‹€.


κ°œμš”

The Tortoise and Hare Algorithm, λ˜λŠ” Floyd's Cycle Detection Algorithm은 μ—°κ²° λ¦¬μŠ€νŠΈμ—μ„œ 사이클이 μžˆλŠ”μ§€ νƒμ§€ν•˜λŠ” 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•œ μ•Œκ³ λ¦¬μ¦˜μž…λ‹ˆλ‹€. 이 μ•Œκ³ λ¦¬μ¦˜μ€ 두 개의 포인터(ν•˜λ‚˜λŠ” 느리게 μ΄λ™ν•˜κ³ , λ‹€λ₯Έ ν•˜λ‚˜λŠ” λΉ λ₯΄κ²Œ μ΄λ™ν•˜λŠ” 포인터)λ₯Ό μ΄μš©ν•˜μ—¬ 리슀트λ₯Ό μˆœνšŒν•˜λ©΄μ„œ, 사이클이 μ‘΄μž¬ν•˜λŠ”μ§€ νƒμ§€ν•©λ‹ˆλ‹€.

μ•Œκ³ λ¦¬μ¦˜ μ„€λͺ…

1. μ•Œκ³ λ¦¬μ¦˜μ˜ 핡심 κ°œλ…

  • Tortoise (거뢁이): ν•œ λ²ˆμ— ν•œ μΉΈμ”© μ΄λ™ν•˜λŠ” 포인터 (slow).
  • Hare (토끼): ν•œ λ²ˆμ— 두 μΉΈμ”© μ΄λ™ν•˜λŠ” 포인터 (fast).

두 ν¬μΈν„°λŠ” μ—°κ²° 리슀트λ₯Ό μˆœνšŒν•˜λ©΄μ„œ, λ§Œμ•½ 사이클이 μ‘΄μž¬ν•œλ‹€λ©΄ κ²°κ΅­ λ§Œλ‚˜κ²Œ λ©λ‹ˆλ‹€. 사이클이 μ—†μœΌλ©΄ fast 포인터가 끝에 λ„λ‹¬ν•˜κ³  탐색을 μ’…λ£Œν•˜κ²Œ λ©λ‹ˆλ‹€.

2. λ™μž‘ 원리

  1. μ΄ˆκΈ°ν™”:

    • 두 포인터 slow와 fastλŠ” 리슀트의 headμ—μ„œ μ‹œμž‘ν•©λ‹ˆλ‹€.
  2. 포인터 이동:

    • slowλŠ” ν•œ λ²ˆμ— ν•œ μΉΈμ”© μ΄λ™ν•˜κ³ , fastλŠ” ν•œ λ²ˆμ— 두 μΉΈμ”© μ΄λ™ν•©λ‹ˆλ‹€.
  3. λ§Œλ‚¨ 탐지:

    • slow와 fastκ°€ 같은 λ…Έλ“œλ₯Ό 가리킀면, μ΄λŠ” λ¦¬μŠ€νŠΈμ— 사이클이 μ‘΄μž¬ν•œλ‹€λŠ” λœ»μž…λ‹ˆλ‹€.
  4. 사이클이 μ—†λŠ” 경우:

    • fastκ°€ null에 λ„λ‹¬ν•˜λ©΄, 사이클이 μ—†λ‹€κ³  νŒλ‹¨ν•˜κ³  μ’…λ£Œν•©λ‹ˆλ‹€.

3. μ‹œκ°„ 및 곡간 λ³΅μž‘λ„

  • μ‹œκ°„ λ³΅μž‘λ„: O(n)

    • 리슀트의 λͺ¨λ“  λ…Έλ“œλ₯Ό ν•œ λ²ˆμ”©λ§Œ νƒμƒ‰ν•˜λ―€λ‘œ, 리슀트의 λ…Έλ“œ κ°œμˆ˜κ°€ n일 λ•Œ **O(n)**의 μ‹œκ°„μ΄ μ†Œμš”λ©λ‹ˆλ‹€.
  • 곡간 λ³΅μž‘λ„: O(1)

    • 두 개의 ν¬μΈν„°λ§Œ μ‚¬μš©ν•˜λ―€λ‘œ 좔가적인 λ©”λͺ¨λ¦¬ μ‚¬μš©μ΄ 거의 μ—†μœΌλ©°, μƒμˆ˜ κ³΅κ°„λ§Œ μ°¨μ§€ν•©λ‹ˆλ‹€.

μ•Œκ³ λ¦¬μ¦˜ κ΅¬ν˜„ (TypeScript)

class ListNode {
    val: number;
    next: ListNode | null;
    constructor(val?: number, next?: ListNode | null) {
        this.val = (val === undefined ? 0 : val);
        this.next = (next === undefined ? null : next);
    }
}

function hasCycle(head: ListNode | null): boolean {
    if (!head || !head.next) {
        return false; // λ¦¬μŠ€νŠΈκ°€ λΉ„μ–΄μžˆκ±°λ‚˜ λ…Έλ“œκ°€ ν•˜λ‚˜λΏμ΄λ©΄ 사이클이 μžˆμ„ 수 μ—†μŒ
    }

    let slow: ListNode | null = head;
    let fast: ListNode | null = head;

    while (fast && fast.next) {
        slow = slow!.next;           // ν•œ μΉΈμ”© 이동
        fast = fast.next.next;       // 두 μΉΈμ”© 이동

        if (slow === fast) {
            return true; // slow와 fastκ°€ λ§Œλ‚˜λ©΄ 사이클이 있음
        }
    }

    return false; // fastκ°€ null에 λ„λ‹¬ν•˜λ©΄ 사이클이 μ—†μŒ
}

μ˜ˆμ‹œ

μ—°κ²° λ¦¬μŠ€νŠΈκ°€ λ‹€μŒκ³Ό κ°™λ‹€κ³  κ°€μ •:

3 -> 2 -> 0 -> -4
     ^         |
     |_________|
  • λ§ˆμ§€λ§‰ λ…Έλ“œ -4λŠ” 1번 λ…Έλ“œ 2둜 μ—°κ²°λ˜μ–΄ 사이클이 λ°œμƒν•©λ‹ˆλ‹€.
  • hasCycle ν•¨μˆ˜λŠ” 이 μ—°κ²° λ¦¬μŠ€νŠΈμ—μ„œ 두 포인터가 λ°˜λ³΅ν•΄μ„œ μˆœνšŒν•œ λ’€ λ§Œλ‚¨μ„ κ°μ§€ν•˜μ—¬ 사이클이 μ‘΄μž¬ν•¨μ„ λ°˜ν™˜ν•©λ‹ˆλ‹€.

κ²°λ‘ 

  • The Tortoise and Hare Algorithm은 κ°„λ‹¨ν•˜λ©΄μ„œλ„ 맀우 효율적인 사이클 탐지 μ•Œκ³ λ¦¬μ¦˜μž…λ‹ˆλ‹€.
  • μ‹œκ°„ λ³΅μž‘λ„λŠ” O(n), 곡간 λ³΅μž‘λ„λŠ” O(1)둜 λŒ€κ·œλͺ¨ λ°μ΄ν„°μ—μ„œλ„ μ„±λŠ₯이 λ›°μ–΄λ‚©λ‹ˆλ‹€.
  • 이 μ•Œκ³ λ¦¬μ¦˜μ€ μ—°κ²° 리슀트뿐만 μ•„λ‹ˆλΌ, λ‹€λ₯Έ μˆœν™˜ ꡬ쑰λ₯Ό κ°€μ§„ 데이터 ꡬ쑰에도 μ‘μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이 λ¬Έμ„œκ°€ TIL ν˜•μ‹μœΌλ‘œ μ ν•©ν•˜κΈΈ λ°”λžλ‹ˆλ‹€! ν•„μš”ν•œ μˆ˜μ • 사항이 μžˆκ±°λ‚˜ λ‹€λ₯Έ 포맷으둜 μž‘μ„±μ΄ ν•„μš”ν•˜λ©΄ 말씀해 μ£Όμ„Έμš”!