ShovelingLife
A Game Programmer
ShovelingLife
전체 방문자
오늘
어제
  • 분류 전체보기 (1078) N
    • 그래픽스 (57)
      • 공통 (19)
      • 수학 물리 (22)
      • OpenGL & Vulkan (1)
      • DirectX (14)
    • 게임엔진 (184)
      • Unreal (69)
      • Unity (104)
      • Cocos2D-X (3)
      • 개인 플젝 (8)
    • 코딩테스트 (221)
      • 공통 (7)
      • 프로그래머스 (22)
      • 백준 (162)
      • LeetCode (19)
      • HackerRank (2)
      • 코딩테스트 알고리즘 (8)
    • CS (236) N
      • 공통 (21)
      • 네트워크 (44)
      • OS & 하드웨어 (56) N
      • 자료구조 & 알고리즘 (98)
      • 디자인패턴 (6)
      • UML (4)
      • 데이터베이스 (7)
    • 프로그래밍 언어 (351) N
      • C++ (168)
      • C# (90)
      • Java (9)
      • Python (35) N
      • SQL (30)
      • JavaScript (8)
      • React (7)
    • 그 외 (10)
      • Math (5)
      • 일상 (5)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

  • Source Code 좌측 상단에 복사 버튼 추가 완료
  • 언리얼 엔진 C++ 빌드시간 단축 꿀팁
  • 게임 업계 코딩테스트 관련
  • 1인칭 시점으로 써내려가는 글들

인기 글

태그

  • C
  • 클래스
  • Unity
  • 알고리즘
  • 티스토리챌린지
  • C++
  • 문자열
  • 그래픽스
  • c#
  • string
  • 배열
  • 언리얼
  • 유니티
  • python
  • 오블완
  • SQL
  • 함수
  • 파이썬
  • 백준
  • 포인터

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
ShovelingLife

A Game Programmer

[C#] 우선순위 큐 개념과 힙을 통한 구현
CS/자료구조 & 알고리즘

[C#] 우선순위 큐 개념과 힙을 통한 구현

2023. 10. 26. 16:15

우선순위 큐란?

큐(Qeueu)는 FIFO(First In First Out) 방식을 따르기 때문에, 입력 순서대로 출력되는 데이터 구조인 반면, 우선순위 큐(Priority Qeueu)는 입력 순서와는 무관하게 우선순위대로 출력되는 데이터 구조다.

시간 복잡도

https://www.bigocheatsheet.com/

우선 순위 큐는 Enqueue()시, 내부적으로 정렬을 해주거나 또는 탐색을 해주어야하는 로직이 필요하다. 우선 순위대로 정렬하거나 탐색하는 방법으로 얼마나 효율적으로 만드느냐가 주제의 핵심 포인트다. 이 글에서는 'O(N)'과 'O(logN)' 두 가지 방식을 다룬다.

우선순위 큐 구현 방법과 종류

이진 탐색'O(logN)'과 단순 선형 탐색 차이'O(N)'

  • Queue의 Enqueue()시, 내부적으로 효율적인 탐색 방법이 필요하다.
  • 간단한 구현 방식으로는 'O(N)'의 복잡도를 가지고 있는 단순 선형 탐색이 있다.
  • 대표적으로 알려진 'O(logN)'의 복잡도를 가지고 있는 이진 탐색 방식이 있다.

스크립트, 단순 선형 탐색 'O(N)'

처음부터 끝까지 우선 순위를 탐색하는 방식으로 길이가 짧을 때 유용하다. 단순 연결 리스트 구조로 구현되어 있어, 이해하기 쉬우며, 복잡하지 않고 무겁지 않은 구조를 가지고 있다. 다만 대량의 큐를 넣어야한다면 추천하지 않는다.

public class PriorityQueue<T>
{
    private class Node
    {
        public T Data { get; private set; }
        public int Priority { get; set; } = 0;

        public Node prve;
        public Node next;

        public Node(T data, int priority)
        {
            this.Data = data;
            this.Priority = priority;
        }
    }

    private Node head = null;
    private Node tail = null;

    public int Count = 0;

    public void Enqueue(T data, int priority)
    {
        ++Count;
        Node newNode = new Node(data, priority);
        if (head != null)
        {
            Node prev = null;
            Node node = head;

            //////////////////////////////
            /// 처음부터 탐색한다. 'O(N)'
            do
            {
                if (node.Priority > priority) break;
                prev = node;
                node = node.next;
            } while (node != null);
            //////////////////////////////

            if (prev != null)
                prev.next = newNode;

            newNode.prve = prev;

            if (node != null)
            {
                if (node.Priority > priority)
                {
                    newNode.next = node;
                    node.prve = newNode;
                }
                else
                {
                    node.next = newNode;
                    newNode.prve = node;
                }
            }

            if (newNode.prve == null)
                head = newNode;

            if (newNode.next == null)
                tail = newNode;
        }
        else
        {
            head = newNode;
            tail = head;
        }
    }

    public T Dequeue()
    {
        --Count;
        Node temp = tail;
        tail = tail.prve;
        if (tail == null) head = null;

        if (tail != null && tail.prve != null)
            tail.prve.next = null;

        if (temp != null)
            return temp.Data;
        return default(T);
    }

    public T Peek()
    {
        if (tail != null)
            return tail.Data;
        return default(T);
    }
}

스크립트, 이진 탐색 방식 'O(logN)'

대량의 큐를 집어 넣어야하는 경우, 이진 탐색 방식을 이용하여 적절한 타겟을 찾는다. 이론상, O(N) 방식보다 효율적이며 범용적으로 쓰이는 방식이다. 다만 조금 아쉬운 부분으로는, List의 Insert()가 들어가기 때문에 비용이 좀 더 나올 수 있다.

using System.Collections.Generic;

public class PriorityQueue<T>
{
    private class Node
    {
        public T Data { get; private set; }
        public int Priority { get; set; } = 0;

        public Node(T data, int priority)
        {
            this.Data = data;
            this.Priority = priority;
        }
    }

    private List<Node> nodes = new List<Node>();

    public int Count => nodes.Count;

    public void Enqueue(T data, int priority)
    {
        Node newNode = new Node(data, priority);
        if (nodes.Count == 0)
        {
            nodes.Add(newNode);
        }
        else
        {
            //////////////////////////////
            // 이진 탐색을 시작한다. 'O(logN)'
            int start = 0;
            int end = nodes.Count - 1;
            int harf = 0;
            while (start != end)
            {
                if (end - start == 1)
                {
                    if (nodes[start].Priority < priority)
                    {
                        harf = end;
                    }
                    else
                    {
                        harf = start;
                    }
                    break;
                }
                else
                {
                    harf = start + ((end - start) / 2);
                    if (nodes[harf].Priority > priority)
                    {
                        // Down
                        end = harf;
                    }
                    else
                    {
                        // Up
                        start = harf;
                    }
                }
            }
            //////////////////////////////

            if (nodes[harf].Priority > priority)
                nodes.Insert(harf, newNode);
            else
                nodes.Insert(harf + 1, newNode);
        }
    }

    public T Dequeue()
    {
        Node tail = null;
        
        if (Count > 0)
        {
            tail = nodes[nodes.Count - 1];
            nodes.RemoveAt(nodes.Count - 1);
        }

        if (tail != null)
            return tail.Data;
        return default(T);
    }

    public T Peek()
    {
        Node tail = null;
        
        if (Count > 0)
            tail = nodes[nodes.Count - 1];

        if (tail != null)
            return tail.Data;
        return default(T);
    }
}

예제

위의 두가지의 스크립트는 아래와 같은 결과를 도출해낸다. 우선 순위 값이 클 수록 우선 순위가 높다.

PriorityQueue<int> priorityQueue = new PriorityQueue<int>();
priorityQueue.Enqueue(9, 1);
priorityQueue.Enqueue(8, 2);
priorityQueue.Enqueue(7, 3);
priorityQueue.Enqueue(6, 0);
while (priorityQueue.Count > 0)
{
    Console.WriteLine(priorityQueue.Dequeue());
}

 

C#, 우선순위 큐 개념과 힙을 통한 구현 (tistory.com)

저작자표시 (새창열림)

'CS > 자료구조 & 알고리즘' 카테고리의 다른 글

[C++] Palindrome (팰린드롬 회문)에 대한 모든것  (0) 2023.11.06
[C++] valarray - 오직 수치값만 저장하는 컨테이너  (0) 2023.11.05
냅색 - meet in the middle (밋 인 더 미들) 알고리즘  (0) 2023.10.25
Boyer-Moore (보이어무어) 알고리즘 / 문자열 탐색  (0) 2023.10.25
KMP (문자열 중 특정 패턴을 찾아내는 탐색) 알고리즘 C++ 구현  (0) 2023.10.25
    'CS/자료구조 & 알고리즘' 카테고리의 다른 글
    • [C++] Palindrome (팰린드롬 회문)에 대한 모든것
    • [C++] valarray - 오직 수치값만 저장하는 컨테이너
    • 냅색 - meet in the middle (밋 인 더 미들) 알고리즘
    • Boyer-Moore (보이어무어) 알고리즘 / 문자열 탐색
    ShovelingLife
    ShovelingLife
    Main skill stack => Unity C# / Unreal C++ Studying Front / BackEnd, Java Python

    티스토리툴바