CodeGym /ํ–‰๋™ /Python SELF KO /์ถฉ๋Œ ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

์ถฉ๋Œ ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

Python SELF KO
๋ ˆ๋ฒจ 54 , ๋ ˆ์Šจ 2
์‚ฌ์šฉ ๊ฐ€๋Šฅ

7.1 ํ•ด์‹œ ํ…Œ์ด๋ธ”์—์„œ์˜ ์ถฉ๋Œ ์ •์˜

ํ•ด์‹œ ํ…Œ์ด๋ธ”์—์„œ์˜ ์ถฉ๋Œ์€ ๋‘ ๊ฐœ์˜ ๋‹ค๋ฅธ ํ‚ค๊ฐ€ ๊ฐ™์€ ๋ฐฐ์—ด ์ธ๋ฑ์Šค๋กœ ํ•ด์‹ฑ๋  ๋•Œ ๋ฐœ์ƒํ•ด. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๋‘ ๊ฐœ ์ด์ƒ์˜ ์š”์†Œ๊ฐ€ ๊ฐ™์€ ํ•ด์‹œ ํ…Œ์ด๋ธ” ์…€์„ ์ฐจ์ง€ํ•˜๋ ค๊ณ  ํ•ด.

์ถฉ๋Œ์˜ ์˜ˆ:

๊ฐ„๋‹จํ•œ ํ•ด์‹œ ํ•จ์ˆ˜๊ฐ€ ํ…Œ์ด๋ธ” ํฌ๊ธฐ์— ๋Œ€ํ•œ ํ‚ค์˜ ๋‚˜๋จธ์ง€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด ๋ณด์ž. ํ…Œ์ด๋ธ” ํฌ๊ธฐ๊ฐ€ 10์ผ ๋•Œ ํ‚ค 5์™€ 15๋Š” ๋™์ผํ•œ ์ธ๋ฑ์Šค๋ฅผ ์ฃผ๊ฒŒ ๋ผ: 5 % 10 = 5์™€ 15 % 10 = 5. ์ด๊ฒƒ์ด ์ถฉ๋Œ์ด์•ผ.

๋˜ ๋‹ค๋ฅธ ์˜ˆ:

์ด๋ฆ„ ์‚ฌ์ „์„ ์ž‘์„ฑํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด, ๊ทธ๋ฆฌ๊ณ  ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ž๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ํ•ด์‹œ ํ•จ์ˆ˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด. ๊ทผ๋ฐ ์ด๋ฆ„์˜ 80%๊ฐ€ "A"๋กœ ์‹œ์ž‘ํ•˜๋ฉด, ์ด๊ฑด ๋‹จ์ˆœํ•œ ์ถฉ๋Œ์ด ์•„๋‹ˆ๋ผ ๋ฌธ์ œ์•ผ.

7.2 ์ถฉ๋Œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• โ€“ ์ฒด์ด๋‹

ํ•ด์‹œ ํ…Œ์ด๋ธ”์—์„œ ์ถฉ๋Œ์„ ํ•ด๊ฒฐํ•˜๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์–ด. ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์€ ์ฒด์ด๋‹(chaining)๊ณผ ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ(open addressing)์ด์•ผ.

์ฒด์ด๋‹ (Chaining)

์žฅ์ :

  • ๊ตฌํ˜„์ด ์‰ฝ๋‹ค.
  • ์ถฉ๋Œ์ด ์ ์€ ๊ฒฝ์šฐ ํšจ์œจ์ ์ด๋‹ค.
  • ํ•ด์‹œ ํ…Œ์ด๋ธ” ๋ฐ€๋„์— ๋œ ์˜์กด์ ์ด๋‹ค.

๋‹จ์ :

  • ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ํฌ์ธํ„ฐ๋ฅผ ์œ„ํ•œ ์ถ”๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
  • ์ถฉ๋Œ์ด ๋งŽ์€ ๊ฒฝ์šฐ ๊ธด ์ฒด์ธ์œผ๋กœ ์ธํ•ด ์„ฑ๋Šฅ์ด ๊ฐ์†Œํ•  ์ˆ˜ ์žˆ๋‹ค.

์ฒด์ด๋‹ ๋ฐฉ๋ฒ• ๊ตฌํ˜„ ์˜ˆ์ œ:

insert ํ•จ์ˆ˜์˜ ์ž‘๋™ ๋ฐฉ์‹์„ ๋ฐฐ์šฐ๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜์ง€๋งŒ, ํŽธ์˜๋ฅผ ์œ„ํ•ด ์ „์ฒด ์ฝ”๋“œ๋ฅผ ์ œ๊ณตํ• ๊ฒŒ.


class HashTableChaining:
    def __init__(self, size):
        self.size = size
        self.table = [None] * size

    def hash_function(self, key):
        return hash(key) % self.size

    def insert(self, key, value):
        index = self.hash_function(key)
        if self.table[index] is None:
            self.table[index] = [(key, value)]
        else:
            for i, kv in enumerate(self.table[index]):
                k, v = kv
                if k == key:
                    self.table[index][i] = (key, value)
                    return
            self.table[index].append((key, value))

    def search(self, key):
        index = self.hash_function(key)
        if self.table[index] is None:
            return None
        for k, v in self.table[index]:
            if k == key:
                return v
        return None

    def delete(self, key):
        index = self.hash_function(key)
        if self.table[index] is None:
            return
        for i, kv in enumerate(self.table[index]):
            k, v = kv
            if k == key:
                del self.table[index][i]
                return

# ์‚ฌ์šฉ ์˜ˆ์ œ:
hash_table = HashTableChaining(10)
hash_table.insert("apple", 1)
hash_table.insert("banana", 2)
hash_table.insert("cherry", 3)
print(hash_table.search("banana"))  # ์ถœ๋ ฅ: 2
hash_table.delete("banana")
print(hash_table.search("banana"))  # ์ถœ๋ ฅ: None

7.3 ๋‘ ๋ฒˆ์งธ ๋ฐฉ๋ฒ• โ€“ ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ

์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ ๋ฐฉ๋ฒ•์€ ์ถฉ๋Œ ์‹œ ๋ฐฐ์—ด์˜ ๋‹ค์Œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์…€์„ ์ฐพ๋Š”๊ฑฐ์•ผ. ์—ฌ๋Ÿฌ ์ „๋žต์ด ์žˆ์–ด: ์„ ํ˜• ํƒ์‚ฌ, ์ œ๊ณฑ ํƒ์‚ฌ ๋ฐ ์ด์ค‘ ํ•ด์‹ฑ.

์žฅ์ :

  • ํฌ์ธํ„ฐ ์ €์žฅ์„ ์œ„ํ•œ ์ถ”๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•„์š” ์—†๋‹ค.
  • ๋” ์ปดํŒฉํŠธํ•œ ๋ฐ์ดํ„ฐ ํ‘œํ˜„.

๋‹จ์ :

  • ํ…Œ์ด๋ธ” ๋ฐ€๋„๊ฐ€ ๋†’์„ ๋•Œ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.
  • ์ถฉ๋Œ ํ•ด๊ฒฐ์„ ์œ„ํ•ด ๋” ๋งŽ์€ ๊ณ„์‚ฐ ์ž์›์ด ํ•„์š”ํ•˜๋‹ค.

์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ ์ „๋žต ์˜ˆ์ œ

์„ ํ˜• ํƒ์‚ฌ (Linear Probing):

  • ๋‹จ๊ณ„๊ฐ€ 1์ธ ํƒ์‚ฌ: index = (index + 1) % size.
  • ๊ฐ„๋‹จํ•˜์ง€๋งŒ "ํด๋Ÿฌ์Šคํ„ฐ๋ง"์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.

์ œ๊ณฑ ํƒ์‚ฌ (Quadratic Probing):

  • ๋‹จ๊ณ„๊ฐ€ ์ œ๊ณฑ์œผ๋กœ ์ฆ๊ฐ€ํ•˜๋Š” ํƒ์‚ฌ: index = (index + i^2) % size.
  • ํด๋Ÿฌ์Šคํ„ฐ๋ง ๋ฌธ์ œ๋ฅผ ์ค„์ด์ง€๋งŒ ๊ตฌํ˜„์ด ๋” ๋ณต์žกํ•˜๋‹ค.

์ด์ค‘ ํ•ด์‹ฑ (Double Hashing):

  • ๋‘ ๋ฒˆ์งธ ํ•ด์‹œ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹จ๊ณ„ ๊ณ„์‚ฐ: index = (index + i * hash2(key)) % size.
  • ํด๋Ÿฌ์Šคํ„ฐ๋ง ๋ฌธ์ œ๊ฐ€ ์ ์ง€๋งŒ ๊ตฌํ˜„์ด ๋ณต์žกํ•˜๋‹ค.

์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ ๋ฐฉ๋ฒ• ๊ตฌํ˜„ ์˜ˆ์ œ(์„ ํ˜• ํƒ์‚ฌ): ์ „์ฒด ์ž‘๋™ ์ฝ”๋“œ๋ฅผ ์ œ๊ณตํ•  ํ…Œ๋‹ˆ insert ํ•จ์ˆ˜์˜ ์ž‘๋™ ๋ฐฉ์‹๋งŒ ํŒŒ์•…ํ•ด๋„ ๋ผ


class HashTableOpenAddressing:
    def __init__(self, size):
        self.size = size
        self.table = [None] * size

    def hash_function(self, key):
        return hash(key) % self.size

    def insert(self, key, value):
        index = self.hash_function(key)
        original_index = index
        while self.table[index] is not None:
            if self.table[index][0] == key:
                self.table[index] = (key, value)
                return
            index = (index + 1) % self.size
            if index == original_index:
                raise Exception("Hash table is full")
        self.table[index] = (key, value)

    def search(self, key):
        index = self.hash_function(key)
        original_index = index
        while self.table[index] is not None:
            if self.table[index][0] == key:
                return self.table[index][1]
            index = (index + 1) % self.size
            if index == original_index:
                break
        return None

    def delete(self, key):
        index = self.hash_function(key)
        original_index = index
        while self.table[index] is not None:
            if self.table[index][0] == key:
                self.table[index] = None
                return
            index = (index + 1) % self.size
            if index == original_index:
                break

# ์‚ฌ์šฉ ์˜ˆ์ œ:
hash_table = HashTableOpenAddressing(10)
hash_table.insert("apple", 1)
hash_table.insert("banana", 2)
hash_table.insert("cherry", 3)
print(hash_table.search("banana"))  # ์ถœ๋ ฅ: 2
hash_table.delete("banana")
print(hash_table.search("banana"))  # ์ถœ๋ ฅ: None

7.4 ์ถฉ๋Œ์ด ํ•ด์‹œ ํ…Œ์ด๋ธ”์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ

์ถฉ๋Œ์ด ์„ฑ๋Šฅ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ.

ํ‰๊ท  ์ ‘๊ทผ ์‹œ๊ฐ„:

  • ์ด์ƒ์ ์ธ ์กฐ๊ฑด(์ถฉ๋Œ์ด ์—†๋Š” ๊ฒฝ์šฐ)์—์„œ ํ•ด์‹œ ํ…Œ์ด๋ธ”์˜ ์š”์†Œ์— ๋Œ€ํ•œ ํ‰๊ท  ์ ‘๊ทผ ์‹œ๊ฐ„์€ O(1)์ด๋‹ค.
  • ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ, ์ฒด์ธ์„ ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜ ์…€์„ ํƒ์‚ฌํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ ‘๊ทผ ์‹œ๊ฐ„์ด ์ฆ๊ฐ€ํ•ด.

์ฒด์ธ์˜ ๊ธธ์ด ์ฆ๊ฐ€:

  • ์ฒด์ด๋‹ ๋ฐฉ๋ฒ•์—์„œ ์ถฉ๋Œ์ด ๋งŽ์„ ๊ฒฝ์šฐ ์ฒด์ธ์˜ ๊ธธ์ด๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉด ๊ฒ€์ƒ‰, ์‚ฝ์ž… ๋ฐ ์‚ญ์ œ ์—ฐ์‚ฐ์— ๋Œ€ํ•œ ์ ‘๊ทผ ์‹œ๊ฐ„์ด O(n)์ด ๋  ์ˆ˜ ์žˆ์–ด.
  • ๊ธด ์ฒด์ธ์€ ์š”์†Œ๋ฅผ ์ฐพ๊ฑฐ๋‚˜ ์‚ฝ์ž…ํ•  ๋•Œ ํ•„์š”ํ•œ ๋น„๊ต ํšŸ์ˆ˜๋ฅผ ์ฆ๊ฐ€์‹œํ‚จ๋‹ค.

๊ณต๊ฐ„ ๋ณต์žก์„ฑ:

  • ์ฒด์ด๋‹ ๋ฐฉ๋ฒ•์—์„œ๋Š” ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ํฌ์ธํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ์ถ”๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ด.
  • ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ ๋ฐฉ๋ฒ•์—์„œ๋Š” ํ…Œ์ด๋ธ” ๋ฐ€๋„๊ฐ€ ์ผ์ • ์ˆ˜์ค€(์ผ๋ฐ˜์ ์œผ๋กœ ์•ฝ 70-80%) ์ด์ƒ์ด ๋  ๋•Œ ์ถฉ๋Œ์ด ํฌ๊ฒŒ ์ฆ๊ฐ€ํ•˜๊ณ  ์ด์— ๋”ฐ๋ผ ์ ‘๊ทผ ์‹œ๊ฐ„์ด ๋Š˜์–ด๋‚œ๋‹ค.

ํด๋Ÿฌ์Šคํ„ฐ๋ง: ์˜คํ”ˆ ์–ด๋“œ๋ ˆ์‹ฑ ๋ฐฉ๋ฒ•(ํŠนํžˆ ์„ ํ˜• ํƒ์‚ฌ)์„ ์‚ฌ์šฉํ•  ๋•Œ ์—ฌ๋Ÿฌ ์—ฐ์†๋œ ์…€๋“ค์ด ์ฐจ์ง€๋˜๋Š” ํด๋Ÿฌ์Šคํ„ฐ๋ง ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜์—ฌ ์ถฉ๋Œ ๊ฐ€๋Šฅ์„ฑ์ด ์ฆ๊ฐ€ํ•˜๊ณ  ์„ฑ๋Šฅ์ด ์ €ํ•˜๋œ๋‹ค.

์ถฉ๋Œ์ด ์„ฑ๋Šฅ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ ๋ถ„์„ ์˜ˆ์ œ:

๊นŠ์ด ํŒŒ๊ณ ๋“ค๊ณ  ์‹ถ์–ดํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•œ ๊ฒƒ


import time
import random

# ์„ ํ˜• ํƒ์‚ฌ
class HashTableOpenAddressing:
    def __init__(self, size):
        self.size = size
        self.table = [None] * size

    def hash_function(self, key):
        return hash(key) % self.size

    def insert(self, key, value):
        index = self.hash_function(key)
        original_index = index
        while self.table[index] is not None:
            if self.table[index][0] == key:
                self.table[index] = (key, value)
                return
            index = (index + 1) % self.size
            if index == original_index:
                raise Exception("Hash table is full")
        self.table[index] = (key, value)

    def search(self, key):
        index = self.hash_function(key)
        original_index = index
        while self.table[index] is not None:
            if self.table[index][0] == key:
                return self.table[index][1]
            index = (index + 1) % self.size
            if index == original_index:
                break
        return None

# ์‚ฝ์ž…ํ•  ๋ฐ์ดํ„ฐ ๋งŽ์ด ์ƒ์„ฑํ•˜๊ธฐ
hash_table = HashTableOpenAddressing(1000)
keys = [random.randint(1, 10000) for _ in range(800)]

# ๋ฐ์ดํ„ฐ ์‚ฝ์ž… ๋ฐ ์‹œ๊ฐ„ ์ธก์ •
start_time = time.time()
for key in keys:
    hash_table.insert(key, key)
print(f"์‚ฝ์ž… ์‹œ๊ฐ„: {time.time() - start_time:.6f} ์ดˆ")

# ๋ฐ์ดํ„ฐ ๊ฒ€์ƒ‰ ๋ฐ ์‹œ๊ฐ„ ์ธก์ •
start_time = time.time()
for key in keys:
    hash_table.search(key)
print(f"๊ฒ€์ƒ‰ ์‹œ๊ฐ„: {time.time() - start_time:.6f} ์ดˆ")

์ฝ”๋ฉ˜ํŠธ
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION