ABOUT ME

전설입니다.

  • 자료구조 힙2
    자료구조와 알고리즘 2020. 10. 28. 18:29

    힙에 데이터 삭제 구현 (Max Heap 예)

    • 힙 클래스 구현4 - delete1
    • 보통 삭제는 최상단 노드 (root 노드)를 삭제하는 것이 일반적임
      • 힙의 용도는 최대값 또는 최소값을 root 노드에 놓아서, 최대값과 최소값을 바로 꺼내 쓸 수 있도록 하는 것임
    class Heap:
        def __init__(self, data):
            self.heap_array = list()
            self.heap_array.append(None)
            self.heap_array.append(data)
        
        def pop(self): #stack의 pop같은 느낌
            if len(self.heap_array) <= 1:
                return None # node에 있는 값을 리턴해야 하기 때문에 !
            
            returned_data = self.heap_array[1]
            return returned_data
    • 힙 클래스 구현4 - delete2
      • 상단의 데이터 삭제시, 가장 최하단부 왼쪽에 위치한 노드 (일반적으로 가장 마지막에 추가한 노드) 를 root 노드로 이동
      • root 노드의 값이 child node 보다 작을 경우, root 노드의 child node 중 가장 큰 값을 가진 노드와 root 노드 위치를 바꿔주는 작업을 반복함 (swap)

    • 특정 노드의 관련 노드 위치 알아내기
      • 부모 노드 인덱스 번호 (parent node's index) = 자식 노드 인덱스 번호 (child node's index) // 2
      • 왼쪽 자식 노드 인덱스 번호 (left child node's index) = 부모 노드 인덱스 번호 (parent node's index) * 2
      • 오른쪽 자식 노드 인덱스 번호 (right child node's index) = 부모 노드 인덱스 번호 (parent node's index) * 2 + 1

    class Heap:
        def __init__(self, data):
            self.heap_array = list()
            self.heap_array.append(None)
            self.heap_array.append(data)
        
        def move_down(self, popped_idx):
            left_child_popped_idx = popped_idx * 2
            right_child_popped_idx = popped_idx * 2 + 1
            
            # case1: 왼쪽 자식 노드도 없을 때
            if left_child_popped_idx >= len(self.heap_array): #왼쪽에 노드가 있다면 그것은 총 길이보다 1작다
                return False
            # case2: 왼쪽자식만 있을때 
            elif right_child_popped_idx >= len(self.heap_array): #오른쪽에 노드가 있다면 그것은 총 길이보다 1작다
                if self.heap_array[popped_idx] < self.heap_array[left_child_popped_idx]:
                    return True
                else:
                    return False
            # case3: 왼쪽, 오른쪽 자식 노드 모두 있을 때
            else:
                if self.heap_array[left_child_popped_idx] > self.heap_array[right_child_popped_idx]:
                    if self.heap_array[popped_idx] < self.heap_array[left_child_popped_idx]:
                        return True
                    else:
                        return False
                else:
                    if self.heap_array[popped_idx] < self.heap_array[right_child_popped_idx]:
                        return True
                    else:
                        return False
        
        def pop(self):
            if len(self.heap_array) <= 1:
                return None
            
            returned_data = self.heap_array[1]
            self.heap_array[1] = self.heap_array[-1] # -1은 맨끝에 있는 데이터.
            del self.heap_array[-1]
            popped_idx = 1
            
            while self.move_down(popped_idx):
                left_child_popped_idx = popped_idx * 2
                right_child_popped_idx = popped_idx * 2 + 1
    
                # case2: 오른쪽 자식 노드만 없을 때
                if right_child_popped_idx >= len(self.heap_array):
                    if self.heap_array[popped_idx] < self.heap_array[left_child_popped_idx]:
                        self.heap_array[popped_idx], self.heap_array[left_child_popped_idx] = self.heap_array[left_child_popped_idx], self.heap_array[popped_idx]
                        popped_idx = left_child_popped_idx
                # case3: 왼쪽, 오른쪽 자식 노드 모두 있을 때
                else:
                    if self.heap_array[left_child_popped_idx] > self.heap_array[right_child_popped_idx]:
                        if self.heap_array[popped_idx] < self.heap_array[left_child_popped_idx]:
                            self.heap_array[popped_idx], self.heap_array[left_child_popped_idx] = self.heap_array[left_child_popped_idx], self.heap_array[popped_idx]
                            popped_idx = left_child_popped_idx
                    else:
                        if self.heap_array[popped_idx] < self.heap_array[right_child_popped_idx]:
                            self.heap_array[popped_idx], self.heap_array[right_child_popped_idx] = self.heap_array[right_child_popped_idx], self.heap_array[popped_idx]
                            popped_idx = right_child_popped_idx
            
            return returned_data
        
        def move_up(self, inserted_idx):
            if inserted_idx <= 1:
                return False
            parent_idx = inserted_idx // 2
            if self.heap_array[inserted_idx] > self.heap_array[parent_idx]:
                return True
            else:
                return False
    
        def insert(self, data):
            if len(self.heap_array) == 1:
                self.heap_array.append(data)
                return True
            
            self.heap_array.append(data)
            inserted_idx = len(self.heap_array) - 1
            
            while self.move_up(inserted_idx):
                parent_idx = inserted_idx // 2
                self.heap_array[inserted_idx], self.heap_array[parent_idx] = self.heap_array[parent_idx], self.heap_array[inserted_idx]
                inserted_idx = parent_idx
            return True    

    . 힙 (Heap) 시간 복잡도

    • depth (트리의 높이) 를 h라고 표기한다면,
    • n개의 노드를 가지는 heap 에 데이터 삽입 또는 삭제시, 최악의 경우 root 노드에서 leaf 노드까지 비교해야 하므로 =𝑙𝑜𝑔2𝑛h=log2n 에 가까우므로, 시간 복잡도는 𝑂(𝑙𝑜𝑔𝑛)O(logn)
      • 참고: 빅오 표기법에서 𝑙𝑜𝑔𝑛logn 에서의 log의 밑은 10이 아니라, 2입니다.
      • 한번 실행시마다, 50%의 실행할 수도 있는 명령을 제거한다는 의미. 즉 50%의 실행시간을 단축시킬 수 있다는 것을 의미함

    '자료구조와 알고리즘' 카테고리의 다른 글

    알고리즘 버블 정렬  (0) 2020.10.29
    알고리즘 들어가기  (0) 2020.10.29
    자료구조 힙  (0) 2020.10.27
    자료구조 트리3  (0) 2020.10.27
    자료구조 트리2  (0) 2020.10.26
전설의 개발자