이원 탐색 트리 정의

  • 이진트리로서 공백가능하고, 만약 공백이 아니라면
    1. 모든 원소는 서로 상이한 키를 갖는다.
    2. 왼쪽 서브트리의 키들은 그 루트의 키보다 작다.
    3. 오른쪽 서브트리의 키들은 그 루트의 키보다 크다.
    4. 왼쪽과 오른쪽 서브트리도 이원 탐색 트리이다.
  • k = 루트의 키 : 성공적 종료
  • k < 루트의 키 : 왼쪽 서브트리 탐색
  • k > 루트의 키 : 오른쪽 서브트리 탐색
  • 삽입
    • x의 key 값을 가진 노드를 탐색
    • 탐색이 성공하면 이 키에 연관된 원소를 변경
    • 탐색이 실패하면 탐색이 끝난 지점에 쌍을 삽입
  • 삭제
    • 리프 원소의 삭제 : 부모의 자식 필드에 0을 삽입. 삭제된 노드 반환
    • 하나의 자식을 가진 비리프 노드의 삭제 : 삭제된 노드의 자식을 삭제된 노드의 자리에 위치
    • 두 개의 자식을 가진 비리프 노드의 삭제 : 왼쪽 서브트리에서 가장 큰 원소나 오른쪽 서브트리에서 가장 작은 원소로 대체. 대체된 서브트리에서 대체한 원소의 삭제 과정 진행
    • 시간 복잡도 : O(h)
  • 이원 탐색 트리(n)의 높이
    • 이원 탐색 트리의 원소 수 : n
    • 최악의 경우
      • 이원 탐색 트리의 높이 = n
      • 키 [1, 2, 3, ... , n]을 순서대로 삽입
    • 삽입 삭제가 무작위로 이루어질 때
      • 트리의 높이 = O(log n)
    • 균형 탐색 트리 (balanced search tree)
      • 최악의 경우에도 높이가 O(log n)이 되는 트리
      • 탐색, 삽입, 삭제의 시간 복잡도 : O(h)
      • AVL, 2-3, 2-3-4, 레드-블랙(red-black), B 트리, B+ 트리 등

'프로그래밍 > 자료구조' 카테고리의 다른 글

우선 탐색  (0) 2020.08.30
그래프  (0) 2020.08.27
우선순위 큐  (0) 2020.08.24
이진 트리  (0) 2020.08.23
트리  (0) 2020.08.23

우선순위 큐

  • 우선순위가 가장 높은(낮은) 원소를 먼저 삭제
  • 임의의 우선순위를 가진 원소 삽입 가능
  • 무순서 선형 리스트
    • IsEmpty() : O(1)
    • Push() : O(1)
    • Top() : Θ(n)
    • Pop() : Θ(n)
# 우선순위 큐 : 가장 값이 큰 원소부터 제거
class MaxPQ:
    pq = []
    size = 0

    def isEmpty(self):
        if len(self.pq) == 0:
            True
        else:
            False

    def push(self, data):
        self.pq.append(data)

    def top(self):
        max = 0
        
        for x in self.pq:
            if max < x:
                max = x
                
        return max

    def pop(self):
        max = 0
        
        for i in range(len(self.pq)): # i는 index
            if self.pq[max] < self.pq[i]:
                max = i
                
        self.pq.pop(max)

    def all(self):
        print(self.pq)
  • 최대 히프 : 최대 트리이면서 완전 이진 트리 (√ 최대트리 : 각 노드의 키 값이 그 자식의 키 값보다 작지 않은 트리)
    • IsEmpty() : O(1)
    • Push() : O(1)
    • Top() : O(log n)
    • Pop() : O(log n)
    • 삽입 후에도 최대 히프 유지 : 새로 삽입되는 원소는 부모 원소와 비교하면서 최대 히프가 되는 것이 확인될 때까지 위로 올라감
    • 삭제 후에도 최대 히프 유지
      1. 루트에서 삭제
      2. 마지막 원소를 제거하고 제거된 마지막 원소와 루트의 왼쪽 자식, 오른쪽 자식 중 큰 값과 서로 교환
      3. 최대 히프가 될 때까지 원소 값을 비교하여 교환
# 최대 히프 - list로 구현
class MaxHeap:

    def __init__(self):
        self.data = None

    def push(self, item):
        self.data.append(item)
        i = len(self.data) - 1

        while i > 1:
            if self.data[i] > self.data[(i // 2)]:
                self.data[i], self.data[(i // 2)] = self.data[(i // 2)], self.data[i]
                i = i // 2
            else:
                break

    def pop(self):
        if len(self.data) > 1:
            self.data[1], self.data[-1] = self.data[-1], self.data[1]
            data = self.data.pop(-1)
            self.maxHeapify(1)
        else:
            data = None

        return data

    def maxHeapify(self, i):
        left = 2 * i
        right = (2 * i) + 1
        smallest = i
        if left < len(self.data) and self.data[i] < self.data[left]:
            smallest = left

        if right < len(self.data) and self.data[i] > self.data[right]:
            smallest = right

        if smallest != i:
            self.data[i], self.data[smallest] = self.data[smallest], self.data[i]
            self.maxHeapify(smallest)

# heapq 사용해서 최대 힙 만들기 : heapq는 최소 힙 지원
import heapq
import sys

numbers = int(input())
heap = []

for _ in range(numbers):
    num = int(sys.stadin.readline())
    if num != 0:
        heapq.heappush(heap, (-num))
    else:
        try:
            print(-1 * heapq.heappop(heap))
        except:
            print(0)

 

 

'프로그래밍 > 자료구조' 카테고리의 다른 글

그래프  (0) 2020.08.27
이원 탐색 트리  (0) 2020.08.24
이진 트리  (0) 2020.08.23
트리  (0) 2020.08.23
희소행렬  (0) 2020.08.21

이진 트리의 특성

  • 한 노드는 최대 두 개의 가지
  • 왼쪽 서브트리와 오른쪽 서브트리 구별
  • 0개의 노드를 가질 수 있음

이진 트리의 정의

  • 공백이거나 루트와 두 개의 분리된 이진 트리로 구성 된 노드의 유한 집합

이진 트리와 일반 트리의 차이점

  • 공백 이진 트리 존재
  • 자식의 순서 구별
    • 서로 다른 두 이진 트리

편향 이진 트리와 완전 이진 트리

이진 트리의 성질

  • 최대 노드의 수
    • 레벨 i에서의 최대 노드 수 : 2i-1 (i >= 1)
    • 깊이가 k인 이진 트리가 가질 수 있는 최대 노드 수 : 2k - 1 (k >= 1)
  • 리프 노드 수와 차수가 2인 노드 수와의 관계
    • n0 = n2 + 1 (n : 리프 노드 수, n2 : 차수가 2인 노드 수)
    • n1 : 차수 1인 노드 수, n : 총 노드 수, B : 총 가지 수
    • n = n + n1 + n2
    • 루트를 제외한 모든 노드들은 들어오는 가지가 하나씩 있으므로 n = B + 1
    • 모든 가지들은 차수가 2 또는 1인 노드에서 뻗어 나오므로 B = n1 + 2n2

포화 이진 트리 (full binary tree)

  • 깊이가 k이고, 노드 수가 2k - 1 (k >= 0)인 이진 트리
  • 노드 번호 1, ... , 2k - 1 까지 순차적 부여 가능

완전 이진 트리 (Complete binary tree)

  • 깊이가 k이고 노드 수가 n인 이진 트리의 각 노드들이 깊이가 k인 포화 이진 트리에서 1부터 n까지 번호를 붙인 노드와 1대 1로 일치
  • n개 노드를 갖는 완전 이진 트리의 높이 :⌈log2(n+1)⌉

이진 트리 배열 표현

  • 1차원 배열에 노드를 저장
  • n개의 노드를 가진 완전 이진 트리가 순차적으로 표현되고 인덱스가 1 <= i <= n 일 때
    1. parent(i) : ⌊i / 2⌋                       if i ≠ 1
    2. leftChild(i) : 2i                          if 2i ≤ n
                     왼쪽 자식 없음          if 2i > n
    3. rightChild(i) : 2i + 1                   if 2i + 1 ≤ n
                     오른쪽 자식 없음        if 2i + 1 > n
  • 완전 이진 트리 : 낭비 되는 공간 없음
  • 편향 트리 : 공간 낭비 (최악의 경우, 깊이 k 편향 트리는 2k - 1 중 k개만 사용

이진 트리 연결 표현

  • 노드 표현
leftChild data rightChild
  • 부모 알기 어려움

트리 순회 (tree traversal)

  • 트리에 있는 모든 노드를 한 번씩만 방문
  • 순회 방법 : LVR, LRV, VLR, VRL, RVL, RLV (L : 왼쪽 이동, V : 노드 방문, R : 오른쪽 이동)
  • 왼쪽을 오른쪽보다 먼저 방문 (LR)
    • LVR : 중위 (inorder) 순회
    • VLR : 전위 (preorder) 순회
    • LRV : 후위 (postorder) 순회
class TreeNode:

    def __init__(self, data, leftChild=None, rightChild=None):
        self.data = data
        self.leftChild = leftChild
        self.rightChild = rightChild

    def hasLeftChild(self):
        return self.leftChild

    def hasRightChild(self):
        return self.rightChild

class Tree:

    def __init__(self):
        self.root = None

    def Put(self, data):
        if self.root:
            self._put(data, self.root)
        else:
            self.root = TreeNode(data)

    def _put(self, data, currentNode):
        if data < currentNode.data:
            if currentNode.hasLeftChild():
                self._put(data, currentNode.leftChild)
            else:
                currentNode.leftChild = TreeNode(data)
        else:
            if currentNode.hasRightChild():
                self._put(data, currentNode.rightChild)
            else:
                currentNode.rightChild = TreeNode(data)

    # 중위 순회
    def Inorder(self, currentNode):
        if currentNode is not None:
            self.Inorder(currentNode.leftChild)
            self.Visit(currentNode)
            self.Inorder(currentNode.rightChild)

    # 전위 순회
    def Preorder(self, currentNode):
        if currentNode is not None:
            self.Visit(currentNode)
            self.Preorder(currentNode.leftChild)
            self.Preorder(currentNode.rightChild)

    # 후위 순회
    def Postorder(self, currentNode):
        if currentNode is not None:
            self.Postorder(currentNode.leftChild)
            self.Postorder(currentNode.rightChild)
            self.Visit(currentNode)

    # 출력
    def Visit(self, currentNode):
        print(currentNode.data)

'프로그래밍 > 자료구조' 카테고리의 다른 글

이원 탐색 트리  (0) 2020.08.24
우선순위 큐  (0) 2020.08.24
트리  (0) 2020.08.23
희소행렬  (0) 2020.08.21
연결 스택과 큐  (0) 2020.08.17

트리 : 하나 이상의 노드 (node)로 이루어진 유한집합

  1. 하나의 루트 (root) 노드
  2. 나머지 노드들은 n (>= 0) 개의 분리 집합 T1, T2, ... , Tn 으로 분할 (Ti : 루트의 서브트리)
  • 노드 : 한 정보 아이템 + 다른 노드로 뻗어진 가지
  • 노드의 차수 (degree) : 노드의 서브트리 수
  • 단말 (leaf) 노드 : 차수 = 0
  • 비단말 노드 : 차수 ≠ 0
  • 자식 : 노드 X의 서브트리의 루트 (↔ 부모)
  • 형제 : 부모가 같은 자식들
  • 트리의 차수 = max{노드의 차수}
  • 조상 : 루트까지의 경로상에 있는 모든 노드
  • 레벨 : 루트 == 레벨1, 자식노드 == 부모레벨 +1
  • 높이 (깊이) = max{노드 레벨}
  • 노드 구조
data
left child right sibling

 

'프로그래밍 > 자료구조' 카테고리의 다른 글

우선순위 큐  (0) 2020.08.24
이진 트리  (0) 2020.08.23
희소행렬  (0) 2020.08.21
연결 스택과 큐  (0) 2020.08.17
원형 리스트  (0) 2020.08.17

희소 행렬에 대한 연결 표현 방법

  • 0이 아닌 각 항은 행 원형 리스트와 열 원형 리스트에 위치
  • 모든 원형 리스트는 헤더 노드를 포함
  • 헤더 노드
    • down : 열 리스트 연결에 사용
    • right : 열 리스트 연결에 사용
    • next : 헤드 노드들을 서로 연결

  • 원소 노드
    • down : 같은 열에 있는 0이 아닌 다음 항 연결
    • right : 같은 행에 있는 0이 아닌 다음 항 연결

희소 행렬 입력

  • 첫 번째 입력 라인
    • 행렬에 있는 행의 수, 열의 수, 0이 아닌 항의 수
  • 다음 입력 라인
    • (i, j, aij)형식의 3원소 쌍
    • 행렬의 0이 아닌 항들의 row, col, value 데이터 멤버로 구성
    • 행으로 정렬되고 행 내에서는 열로 정렬

희소 행렬 삭제

  • 가용 공간 리스트를 이용한 파괴자

'프로그래밍 > 자료구조' 카테고리의 다른 글

이진 트리  (0) 2020.08.23
트리  (0) 2020.08.23
연결 스택과 큐  (0) 2020.08.17
원형 리스트  (0) 2020.08.17
연결 리스트  (0) 2020.08.16

연결 스택

  • 톱에서 노드 삽입/삭제 용이
  • top : 스택의 톱 노드를 가리키는 전용 데이터 멤버
  • 초기엔 top을 0으로 설정
class ChainNode:
    def __init__(self, data, next):
        self.data = data
        self.next = next

class LinkedStack:

    def __init__(self):
        self.top = ChainNode(0, None)

    def Push(self, new):
        self.top = ChainNode(new, self.top)

    def Pop(self):
        if self.isEmpty():
            raise Exception('Empty')

        delNode = self.top
        self.top = self.top.next

        del delNode

    def isEmpty(self) :
        if self.top.next is None:
            return True
        else:
            return False

    def PrintAll(self):
        temp = self.top
        while True:
            if temp.next is None:
                print()
                break
            else:
                print(temp.data, end=" ")
            temp = temp.next

    def Top(self):
        return self.top.data

 

연결 큐

  • 리어에서 노드 삽입 용이
  • 프런트에서 삽입 삭제 용이
  • front : 큐의 처음 노드를 가리키는 전용 데이터 멤버
  • rear : 큐의 마지막 노드를 가리키는 전용 데이터 멤버
  • 초기엔 front와 rear를 0으로 설정
class ChainNode:
    def __init__(self, data, next):
        self.data = data
        self.next = next

class LinkedQueue:
    def __init__(self):
        self.rear = ChainNode(0, 0)
        self.front = ChainNode(0, self.rear)
        self.chain = 0 # chain 개수 세기 위함

    def isEmpty(self):
        if self.chain == 0:
            return True
        else:
            return False

    def Front(self):
        return self.front.next.data

    def Rear(self):
        return self.rear.data

    def Push(self, new):
        if self.isEmpty():
            self.rear = ChainNode(new, 0)
            self.front.next = self.rear
            self.chain += 1
        else:
            self.rear.next = ChainNode(new, 0)
            self.rear = self.rear.next
            self.chain += 1

    def Pop(self):
        if self.isEmpty():
            raise Exception('Empty')

        delNode = self.front.next
        self.front.next = self.front.next.next

        self.chain -= 1

        del delNode

    def PrintAll(self):
        temp = self.front.next

        while True:
            if temp == self.rear:
                print(temp.data)
                break
            else:
                print(temp.data, end=" ")
            temp = temp.next

'프로그래밍 > 자료구조' 카테고리의 다른 글

트리  (0) 2020.08.23
희소행렬  (0) 2020.08.21
원형 리스트  (0) 2020.08.17
연결 리스트  (0) 2020.08.16
수식의 계산  (0) 2020.08.14

원형 리스트 (circular list)

  • 체인에서 마지막 노드의 link 필드가 첫 번째 노드를 가리킴
  • 단순 연결 원형 리스트 (singly-linked circular list)
  • 포인터 current 가 마지막 노드를 가리키는지 검사할 때
    • current -> link == 0 이 아니라 current -> link == first로 검사
  • 삽입, 삭제 함수에서 마지막 노드의 link 필드가 첫 번째 노드를 가리키는지를 연산이 끝난 후 반드시 확인
  • 리스트 앞에 새 노드 삽입
    • 마지막 노드의 link를 변경해야 하므로 접근 포인터가 마지막 노드를 가리키는 편이 더 편리
  • 헤더 노드
    • 모조 노드
    • 공백 리스트를 특별한 경우로 처리할 필요 없음
  • 자유 (삭제된) 노드의 체인
    • 노드 삭제 시간은 체인의 길이에 비례
    • 자유 노드의 체인 유지를 통해 파괴자의 실행 시간 O(1)로 감소
    • 새 노드가 필요할 때는 자유 노드 체인에서 사용
    • 자유 노드 체인이 공백일 때만 new 호출
class ChainNode:
    def __init__(self, data):
        self.data = data
        self.next = None

class CircularList:
    def __init__(self):
        self.head = None
        self.tail = None

    def InsertFront(self, new):
        newNode = ChainNode(new) # ChainNode

        if self.head is None:
            self.head = self.tail = newNode
        else:
            newNode.next = self.head
            self.head = newNode
            self.tail.next = self.head

    def InsertBack(self, new):
        newNode = ChainNode(new)

        if self.tail is None:
            self.tail = self.head = newNode
        else:
            newNode.next = self.tail.next
            self.tail.next = newNode
            self.tail = newNode

    def IsEmpty(self):
        return self.head == None and self.tail == None

    def DeleteFront(self):
        if self.IsEmpty():
            raise Exception('Empty')

        delNode = self.head
        self.head = self.head.next
        self.tail.next = self.head

        del delNode

    def DeleteBack(self):
        if self.IsEmpty():
            raise Exception('Empty')

        delNode = self.tail
        pNode = self.head # 이전 노드

        while pNode.next is not self.tail:
            pNode = pNode.next

        pNode.next = delNode.next
        self.tail = pNode

        del delNode

    def PrintNode(self, node):
        temp = node

        while True:
            if temp.next is node:
                print(temp.data, end="")
            else:
                print(str(temp.data) + '->', end="")

            temp = temp.next
            if temp is node:
                break
        print()

'프로그래밍 > 자료구조' 카테고리의 다른 글

희소행렬  (0) 2020.08.21
연결 스택과 큐  (0) 2020.08.17
연결 리스트  (0) 2020.08.16
수식의 계산  (0) 2020.08.14
큐 (Queue)  (0) 2020.08.12

순차 (sequential) 표현

  • 연속된 원소들이 일정한 거리만큼 떨어져서 저장
  • 테이브에서 임의 원소 접근, 스택이나 큐의 원소 삽입/삭제에 적합
  • 순서 리스트에서 임의 원소의 삽입/삭제 비용 큼

연결된 (linked) 표현

  • 각 원소들이 기억 장소 내의 어떤 곳에나 위치
  • 노드는 데이터와 링크(포인트)로 구성됨

노드 삽입

  • 삽입 절차 (ex. GAT 삽입) 
    1. 현재 사용하고 있지 않은 노드 a 할당
    2. 노드 a의 data 필드에 GAT 설정
    3. a의 link 필드가 FAT 다음 노드, 즉 HAT를 가리키도록
    4. FAT를 포함한 노드의 link 필드가 a를 가리키도록
  • 삽입할때 리스트에 있는 다른 원소들의 이동이 불필요
  • link 필드를 위한 저장 공간이 추가로 사용

노드 삭제

  • 삭제 절차 (ex. GAT 삭제)
    1. GAT 바로 앞에 있는 원소 FAT 찾기
    2. FAT의 link를 HAT의 위치로 설정

이중 연결 리스트

  • 각 노드느 전방과 후방을 가리키는 두 개의 링크를 가짐

'프로그래밍 > 자료구조' 카테고리의 다른 글

연결 스택과 큐  (0) 2020.08.17
원형 리스트  (0) 2020.08.17
수식의 계산  (0) 2020.08.14
큐 (Queue)  (0) 2020.08.12
스택 (Stack)  (0) 2020.08.12

+ Recent posts