자료구조

자료구조 중간고사 정리

FDEE 2020. 10. 25. 23:29

자료구조 이론 요약

 

1단원 : Arrays & Linked Lists (순서가 있다)

⁃ Array

  - index (0 ~ n-1)로 접근 : 특정 index값 접근시 유용

  - fixed size

  - insertion : 1. 크기를 비교하며 들어갈 위치 i 구하기, 2. i+1부터 뒤에값들을 뒤로 미루기, 3. i에 넣기

  - insertion - time : O(n)

  - deletion : 1. i번째 내용 remove, 2. i+1번째부터 앞으로 이동(i+1 ~ n-1)

  - deletion - time : O(n)

 

⁃ Dynamic Array : 사이즈 능동 (Vector, ArrayList, List)

 

⁃ Singly Linked List : 단방향 링크드 리스트

  - Node (T element, Node* next)

  - LinkedList(Node* head, Node* tail)

  - addFront(element) : 1. 새로운 노드 v 생성, 2. element 입력, 3. next : head 설정, 4. head : v 설정

  - addFront - time : O(1) : 4번 수행 (constant time)

  - removeFront() : 1. 삭제될 head : temp로 저장, 2. head : head->next로 이동, 3. temp 삭제

  - removeFront - time : O(1) : 3번 수행 (constant time)

  - addTail(element)

    - 1. 새로운 노드 v 생성, 2. element 입력, 3. next : null 설정

    - 4. tail->next위치에 v설정, 5. tail : v 설정

  - addTail - time : O(1) : 4번 수행 (constant time)

  - removeTail() : 1. 삭제될 tail : temp로 저장, 2. tail : n-2번째의 노드로 설정, 3. tail->next : null 설정, 4. temp 삭제

  - removeTail - time : O(n) : O(1) + O(n) : i=0부터 n-2번째까지 반복되기 때문 + O(1) + O(1)

 

⁃ Doubly Linked List : 양방향 링크드 리스트 

  - Node(T element, Node* next, Node* prev)

  - DLinkedList(Node* header, Node* tailer)

  - add(Node* p, element) : Node p의 위치에 새로운노드를 생성하여 넣는다 (p -> p의 next로 이동)

    - 1. 새로운 노드 v 생성, 2. element 입력, 3. p의 이전노드를 u로 저장한다. : Node*u = p->prev

    - 4. u의 next를 v로 수정한다 (->) : u->next = v

    - 5. v의 이전노드를 u로 저장한다 (<-) : v->prev = u

    - 6. p의 이전노드를 새로운 v로 수정한다 (<-) : p->prev = v

    - 7. v의 다음노드를 p로 저장한다 (->) : v->next = p

  - add - time : O(1) : 7번 수행 (constant time)

  - add(header->next, element) : addFront(element) 동일

  - add(tailer,element) : addTail(element) 동일

  - remove(Node* p) : Node p를 삭제한다 (p->next : p로 이동)

    - 1. p의 이전노드를 u로 저장한다 : Node* u = p->prev

    - 2. p의 다음노드를 w로 저장한다 : Node* w = p->next

    - 3. u의 다음노드를 w로 수정한다 : u->next = w

    - 4. w의 이전노드를 u로 수정한다 : w->prev = u

    - 5. p를 삭제한다 : delete p

  - remove - time : O(1) : 5번 수행 (constant time)

  - remove(header->next) : removeFront() 동일

  - remove(tailer->prev) : removeTail() 동일

  - remove time : O(1) : 5번 수행 (constant time)

 

⁃ ADT : Abstract Data Types : “What”, class 선언부분 (how는 선언되지 않은 부분)

⁃ List ADT

  - size()

  - empty()

  - begin()

  - end()

  - next()

  - insertFront(e)

  - insertBack(e)

  - removeFront()

  - removeBack()

  - insert(p,e)

  - remove(p)

 

⁃ Iterators : 집합체로부터 정보를 얻어오는 도구

  - for(p = c.begin(); p != c.end(); p = c.next())

  - begin() : index로는 0

  - end() : index로는 n (정보가 저장안되어있다, p.end()->data 접근 불가)

  - next() : i+1

 

⁃ LinkedList

  - begin() : head

  - end() : null

  - next() : p.next()

 

⁃ DoubleLinkedList

  - begin() : header.next()

  - end() : tailer

  - next() : p.next()

 

 

 

2단원 : Stacks

⁃ Stack ADT

  - LIFO (Last in first out)

  - size()

  - empty()

  - top() : Exception 고려

  - push(object)

  - pop() : Exception 고려

 

⁃ Use Stack

  - 1. history

  - 2. undo

  - 3. chain

 

⁃ Stack (based Array)

  - Stack(E* S, max, t)

  - E* S : array

  - max : capacity

  - t : index of top

  - size() : return t+1

  - pop() : if(!empty()) : t=t-1, return S[t+1]

  - push(E& e) : if(t+1 >= max) : throw stackFull / else : t=t+1, S[t] = e

  - empty() : return (t == -1)

 

  - “(“, “{“, “[“, “]”, “}”, “)” 사용시 stack이 쓰인다

  - Parentheses Matching Algorithm : 닫고 여는 stack 알고리즘

    for(i=0 to n-1)

      if(X[i] : open -> S.push(X[i])

      else

        if(S.empty() -> return false

        if(S.pop() : not match -> return false

    if(S.empty()) : return true / else return false

 

⁃ Stack (based LinkedList)

  - Stack(Linked* L, header, tailer)

  - peak() : return L->head->element

  - pop() : temp=L->head->element / L->removeFront() / return temp

  - push() : L->addFront(element)

  - peak - time : O(1)

  - pop - time : O(1)

  - push - Time : O(1)

  - Stack - Space : O(n)

 

⁃ Algorithm for Evaluating expressions : 숫자,연산자 조합으로 입력된 값 계산

⁃ valStack, opStack 두가지 스택이 존재 (숫자, 연산자 각각 스택으로 입력)

  while(char z)

    if(z : number) : valStack.push(z) // 만약 문자가 숫자인 경우 숫자스택에 입력

    else

      repeatOps(z)

      //문자인 경우 현재문자와 문자스택의 상단의 우선순위를 비교하여 

      //스택상단의 우선순위가 큰경우 (* > +) 계산을 하여 숫자스택에 넣는다

      opStack.push(z) //연산을 더이상 할수없는경우 연산자 스택에 문자를 입력한다

  repeatOps($) //마지막 문자입력을 넣는다

  return valStack.top() //숫자스택의 상단에 연산된결과가 저장되어있기에 반환한다

 

⁃ repeatOps(newOp)1*2+ : “*” >= “+” : 2+

  while(valStack.size() > 1 && prec(newOp) <= prec(opStack.top()) : doOp()

  // 숫자 스택에 2개이상 있으며 현재 스택상단의 연산자 우선순위가 더 높은경우 연산진행

 

⁃ doOp() : 1+2 -> 3

  x = valStack.pop() //최상단 숫자 x로 저장

  y = valStack.pop() // 두번째 상단 숫자 y로 저장

  op = opStack.pop() //최상단 연산자 저장

  valStack.push(y op x) //연산 후 숫자스택에 넣는다

 

 

3단원 : Analysis of algorithms : 알고리즘 분석

⁃ Pseudocode : 알고리즘을 설명하는 방식

  - More structured than English

  - Less detailed than a program

 

⁃ Basic operations 기본 연산들 : 한정된 시간 소요

  - 1. memory access

  - 2. arithemetic operations (+,-,*,/,%)

  - 3. assignment (=)

  - 4. return, allocation (반환, 할당)

  - 5. comperation (<,<=,>,>=,!=) (비교연산)

 

⁃ Counting primitive operations : 알고리즘의 최대 회수를 알 수 있다

⁃ Estimating running time : T(n) : n개의 자료에 대한 시간 : worst case time을 기준으로 측정한다

⁃ which one is faster? (8n-2) : (5n) -> worst case로 예측한거기 때문에 모른다 -> Big-Oh Notation 사용

⁃ Big-Oh Notation

  - f(n) = O(g(n)), f(n) <= c*g(n) 즉 g(n)이 f(n)보다 작으며 g(n)을 상수배 했을때 f(n)보다 큰 경우이다

  - upperbound가 적용된다 (크지않다! : 상한선)

⁃ Common functions

  - O(1) : Constant

  - O(log n) : Logarithmic

 

  - O(n) : Linear

  - O(n log n) : N-Log-N

 

  - O(n^2) : Quadratic

  - O(n^3) : Cubic
  - O(2^n) : Exponential

 

⁃ Relatives of Big-Oh

⁃ Big-Omega : 하한선

  - f(n) >= c*g(n) : 성공확률, 즉 최대치를 의미한다

⁃ Big-Theta : 같은값들

  - c1*g(n) <= f(n) <= c2*g(n)

 

⁃ Big-Oh : 작거나 같다 (상한선 아래)

⁃ Big-Omega : 크거나 같다 (하한선 위)

⁃ Big Theta : asymptotically 하게 같다

 

 

 

4단원 : Queue : FIFO (First in First out)

⁃ Queue ADT : “what”

  - enqueue(object)

  - dequeue() : Exception 고려

  - size()

  - front() : Exception 고려

  - empty()

⁃ Applications of Queue

  - waiting list

  - multiprogramming

  - level order

 

⁃ Queue (based Array) : 단점으로 크기제한이 있다

  - Queue(n, f, r, MAX) : number of items, index of front, index of rear, max of size

  - f : 맨앞 index

  - r : 실제 마지막값 + 1위치

  - size() : return n

  - empty() : return (n==0)

  - enqueue(val)

  if(size() == n-1) : return QueueFull : n개칸에 n-1개의 자료가 있고 마지막에 r이 위치하고 있는경우 : QueueFull

  else Q[r] = val, r++; r %= MAX (배열끝이 다 찬경우 배열앞으로 이동)

  - dequeue()

  if(empty()) : return QueueEmpty

  else f++; n—; f %= MAX; return Q[f-1]

 

⁃ 두개의 queue를 사용하여 stack을 만들자

  - 1. push(O(1)), pop(O(n))

    - push(val) : Q1.enqueue(val)

    - pop()

    - 1. while(Q1.size() > 1) : Q2.enqueue(Q1.dequeue))

    - 2. 그러면 Q1에 하나의 값만 남게된다 -> swap(Q1,Q2) (Q2 : 1개만)

    - 3. return Q2.dequeue()

  - 2. push(O(n)), pop(O(1))

    - push(val)

      - 1. Q2.enqueue(val)

      - 2. while(Q1.size() >= 1) : Q2.enqueue(Q1.dequeue())

      - 3. 그러면 Q1에 아무값도 없게된다 -> swap(Q1,Q2) (Q2 : 0개)

    - pop() : return Q1.dequeue()

 

⁃ 두개의 stack을 사용하여 queue를 만들자

  - enqueue(val) : S1.push(val)

  - dequeue()

    - 1. if(S2.empty()) : while(!S1.empty()) : S2.push(S1.pop()) // S2가 비어있지 않은경우는 그대로 패스한다

    - 2. 그러면 S1에 아무값도 없게된다. -> return S2.pop()

 

 

 

5단원 : Recursion 재귀

 

⁃ Factorial : 펙토리얼 함수 (재귀)

⁃ factorial(n)

  if(n == 0) : return 1

  else return n * factorial(n-1)

⁃ Time : O(n)

⁃ Space : O(n)

⁃ 반목문으로 제작시 Time : O(n), Space : O(1)로 제작이 가능하다

 

⁃ Recursive Method의 구성요소

  - BaseCase : 탈출조건

  - RecursiveCalls : 재귀를 부르는 내용

 

⁃ Linear Recursion : 선형 재귀 : 재귀를 한번만 부른다

⁃ Recur Once : if(case 1: return A) else return B : A또는 B중 한번만 반환되기 때문에 선형재귀가 맞다

 

⁃ LinearSum(A,n) : 배열 A의 각 요소 n개의 합 : Sum(A,n-1) + A[n-1] 즉, 사이즈를 줄인 배열과 함께 끝자료를 더한다

  if(n==1) : return A[0];

  else return LinearSum(A,n-1) + A[n-1]

 

⁃ ReverseArray(A, i, j) : 배열 순서 바꾸기 (A : 배열, i : 시작 인덱스, j : 끝점 인덱스)

  if(i<j) : swap(A[i],A[j]), ReverseArray(A,i++, j—) : 시작과 끝내용을 바꾼다음 시작++, 끝— 하여 다시 시작한다

 

⁃ BinarySum(A, i, n) : 바이너리트리 A의 시작인덱스 i부터 n개의 값을 더한다

  if(n==1) : return A[i] //개수가 1개이며 i위치이기 때문에 A[i]값을 반환한다 (리프노드일때 반환한다)

  else return BinarySum(A, i, n/2) + BinarySum(A,i + n/2, n/2) //개수가 2개이상인 경우 반으로 쪼개어 i부터 n/2개, i+n/2부터 n/2개씩 더한다

  - 높이 k : 총 데이터 수 n / 2^k승 = 1이 되는 k번째 -> n = 2^k승 -> k = log2 n이 된다!

  - B(n) = 2 * B(n/2) + C = 4 * B(n/4) + 3C -> 2^k * B(1) + a*C = 2^k * a + b -> O(2^k) = O(n)

  - BinarySum - Time : O(n) = O(2^k), k = log2 n

  - BinarySum - Space : O(log n) : 높이 k (n = 2^k)

 

⁃ Fibonacci(k) : 1부터 k개의 숫자를 피보나치로 더한값을 반환한다, k위치값 = k-1위치값 + k-2위치값

  - F(0) = 0, F(1) = 1 로 시작값을 저장한다음 시작한다

  if(k <= 1) : return k

  else return Fibonacci(k-1) + Fibonacci(k-2)

  - 시간(n) >= 2*시간(n-2) >= 4*시간(n-4) >= 8*시간(n-6) -> 2^k * 시간(n-2k)  = 2^k시간 : n = 2k -> k = n/2

  - Fibonacci - Time : O(2^n)

  - Fibonacci - Space : O(n)

 

⁃ LinearFibonacci(k) : (i+j,j) : (k-2+k-1,k-1) 식으로 이전, 두번이전의 값을 한번에 반환하여 linear 방식으로 변환

  if(k==1) : return (k,0)

  else (i, j) = LinearFibonacci(k-1), return (i+j,i) : i=현재 k번째 위치의 값, j=k-1번째 값

 

 

 

6단원 : Tree : parent-child 관계

  - Organization charts

  - File Systems

  - Programming environments

  - Root : parent가 없는 최상단 노드

  - Internal node : 자식이 하나이상 있는 노드

  - External node : 자식이 없는 노드 : leaf 노드

  - Ancestors of node : 자신을 포함한 위에있는 노드들

  - Depth of a node : 최상단 노드 = 0을 기준으로 얼마나 내려와있는지,  부모노드의 개수

    - Time : O(depth of node) : 자신의 depth 만큼 시간이 걸린다

    - Space : O(depth of node) : 자신의 depth 만큼 공간을 사용한다

  - Height of a node : 리프노드 = 0을 기준으로 얼마나 올라가는지, 자식들의 Height의 Max + 1

    - Time : O(자식노드의수)

    - Space : O(Height of node): 자신의 height 만큼 공간을 사용한다

  - Height of a tree : Maximum of depth (최고 레벨)

  - Descendant of a node : 자신을 포함한 아래방향의 자식들 (subTree의 모든요소들)

  - Subtree : Descendant of a node로 이뤄진 트리 (자신이 Root 이고 자식들이 요소인 새로운 트리)

 

⁃ Tree ADT : “What” : class 틀 (tree의 어떤 기능들이 있니)

  - size()

  - empty()

  - root()

  - nodes()

  - parent()

  - children()

  - isRoot()

  - isExternal()

 

⁃ Tree (based LinkedList)

  - Node(E element, Node* parent, List<Node*> Children) : 각 노드는 값, 부모노드, 자식노드들을 가진다

  - Tree(Node* root, List<Node*> Nodes) : 트리는 root노드와 노느들의 리스트를 가진다, 각 리스트에 노드가 있으며 노드의 부모, 노드의 자식을을 접근하는 식으로 구성된다

 

⁃ Tree Traversal : 노드를 방문하는 순서

⁃ DFS (Depth first search)

  - 1. Preorder : 노드에 방문했을시 수행 후, 자식들의 노드를 방문한다

    preorder(v)

      visit(v) //print(v.data)

      for each child w of v

        preorder(w)

  - Time : O(n)

  - Space : O(Height of v)

  - 2. Postorder(v) : 노드에 방문하여 자식들의 노드를 방문 후 수행한다

    postorder(v)

      for each child w of v

        postorder(w)

      visit(v) // print(v.data)

  - Time : O(n)

  - Space : O(Height of v)

 

  - Preorder : 목차에 사용, html의 헤더에 사용

  - Postorder : html의 헤더닫기에 사용

 

⁃ Binary Tree : 자식이 최대 두개밖에 없는 트리

⁃ Proper Binary Tree : 모든 자식이 두개인 트리, 자식노드의 순서를 부여할 수 있다 (왼쪽, 오른쪽)

⁃ Full Binary Tree : Proper Binary Tree와 같은개념

⁃ Complete Binary Tree : 노드가 왼쪽 -> 오른쪽 순으로 채움을 만족하는 트리

 

⁃ Proper Binary Tree = Complete Binaty Tree : 항상 만족한다

⁃ Complete Binaty Tree != Proper Binaty Tree : 왼쪽 노드만 있는 경우도 있기 때문에 항상 같지는 않다

⁃ Binary Search Tree : 위와 전혀다른 개념, 착각하지 말자

 

⁃ Proper Binary Tree : Left, Right 노드 존재

  - m : non-leaf 노드의 개수

  - l : leaf 노드의 개수

  - 1. l = m+1 (leaf 노드의 수는 non-leaf 노드의 수 + 1개이다)

  - 2. m = l-1 (non-leaf 노드의 수는 leaf 노드의 수 - 1개이다) : n개의 축구팀의 토너먼트 : n-1 경기수

 

⁃ Properties of Binary Tree : 한쪽만 노드가 있을 수 있다!

  - n : number of nodes

  - m : number of internal nodes

  - l : number of leaf nodes

  - h : height of node

  ※ h <-> n의 관계 : Tree of V의 space complexity = V의 height 이다

 

  - 1. n의 관계 (n <-> h 관계를 사용한다)

    - 자식노드가 하나씩 밖에 없는경우 (일렬) : 높이 h = 개수 n-1개이다

    -> h+1 <= n을 만족한다

    - Proper binary인 경우 : n = 2^0 + … + 2^h -> 2^(h+1)-1 = n

    -> n <= 2^(h+1) -1을 만족한다

    따라서 “ h+1 <= n <= 2^(h+1) -1 “ 만족한다

 

  - 2. h의 관계 (h <-> h 관계를 사용한다)

    - 자식노드가 하나씩 밖에 없는경우 (일렬) : 높이 h = 개수 n-1개이다

    -> h <= n-1을 만족한다

    - Proper binary인 경우 : 2^(h+1) -1 = n -> 2^(h+1) = n+1 -> h+1 = log 2 (n+1)

    -> h >= log2 (n+1) -1을 만족한다

    따라서 “ log2 (n+1) -1 <= h <= n-1 “ 만족한다

 

  - 3. m의 관계 (m = n - l) 관계를 사용한다)

    - 자식노드가 하나씩 밖에 없는경우 (일렬) : 리프노드가 1 이므로 m = n-l = n-1이 된다

    -> m >= n-1 = h을 만족한다

    - Proper Binary인 경우 : internal node : m = 2^0 + … + 2^(h-1)이다 -> m = 2^(h)-1

    -> m <= 2^h -1을 만족한다 

    따라서 “ n-1 <= m <= 2^h -1 “ 만족한다

 

  - 4. l의 관계

    - 노드가 단 하나인 경우 : m=1, l=0인 경우이다

    -> l >= 0 or 1 을 만족한다

    - Proper binary인 경우 : h번째의 노드의 수 = 2^h이므로

    -> l <= 2^h 를 만족한다

    따라서  “ 0 or 1 <= l <= 2^h “ 만족한다

 

⁃ Properties of Proper Binary Trees : 항상 자식노드가 두개씩 존재한다

  - n : number of nodes

  - m : number of internal nodes

  - l : number of leaf nodes

  - h : height of node

  ※ l = m+1 (리프노드의 개수 = 리프가 아닌 노드의 개수 + 1)

 

  - 1. h의 관계

    - 한쪽 방향으로 길게 뻗은경우 : 한쪽방향만 보면 h = m = n-1이 된다 (h가 최대인 경우)

    -> h <= m 을 만족한다

    - Proper Binary인 경우 : 리프노드의 개수 l = 2^h가 된다 -> h = log 2 (l) (h가 최소인 경우)

    -> h >= log2 (l) 을 만족한다

    따라서 “ log2 (l) <= h <= m “ 만족한다

 

  - 2. n의 관계 : 두가지 식을 사용하여 구할 수 있다 (n값 비교하기)

    - m + 1 = l

    - m + l = n

    -> n = (l-1) + l = 2l - 1

    따라서 “ n = 2l -1 “ 만족한다

 

  - 3. h의 관계 : 두가지 식, h의 관계를 사용하여 구할 수 있다(l값 비교하기)

    - l = m+1 = n-m -> 2m = n-1

    -> m = (n-1)/2 만족한다

    - h <= m을 만족한다 (h가 최대일 때)

    따라서 “ h <= (n-1)/2 “ 만족한다

 

  - 4. l의 관계

    - Proper Binary의 경우 : 리프노드의 개수 l = 2^h를 만족한다 (l이 최대일 때)

    따라서 “ l <= 2^h “ 만족한다

 

  - 5. h의 관계

    - n = 2^(h+1) -1 -> 2^(h+1) = n+1 -> h+1 = log2 (n+1)

    -> h = log2 (n+1) - 1 을 만족한다 (h가 최소일때)

    따라서 “ h >= log2 (n+1) - 1 “ 만족한다

 

⁃ Binary Tree ADT

  - left()

  - right()

  

⁃ Inorder Traversal : Binary Tree의 경우 일반 Tree와 다르게 left, right 가 있기에 middle의 개념이 가능하다

⁃ inorder(v)

  if(!v.isExternal()) : inorder(v.left()) //리프 노드가 아닌경우 left로 이동한다

  visit(v) //left가 다 끝난 후 middle 지점에서 수행한다

  if(!v.isExternal()) : inorder(v.right()) //마지막으로 right로 이동한다

 

⁃ Inorder 순서 활용

  - ( (2 ✕ (a - 1) ) + ( 3 ✕ b) ) : 왼쪽값, 중간 오페레이터, 오른쪽값 -> 반환

  if(v.isExternal()) : return v.element()

  else x = evalExpr(v.left()), y = eval(Expr(v.right()), op = operator stored at v

    return x op y

  - Time : O(n)

  - Space : O(h)

 

⁃ Euler Tour Traversal : DFS 방식으로 노드를 거쳐가는 루트, 항상 각 노드를 세번씩 지나치게 된다 (pre, in, post)

⁃ EulerTour(v)

  visit(v) // pre

  EulerTour(v.left())

  visit(v) //in

  EulerTour(v.right())

  visit(v) //post

 

⁃ Tree Traversal : 노드를 방문하는 순서

⁃ BFS (Breadth first search) : 너비 우선 탐색

⁃ Level order Traversal : BFS 방식으로 탐색 : Queue를 사용하여 구현한다

 

⁃ levelOrder(v)

  Q.enqueue(v) //Q에 v를 넣는다

  while (!Q.isEmpty()) : u = Q.dequeue(), visit(u) , for each child w of u : Q.enqueue(w)

  // 일단 Q에 v를 넣고 시작한다, 그리고 Q가 비어있지 않는경우 dequeue를 하며 동시에 자식 노드들을 enqueue 한다

⁃ Time : O(n)

⁃ Space : O(L)

⁃ #L : 각 level 에 있는 노드의 수들 중 Max값

 

⁃ Array-Based Representation of Binary Trees : 트리를 array에 저장하는 형식

⁃ 부모노드 : F(v) = 1일때

⁃ left 노드 : 2*F(v) = 2

⁃ right 노드 : 2*F(v)+1 = 3

⁃ left 노드의 left 노드 : 2*(2*F(v)) = 4

⁃ left 노드의 right 노드 : 2*(2*F(v))+1 = 5

⁃ right 노드의 left 노드 : 2*(2*F(v)+1) = 6

⁃ right 노드의 right 노드 : 2*(2*F(v)+1) +1 = 7

⁃ 따라서 F(v)의 최대수 : 2^n -1까지이다

⁃ Space : O(2^n) (빈공간이 많이 사용된다) -> List로 구현하는 경우 Space : O(n)이 소요된다

⁃ 따라서 Complete Tree 형태일 때 사용된다

 

 

7단원 : Priority Queue (PQ)

⁃ Heap을 많이 사용한다 (Heap != PQ)

⁃ Priority Queue ADT

⁃ min : key와 value 가 존재하는데, key가 작은값일수록 우선순위가 놓은 구조이다

  - insert()

  - removeMin() : 우선순위가 높은 값을 삭제한다

  - min() : 우선순위가 높은 값을 반환한다

 

⁃ Total Order Relations : key 간에 순위 (level 따라, left, right 따라 등등)

  - Reflective property : x <= x

  - Antisymmetric property : x<=y && y<=x -> x = y

  - Transitive property : x <= y && y <= z -> x <= z

  - 세가지 모두 만족 : Partial order 만족

 

⁃ Priority Queue Sorting : 정렬

⁃ PQ-sort(list S,C) : Selection Sort : removeMin()시에 정렬된다

  while(!S.empty()) : e = S.removeFront(), P.insert(e,null) //S가 빌때까지 P에 넣는다

  while(!P.empty()) : e = P.removeMin(), S.addTail(e) // P에서 min값을 하나씩 찾으며 S에 넣는다

  - insertTime : O(1) (한번 실행시 기준)

  - removeTime : O(n) (한번 실행시 기준)

 

  * assending(오름차순), dessending(내림차순)

 

⁃ 1. Selection sort : removeMin() 시에 최소값을 찾으며 정렬된다

  - insert Time : O(1) * n번 = O(n)

  - removeMin Time : (n-1) + (n-2) + … + 1 = O(n^2)

  - Time : O(n^2)

  - Space : O(n)

 

⁃ 2. Insertion sort : insert() 시에 정렬되며 추가된다

  - insert Time : 1 + 2 + … + (n-1) = O(n^2)

  - removeMin Time : O(1) * n번 = O(n)

  - Time : O(n^2)

  - Space : O(n)

 

⁃ In-place Insertion sort : 넣을 시에 기존값들과 하나씩 비교하며 swap

⁃ swap을 통해 구현이 가능하다 : 2번째 : 1번째와 비교 -> 3번째 : 1,2번째와 비교 -> 4번째 : 1,2,3번째와 비교 …

⁃ Time : O(n^2) : 1 + 2 + … + (n-1)

⁃ Space : O(1) : auxiliary space

⁃ In-place Selection sort : 넣은 후 정렬이 안된 범위내에서 최소값을 찾아 그 위치와 swap 하며 정렬을 넓혀간다

⁃ 정렬이 안된 범위내에서 가장 작은값을 찾는다 -> 맨앞위치의 값과 swap 한다

⁃ 그러면 앞에서부터 정렬된값으로 하나씩 생긴다

⁃ Time : O(n^2) : n개중 최소값 -> n-1중 최소값 -> … -> 1개중 최소값

⁃ Space : O(1) : auxiliary space

 

* Heap : Time(log n), Space(log n)

 

 

 

8단원 : Heap : Binary Tree의 일종 (complete binary tree : 왼,오 순서대로 : height가 최소)

⁃ Partial order : level별로 순서만 의미부여, level 내의 순서를 고려하지 않는다

⁃ parent의 key값 <= child의 key값 순서가 지켜져야 한다 (parent <= child)

 

⁃ Height of a Heap : 높이

  - 개수 : 2^h <= n <= 2^(h+1) -1

  - 높이 : O(log n) : h <= log2 n

 

⁃ Heap implementation (based Array) : Complete 형태이므로 좋은 형태이다

⁃ f(v) -> left : 2*f(v), right : 2*f(v)+1

 

⁃ Node(f, left, right, parent) : f : 값, 부모, left 자식, right 자식을 지닌다

⁃ left() : return A[f*2]

⁃ right() : return A[f*2+1]

⁃ parent() : return A[f/2]

⁃ upHeap() : 특정노드 <-> 부모노드와 swap 하며 노드가 위로 올라가는 메소드

⁃ downHeap() : 특정노드 <-> 자식노드(중 값이 작은노드)와 swap 하며 노드가 내려가는 메소드

 

⁃ Insertion into Heap (Heapify)

  1. array의 마지막 위치에 insert 된다

  2. 부모와 key 값을 비교하며 upHeap()을 한다

  3. 최종적으로 알맞은 위치에 들어가게 된다

  Time : O(log n) : insert : O(1) + upHeap : O(log n)

  Space : O(1)

 

⁃ UpHeap(Node v) : 노드를 위로 올려 Heap으로 만든다

  while(v != root() && key(v) < key(v.parent())) : swap(v.parent(),v)

  v = v.parent() 

  - Time : O(log n) : 높이 (level의 수)

 

⁃ Removal Min from heap (Heapify)

  1. first 노드를 last 노드와 swap 한다

  2. last 노드를 delete 한다

  3. DownHeap 하며 Heap을 만든다

  Time : O(log n) : swap : O(1) + downHeap : O(log n)

  Space : O(1)

 

⁃ DownHeap(Node v) : 노드를 아래로 내려 Heap으로 만든다

  while(v != leaf() && key(v) > u = min{key(left(v)), key(right(v))} ) : swap(v,u)

  v = u

  - Time : O(log n) : 높이(level의 수)

 

⁃ PQ sort(used Heap)

  - Space : O(n)

  - Time : O(n log n) : removeMin Time(O(log n)) * n개

 

⁃ In-place Heap sort (in-place  insertion sort와 비슷)

⁃ 1. 먼저 Max 값을 찾은 다음 UpHeap을 한다 : O(n)

⁃ 2. max 노드와 last 노드를 swap 한다

⁃ 3. 그렇게 max노드가 마지막으로 유지된다

⁃ 4. 동일 방법으로 남은 노드중 max 값을 찾아 동일하게 마지막노드와 swap 한다

⁃ Time : O(n log n) : logn + log(n-1) + … + log 1

⁃ Space : O(1)

 

⁃ Heap Construction by insertion

⁃ 노드가 추가될때마다 minHeap을 맞춘다

⁃ 새로 추가된 노드는 마지막 위치에 들어가며 바로 upHeap을 비교하게 된다

⁃ Time : O(n log n) : O(1) + O(2) + … + O(log n)

 

⁃ Bottom up Heap construction

⁃ 정렬된 양옆의 트리를 통해 상단의 노드를 아래로 내리는 식으로 정렬한다

⁃ 매 노드마다 DownHeap을 비교

⁃ level (h-1) : 2^(h-1) ✕ O(1)번

⁃ level (h-i) : 2^(h-i) ✕ O(i)번

⁃ level (h-h) : 2^(h-h) ✕ O(h)번

⁃ 따라서 합산 S = 시그마 2^(h-i) ✕ O(i) (i=1~h) 여기서 상수 2^(h)를 빼면

⁃ S = 1/2 + 2/4 + 3/8 + …

⁃ S/2 = 1/4 + 2/8 + …

⁃ S-S/2 = S/2 = 1/2 + 1/4 + 1/8 + …

⁃ 따라서 합산 S = 2로 수렴한다

⁃ 따라서 최종 O(2^h) = O(2^(log2 n)) = O(n)이 된다

⁃ Time : O(n)

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

Stack (based Array) 실습  (0) 2020.10.26
Singly Linked List 실습문제  (0) 2020.10.26
Array 실습문제  (0) 2020.10.26
List 실습  (0) 2020.10.26
Array 실습  (0) 2020.10.26