08/23

이우석·2023년 8월 23일
0

SBS 국기수업

목록 보기
23/120

AVL 트리 완성

using System;
using System.Collections.Generic;

namespace LearnAlgorithm_DataStructure
{
	public class AVL_Node
	{
		public object Key;
		public object Value;
		public AVL_Node Left;
		public AVL_Node Right;
		public int Height = 1;
		public int Balance { get { return AVL_Tree.GetHeight(Left) - AVL_Tree.GetHeight(Right); } }

		public AVL_Node(object key, object value)
		{
			Key = key;
			Value = value;
			Left = null;
			Right = null;
		}

		public AVL_Node(object key, object value, AVL_Node left, AVL_Node right)
		{
			Key = key;
			Value = value;
			Left = left;
			Right = right;
		}
	}

	class AVL_Tree
	{
		private AVL_Node Root = null;

		public void Add(object newKey, object newValue)
		{
			if (Root == null)
			{
				Root = new AVL_Node(newKey, newValue);
				return;
			}

			Add(newKey, newValue, ref Root);
		}

		private void Add(object newKey, object newValue, ref AVL_Node parentNode)
		{
			int compare = Comparer<object>.Default.Compare(parentNode.Key, newKey);

			if (compare == 0)
			{
				Console.WriteLine($"Add : 동일한 Key({newKey})가 이미 존재합니다.");
				return;
			}

			if (compare > 0)
			{
				if (parentNode.Left == null)
				{
					parentNode.Left = new AVL_Node(newKey, newValue);
					UpdateNode(ref parentNode);
					return;
				}

				Add(newKey, newValue, ref parentNode.Left);
				UpdateNode(ref parentNode);
				return;
			}

			if (compare < 0)
			{
				if (parentNode.Right == null)
				{
					parentNode.Right = new AVL_Node(newKey, newValue);
					UpdateNode(ref parentNode);
					return;
				}

				Add(newKey, newValue, ref parentNode.Right);
				UpdateNode(ref parentNode);
				return;
			}

			Console.WriteLine("Add : 알 수 없는 오류입니다.");
			return;
		}

		public void Remove(object removeKey)
		{
			if (Root == null)
			{
				Console.WriteLine($"Remove : 삭제 할 키({removeKey})를 찾을 수 없습니다. 빈 트리입니다.");
				return;
			}

			Remove(removeKey, ref Root);
		}

		private void Remove(object removeKey, ref AVL_Node node)
		{
			int compare = Comparer<object>.Default.Compare(node.Key, removeKey);

			if (compare > 0)
			{
				if (node.Left == null)
				{
					Console.WriteLine($"Remove : 삭제 할 키({removeKey})를 찾을 수 없습니다.");
					return;
				}

				if(Comparer<object>.Default.Compare(node.Left.Key, removeKey) == 0)
				{
					RemoveNodeFromParent(ref node, true);
					UpdateNode(ref node);
					return;
				}

				Remove(removeKey, ref node.Left);
				UpdateNode(ref node);
				return;
			}

			if (compare < 0)
			{
				if (node.Right == null)
				{
					Console.WriteLine($"Remove : 삭제 할 키({removeKey})를 찾을 수 없습니다.");
					return;
				}

				if (Comparer<object>.Default.Compare(node.Right.Key, removeKey) == 0)
				{
					RemoveNodeFromParent(ref node, false);
					UpdateNode(ref node);
					return;
				}

				Remove(removeKey, ref node.Right);
				UpdateNode(ref node);
				return;
			}

			if(compare == 0)
			{
				// 부모가 없는 경우 ( Root 노드인 경우)
                RemoveNodeFromParent(ref Root, null);
				UpdateNode(ref Root);
				return;
            }

			Console.WriteLine("Remove : 알 수 없는 오류입니다.");
			return;
		}

		private void RemoveNodeFromParent(ref AVL_Node parentNode, bool? removeLeft = true)
		{
			// 삭제할 노드 참조
			AVL_Node removeNode;
            if (removeLeft == true)
			{
				removeNode = parentNode.Left;
			}
			else if(removeLeft == false)
			{
				removeNode = parentNode.Right;
			}
			else
			{
                //null 이면 Root 삭제할 때
                removeNode = Root;
			}

            if (removeNode.Left != null)
			{
				if (removeNode.Right != null)
				{
					// 삭제할 노드가 Left, Right 모두 있는 경우
					// 지워진 노드의 left중 가장 오른쪽의 노드와 그 부모 찾기
					AVL_Node Rightmost = removeNode.Left;
					AVL_Node Rightmost_Parent = removeNode;

					// 위치 바꾼 후 삭제할 때 쓸 값
					bool tmpFlag;
                    if (Rightmost.Right == null)
					{
                        tmpFlag = true; 
                    }
					else
					{
                        tmpFlag = false;
                    }


                    while (Rightmost.Right != null)
					{
						Rightmost_Parent = Rightmost;
						Rightmost = Rightmost.Right;
					}

					// 삭제 할 위치의 값을 왼쪽의 가장 오른쪽 노드의 값으로 변경
					removeNode.Key = Rightmost.Key;
					removeNode.Value = Rightmost.Value;

					// 왼쪽의 가장 오른쪽 노드 위치의 노드는 삭제
					if (tmpFlag)
					{
						Rightmost_Parent.Left = null;
                    }
					else
					{
                        Rightmost_Parent.Right = null;
                    }

                    return;
				}

				// 삭제할 노드가 right는 없고 left만 있는 경우
				removeNode.Key = removeNode.Left.Key;
				removeNode.Value = removeNode.Left.Value;
				removeNode.Left = removeNode.Left.Left;

				return;
			}

			// Right만 있는 경우, Right 쪽을 끌어올림
			if (removeNode.Right != null)
			{
				removeNode.Key = removeNode.Right.Key;
				removeNode.Value = removeNode.Right.Value;
				removeNode.Left = removeNode.Right.Left;
                removeNode.Right = removeNode.Right.Right;

                return;
			}

			// Root 노드를 지우는데, Root가 External Node인 경우
            if(removeLeft == null)
			{
                Root = null;
				return;
            }

            //Left도 Right도 없는 External Node를 지우는 경우
            if(removeLeft == true)
			{
                parentNode.Left = null;
				return;
            }

            parentNode.Right = null;
            return;
		}

		private void UpdateNode(ref AVL_Node node)
		{
			UpdateHeight(ref node);
			Restructure(ref node);
			UpdateHeight(ref node);
		}

		private void UpdateHeight(ref AVL_Node node)
		{
			node.Height = Math.Max(GetHeight(node.Left), GetHeight(node.Right)) + 1;
		}

		public static int GetHeight(AVL_Node node)
		{
			if (node == null)
			{
				return 0;
			}

			return node.Height;
		}

		private void Restructure(ref AVL_Node node)
		{
			if (Math.Abs(node.Balance) <= 1)
			{
				return;
			}

			// 재정리 실행
			if (node.Balance > 0)
			{
				// 좌편향
				if(node.Left != null && node.Left.Balance > 0)
				{
					RotateLL(ref node);
				}
				else
				{
					RotateLR(ref node);
				}
			}
			else
			{
				// 우편향
				if (node.Right != null && node.Right.Balance > 0)
				{
					RotateRL(ref node);
				}
				else
				{
					RotateRR(ref node);
				}
			}
		}

		// 왼쪽으로 연속 2개 편향 시 사용
		private void RotateLL(ref AVL_Node node)
		{
			var newRight = new AVL_Node(node.Key, node.Value, node.Left, node.Right);
			var newCenter = new AVL_Node(node.Left.Key, node.Left.Value, node.Left.Left, node.Left.Right);
			newRight.Left = node.Left.Right;
			newCenter.Right = newRight;
			node = newCenter;

			UpdateHeight(ref node.Right);
		}

		// 오른쪽으로 연속 2개 편향 시 사용
		private void RotateRR(ref AVL_Node node)
		{
			var newLeft = new AVL_Node(node.Key, node.Value, node.Left, node.Right);
			var newCenter = new AVL_Node(node.Right.Key, node.Right.Value, node.Right.Left, node.Right.Right);
			newLeft.Right = node.Right.Left;
			newCenter.Left = newLeft;
			node = newCenter;

            UpdateHeight(ref node.Left);
        }

		private void RotateLR(ref AVL_Node node)
		{
            // first Rotate (for R) make node tree LL
            var L = new AVL_Node(node.Left.Key, node.Left.Value);
            var LR = new AVL_Node(node.Left.Right.Key, node.Left.Right.Value);
			L.Left = node.Left.Left;
			L.Right = node.Left.Right.Left;
            LR.Left = L;
            LR.Right = node.Left.Right.Right;
			node.Left = LR;

            UpdateHeight(ref node.Left.Left);
            UpdateHeight(ref node.Left);

            // Second Rotate (for LL)
            RotateLL(ref node);
        }

		private void RotateRL(ref AVL_Node node)
		{
			// first Rotate (for L) make node tree RR
			var R = new AVL_Node(node.Right.Key, node.Right.Value);
            var RL = new AVL_Node(node.Right.Left.Key, node.Right.Left.Value);
			R.Left = node.Right.Left.Right;
			R.Right = node.Right.Right;
            RL.Left = node.Right.Left.Left;
            RL.Right = R;
			node.Right = RL;

            UpdateHeight(ref node.Right.Right);
            UpdateHeight(ref node.Right);

			// Second Rotate (for RR)
			RotateRR(ref node);
        }

		public void Print()
		{
			Print(Root);
		}

		private void Print(AVL_Node node, string locate = "ROOT")
		{
			if(node == null)
			{
				return;
			}

			Console.WriteLine($"({node.Key}, {node.Value}) {node.Height} {locate}");

			Print(node.Left, locate + " L");
			Print(node.Right, locate + " R");
		}
	}
}
profile
게임 개발자 지망생, 유니티 공부중!

0개의 댓글