νž™ Heap

Heap ?

  • β€˜λ¬΄μ—‡μΈκ°€λ₯Ό 차곑차곑 μŒ“μ•„μ˜¬λ¦° 더미’ λ₯Ό λœ»ν•¨
  • μ™„μ „ 이진 트리의 일쒅
    • λ°°μ—΄λ‘œ κ΅¬ν˜„ κ°€λŠ₯
    • νž™μ€ λ°°μ—΄μ—μ„œ 일반적으둜 0번 인덱슀λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  1번 인덱슀λ₯Ό 루트둜 작음
  • μ—¬λŸ¬ 개의 κ°’λ“€ μ€‘μ—μ„œ μ΅œλŒ“κ°’μ΄λ‚˜ μ΅œμ†Ÿκ°’μ„ λΉ λ₯΄κ²Œ 찾아내도둝 λ§Œλ“€μ–΄μ§„ 자료ꡬ쑰
  • νž™μ€ μΌμ’…μ˜ λ°˜μ •λ ¬ μƒνƒœλ₯Ό μœ μ§€ν•œλ‹€(큰 값이 μƒμœ„ λ ˆλ²¨μ— 있고 μž‘μ€ 값이 ν•˜μœ„ λ ˆλ²¨μ— μžˆλ‹€λŠ” 정도)
  • 루트 λ…Έλ“œμ— μš°μ„ μˆœμœ„κ°€ 높은 데이터λ₯Ό μœ„μΉ˜μ‹œν‚€λŠ” 자료ꡬ쑰

  • μ΅œλŒ€ νž™(max heap)κ³Ό μ΅œμ†Œ νž™(min heap)이 있음
    • μ΅œλŒ€ νž™ Max Heap
      • μ–΄λ–€ λ…Έλ“œμ˜ ν‚€κ°€ μžμ‹μ˜ 킀보닀 μž‘μ§€ μ•Šμ€ 트리 (직접 μ—°κ²°λœ μžμ‹-λΆ€λͺ¨ λ…Έλ“œ κ°„μ˜ 크기만 비ꡐ)
    • μ΅œμ†Œ νž™ Min Heap
      • μ–΄λ–€ λ…Έλ“œμ˜ ν‚€κ°€ μžμ‹μ˜ 킀보닀 크지 μ•Šμ€ 트리 (직접 μ—°κ²°λœ μžμ‹-λΆ€λͺ¨ λ…Έλ“œ κ°„μ˜ 크기만 비ꡐ)
  • λ…Έλ“œ μœ„ μˆ«μžλŠ” λ°°μ—΄μ˜ 인덱슀λ₯Ό μ˜λ―Έν•¨
    • index(parent) = ⎣index(child)/2⎦
    • index(left_child) = index(parent) * 2
    • index(right_child) = index(parent) * 2 + 1

[μ΅œλŒ€ νž™μ˜ λ°°μ—΄ ν‘œν˜„]

μ΅œλŒ€ νž™ μ‚½μž…(push)

1) λ§ˆμ§€λ§‰ μ›μ†Œ λ‹€μŒμ˜ μΈλ±μŠ€μ— μ‚½μž… 2) λΆ€λͺ¨ λ…Έλ“œμ™€ 크기 비ꡐ (max/min heap의 νŠΉμ„±μ„ μœ μ§€ν•˜κΈ° μœ„ν•˜μ—¬) 3) heap의 νŠΉμ„±μ΄ κΉ¨μ Έμžˆλ‹€λ©΄ λΆ€λͺ¨ λ…Έλ“œμ™€ λ°”κΏˆ 4) λΆ€λͺ¨ λ…Έλ“œμ™€ λΉ„κ΅ν•˜μ—¬ heap의 νŠΉμ„±μ΄ μœ μ§€λœλ‹€λ©΄ stop

[μ΅œλŒ€ νž™μ˜ push]

μ΅œλŒ€ νž™ μ‚­μ œ(pop)

1) νž™μ—μ„œ μ‚­μ œλŠ” 트리의 root λ…Έλ“œλ§Œ μ‚­μ œ 및 쑰회 κ°€λŠ₯
2) 루트 λ…Έλ“œμ˜ μš”μ†Œλ₯Ό μ‚­μ œ β‡’ μ™„μ „ 이진 트리 x 
1) νž™μ˜ λ§ˆμ§€λ§‰ μš”μ†Œλ₯Ό 루트 λ…Έλ“œλ‘œ λ§Œλ“¦(temp root) β‡’ μ™„μ „ 이진 트리 o,μ΅œλŒ€ νž™ νŠΉμ„± x 
2) νž™μ˜ νŠΉμ„±μ— 도달할 λ•ŒκΉŒμ§€ 재배치

#νž™μ— μ €μž₯ν•  μš”μ†Œλ₯Ό Element class둜 μ •μ˜
class Element:
    def __init__(self, key):
        self.key=key

# MaxHeap ν‚€ 속성을 μ§€λ‹Œ μš”μ†Œ 집합
class MaxHeap:
    MAX_ELEMENTS=100
    def __init__(self):
        # λ°°μ—΄μ˜ 1번 μΈλ±μŠ€λΆ€ν„° ν‚€κ°€ μ‚½μž…λ˜κΈ° λ•Œλ¬Έμ— +1개의 λ°°μ—΄ 생성
        self.arr=[None for i in range(self.MAX_ELEMENTS+1)]
        #heapsize : νž™μ— μ €μž₯된 ν‚€ 개수, νž™μ—μžˆλŠ” λ§ˆμ§€λ§‰ ν‚€μ˜ 인덱슀
        self.heapsize=0

    # νž™μ΄ λΉ„μ–΄μžˆμœΌλ©΄ True, μ•„λ‹ˆλ©΄ Falseλ₯Ό λ°˜ν™˜
    def is_empty(self):
        if self.heapsize==0:
            return True
        return False

    # νž™μ΄ 가득 찼으면 True, μ•„λ‹ˆλ©΄ Falseλ₯Ό λ°˜ν™˜
    def is_full(self):
        if self.heapsize>=self.MAX_ELEMENTS:
            return True
        return False

    # λΆ€λͺ¨ λ…Έλ“œμ˜ 인덱슀λ₯Ό λ°›μ•„μ˜΄
    def parent(self, idx):
        return idx >> 1

    # μ™Όμͺ½ μžμ‹ λ…Έλ“œμ˜ 인덱슀λ₯Ό λ°›μ•„μ˜΄
    def left(self, idx):
        return idx << 1

    # 였λ₯Έμͺ½ μžμ‹ λ…Έλ“œμ˜ 인덱슀λ₯Ό λ°›μ•„μ˜΄
    def right(self, idx):
        return (idx << 1) + 1


    # νž™μ— μš”μ†Œλ₯Ό μ‚½μž…
    def push(self, item):
        if self.is_full():
            raise IndexError("the heap is full!!")

        # λ§ˆμ§€λ§‰ μ›μ†Œμ˜ λ‹€μŒ 인덱슀
        self.heapsize+=1
        cur_idx=self.heapsize

        #cur_idxκ°€ λ£¨νŠΈκ°€ μ•„λ‹ˆκ³ 
        #item의 keyκ°€ cur_idx λΆ€λͺ¨μ˜ 킀보닀 크면
        while cur_idx!=1 and item.key > self.arr[self.parent(cur_idx)].key:
            self.arr[cur_idx]=self.arr[self.parent(cur_idx)]
            cur_idx=self.parent(cur_idx)
        self.arr[cur_idx]=item

    # νž™μ—μ„œ μ΅œλŒ€ μ›μ†Œλ₯Ό μ‚­μ œν•˜λ©΄μ„œ λ°˜ν™˜
    def pop(self):
        if self.is_empty():
            return None

        #μ‚­μ œλœ ν›„ λ°˜ν™˜λ  μ›μ†Œ
        rem_elem=self.arr[1]

        # 맨 λ§ˆμ§€λ§‰μ— μœ„μΉ˜ν•œ μ›μ†Œλ₯Ό λ°›μ•„μ˜¨ ν›„
        # νž™ μ‚¬μ΄μ¦ˆλ₯Ό 쀄이면 μ™„μ „ 이진 트리 νŠΉμ„±μ„ μœ μ§€ν•  수 μžˆλ‹€.
        temp=self.arr[self.heapsize]
        self.heapsize-=1

        #λ£¨νŠΈμ—μ„œ μ‹œμž‘
        cur_idx=1
        # 루트의 μ™Όμͺ½ μžμ‹
        child = self.left(cur_idx)
    
        # λ§Œμ•½ child > heapsize 이면 
        # arr[cur_idx]λŠ” 리프 λ…Έλ“œμ΄λ‹€
        while child <= self.heapsize:
            # 였λ₯Έμͺ½ μžμ‹μ΄ 있고
            # 였λ₯Έμͺ½ μžμ‹μ˜ ν‚€κ°€ μ™Όμͺ½ μžμ‹μ˜ 킀보닀 크면
            # childλ₯Ό 였λ₯Έμͺ½ μžμ‹μœΌλ‘œ
            if child < self.heapsize and \
               self.arr[self.left(cur_idx)].key < self.arr[self.right(cur_idx)].key:
                child = self.right(cur_idx)
            # μ΅œλŒ€ νž™ νŠΉμ„±μ„ λ§Œμ‘±ν•˜λ©΄ 
            # λ°˜λ³΅λ¬Έμ„ λ‚˜μ˜¨λ‹€.
            if temp.key >= self.arr[child].key:
                break
            # ν‚€κ°€ 큰 μžμ‹ μ›μ†Œλ₯Ό λΆ€λͺ¨λ‘œ μ΄λ™μ‹œν‚¨λ‹€
            # cur_idxλŠ” μžμ‹ μ›μ†Œλ‘œ μ΄λ™ν•œλ‹€.
            self.arr[cur_idx]=self.arr[child]
            cur_idx=child
            child=self.left(cur_idx)
        
        self.arr[cur_idx]=temp

        return rem_elem


# νž™ 내뢀에 μžˆλŠ” arrλ₯Ό 
# 직접 ν™•μΈν•˜κΈ° μœ„ν•œ ν•¨μˆ˜
def print_heap(h):
    for i in range(1, h.heapsize+1):
        print("{}".format(h.arr[i].key), end="  ")
    print()

if __name__=="__main__":
    h=MaxHeap()

    h.push(Element(2))
    h.push(Element(14))
    h.push(Element(9))
    h.push(Element(11))
    h.push(Element(6))
    h.push(Element(8))

    print_heap(h)

    while not h.is_empty():
        rem=h.pop()
        print(f"poped item is {rem.key}")
        print_heap(h)

μš°μ„ μˆœμœ„ 큐 Priority Queue

Priority Queue?

  • μ΅œλŒ€ μš°μ„ μˆœμœ„ 큐(Max Priority Queue)와 μ΅œμ†Œ μš°μ„ μˆœμœ„ 큐(Min Priority Queue)κ°€ 있음
  • μš°μ„ μˆœμœ„ νλŠ” νž™μœΌλ‘œ κ΅¬ν˜„ν•˜λŠ” κ²½μš°κ°€ 많음
from heapq import heappush, heappop

class Element:
    def __init__(self, key, string):
        self.key=key
        self.data=string

class MinPriorityQueue:
    def __init__(self):
        self.heap=[]

    def is_empty(self):
        if not self.heap:
            return True
        return False

    def push(self, item):
        heappush(self.heap, (item.key, item.data))

    def pop(self):
        return heappop(self.heap)

    def min(self):
        return self.heap[0]

if __name__=="__main__":
    pq=MinPriorityQueue()
    
    pq.push(Element(2, "kim"))
    pq.push(Element(14, "park"))
    pq.push(Element(9, "choi"))
    pq.push(Element(11, "lee"))
    pq.push(Element(6, "yang"))
    pq.push(Element(8, "jang"))

    while not pq.is_empty():
        elem=pq.pop()
        print(f"key[{elem[0]}] : data[{elem[1]}]")