08/21

이우석·2023년 8월 21일
0

SBS 국기수업

목록 보기
21/120

바이너리 서치 트리 + 파일 읽기 복습
바이너리 서치 트리를 상속하는 class를 하나 만든다
이 class는 엑셀이나, 텍스트, XML 파일(택 1)을 읽어와 Key 값만 콘솔화면에 뿌려주고
콘솔에서 Key를 입력받아 해당하는 Key의 모든 값을 뿌려주는 코드 만들기

힙 트리 구현

힙 트리
힙 트리는 힙 정렬을 위해 쓰인다.
힙 트리는 완전 이진 트리의 형태를 가진다.
다만 힙 트리는 depth 가 낮은 것이 더 작은 값을 가지긴 하지만 left와 right 중 어느 쪽이 더 크고 작은지는 알 수 없다.
완전 이진트리의 형태를 가지므로 height와 node 개수, depth에 아래와 같은 식이 성립한다.
depth = height - 1
특정 depth의 node 개수 = 2^(h-1)

힙 트리 삽입
완전 이진 트리의 형태를 가지도록 왼쪽부터 빈 depth가 없도록 채워넣는다.
이후 up heap 을 통해 재정렬한다.

up heap
새로 추가된 키 값을 부모 키 값과 비교한다. 새로 추가된 키 값이 더 작으면 부모와 위치를 바꾼다.
이를 부모가 없어지거나(루트) 부모값이 더 작을 때까지 반복한다.

힙 트리 제거
삭제할 노드와 가장 최근 추가된 노드의 위치를 변경한다.
이제는 라스트 노드에 위치하게 된 삭제할 노드를 지운다.
삭제할 노드 자리에 들어가게된 노드에 대해서 down heap으로 정렬한다.

down heap
새 자리에 들어간 노드의 자식들 중 더 작은 키 값을 가진 자식 노드와 현재 노드를 비교한다.
현재 노드가 더 작으면 현재 노드와 자식 노드의 위치를 바꾼다
이를 현재 노드가 external 노드가 되거나 자식 노드의 값이 더 클 때까지 반복한다.

인덱스 찾는 법
숫자 n의 바로 위에 적힌 숫자: (n-1)/2
숫자 n의 왼쪽 아래의 숫자: 2n + 1
숫자 n의 오른쪽 아래의 숫자: 2n + 2

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace LearnAlgorithm_DataStructure
{
    public struct hNode<K, V> where K : IComparable
    {
        public K Key;
        public V Value;

        public hNode(K key, V value)
        {
            Key = key;
            Value = value;
        }
    }

    class MyHeapTree<K, V> where K : IComparable
    {
        private List<hNode<K, V>> Nodes = new List<hNode<K, V>>();
        public int Count { get { return Nodes.Count; } }

        public void Add(K key, V val)
        {
            if (IsKeyExist(key))
            {
                Console.WriteLine($"Add : 이미 존재하는 키({key})입니다.");
                return;
            }

            Nodes.Add(new hNode<K, V>(key, val));
            UpHeap();
        }

        public hNode<K, V> Pop()
        {
            int index = 0;

            hNode<K, V> node = Nodes[index];

            ChangeNodeByIndex(index, Nodes.Count - 1);
            Nodes.RemoveAt(Nodes.Count - 1);
            DownHeap(index);

            return node;
        }

        public hNode<K, V> Pop(K key)
        {
            int index = FindIndex(key);

            if(index < 0)
            {
                Console.WriteLine($"Remove : 존재하지 않는 키({key})입니다.");
                return default;
            }

            hNode<K, V> node = Nodes[index];

            ChangeNodeByIndex(index, Nodes.Count - 1);
            Nodes.RemoveAt(Nodes.Count - 1);
            DownHeap(index);

            return node;
        }

        /// <summary>
        /// key 값으로 Nodes 에서의 index 값 찾기, 없으면 -1
        /// </summary>
        /// <param name="key">키 값</param>
        /// <returns>해당 키 값의 노드를 가지고 있는 Nodes의 인덱스 번호. 없으면 -1</returns>
        private int FindIndex(K key)
        {
            for (int i = 0; i < Nodes.Count; i++)
            {
                hNode<K, V> node = Nodes[i];
                if (key.Equals(node.Key))
                {
                    return i;
                }
            }

            return -1;
        }

        private bool IsKeyExist(K key)
        {
            return FindIndex(key) != -1;
        }

        private void UpHeap(int index = -1)
        {
            if(index < 0)
            {
                index = Nodes.Count - 1;
            }

            int upIndex = GetparentIndex(index);
            while (upIndex >= 0 && index != upIndex)
            {
                if (Nodes[upIndex].Key.CompareTo(Nodes[index].Key) < 0)
                {
                    break;
                }

                ChangeNodeByIndex(index, upIndex);

                index = upIndex;
                upIndex = GetparentIndex(index);
                if(upIndex < 0)
                {
                    break;
                }
            }
        }

        private void DownHeap(int index)
        {
            int leftIndex = GetLeftIndex(index);
            int rightIndex = GetRightIndex(index);
            int downIndex;

            if(rightIndex >= Count)
            {
                downIndex = leftIndex;
            }
            else
            {
                if(Nodes[leftIndex].Key.CompareTo(Nodes[rightIndex].Key) < 0)
                {
                    downIndex = leftIndex;
                }
                else
                {
                    downIndex = rightIndex;
                }
            }

            if (downIndex < Nodes.Count)
            {
                if (Nodes[index].Key.CompareTo(Nodes[downIndex].Key) > 0)
                {
                    ChangeNodeByIndex(index, downIndex);
                    DownHeap(downIndex);
                }
            }
        }

        private static int GetparentIndex(int index)
        {
            return (index - 1) / 2;
        }

        private static int GetLeftIndex(int index)
        {
            return index * 2 + 1;
        }

        private static int GetRightIndex(int index)
        {
            return index * 2 + 2;
        }

        private void ChangeNodeByIndex(int index1, int index2)
        {
            hNode<K, V> tmp = Nodes[index1];
            Nodes[index1] = Nodes[index2];
            Nodes[index2] = tmp;
        }

        public void Print(int index = 0, string locate = "ROOT")
        {
            Console.WriteLine($"({Nodes[index].Key}, {Nodes[index].Value}) : {locate}");

            int leftIndex = GetLeftIndex(index);
            if (leftIndex <= Nodes.Count - 1)
            {
                Print(leftIndex, locate + " L");
            }
            else
            {
                return;
            }

            int rightIndex = GetRightIndex(index);
            if (rightIndex <= Nodes.Count - 1)
            {
                Print(rightIndex, locate + " R");
            }
            else
            {
                return;
            }
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LearnAlgorithm_DataStructure
{
	internal class Program
	{
		static void Main()
		{
			//ArrayListTest();
			//LinkedListTest();
			//QueueTest();
			//StackTest();
			//QueueArrayTest();
			//StackArrayTest();
			//BinaryTreeTest();
			//BinarySearchTreeTest();
			//BinarySearchTreeExcel();
			HeapTest();


            Console.ReadKey();
		}

		static void HeapTest()
		{
			var tree = new MyHeapTree<int, string>();
			Console.WriteLine("=== Add : 100, 50, 51, 49, 150, 151, 149, 149 ===");
			tree.Add(100, "나보기가");
			tree.Add(50, "역겨워");
			tree.Add(51, "가실");
			tree.Add(49, "때에는");
			tree.Add(150, "죽어도");
			tree.Add(151, "눈물아니");
			tree.Add(149, "흘리오리다");

			tree.Add(149, "흘리오리다"); // 동일 키 입력 시 메시지 확인
			Console.WriteLine();

			tree.Print();
			Console.WriteLine();


			Console.WriteLine("=== Pop ===");
			tree.Pop();
			tree.Print();
			Console.WriteLine();


			Console.WriteLine("=== Pop : 49 ===");
			tree.Pop(49); // 존재하지 않는 키 입력 시 메시지 확인
			Console.WriteLine();


			Console.WriteLine("=== Pop : 150 ===");
			tree.Pop(150);
			tree.Print();
			Console.WriteLine();


			Console.WriteLine("=== Remove : 100 ===");
			tree.Pop(100);
			tree.Print();
			Console.WriteLine();

		}
	}
}
profile
게임 개발자 지망생, 유니티 공부중!

0개의 댓글