inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

인프런 워밍업 클럽 4기 CS 전공지식 1주차 발자국

H_dong
0

강의 수강

나의 주력 언어는 c++이라서 강의를 토대로 c++버젼으로 바꿔보았다


이진 트리 BinaryTree.cpp

이진 트리의 주요 특징

시간 복잡도 (BST 기준):

#include <iostream>
using namespace std;

class BinaryTree {
private:
	struct Node {
		int data;
		Node* left;
		Node* right;

		Node(int data, Node* left = nullptr, Node* right = nullptr)
			: data(data), left(left), right(right) {}
	};

	Node* root;

public:
	BinaryTree(int data) {
		root = new Node(data);
	}
	
	~BinaryTree() {
		DestroyTree(root);
	}

	Node* GetRoot() const {
		return root;
	}

	Node* GetLeftSubTree() const {
		return root->left;
	}

	Node* GetRightSubTree() const {
		return root->right;
	}

	int getData() const {
		return root->data;
	}

	void printTree() const {
		printTree(root, 0);
	}

private:
	// 노드 탐색 (data 기준)
	Node* FindNode(Node* node, int data) 
	{
		if (node == nullptr) return nullptr;
		if (node->data == data) return node;

		Node* leftSearch = FindNode(node->left, data);
		if (leftSearch) return leftSearch;

		return FindNode(node->right, data);
	}

	// 트리 파괴
	void DestroyTree(Node* node)
	{
		if (node == nullptr) return;

		DestroyTree(node->left);
		DestroyTree(node->right);
		delete node;
	}

	// 전위 순회
	void PreOrderTraversal(Node* node) const
	{
		if (node == nullptr) return;
		cout << node->data << endl;
		PreOrderTraversal(node->left);
		PreOrderTraversal(node->right);
	}

	// 중위 순회
	void InOrderTraversal(Node* node) const
	{
		if (node == nullptr) return;
		InOrderTraversal(node->left);
		cout << node->data << endl;
		InOrderTraversal(node->right);
	}

	// 후위 순회
	void PostOrderTraversal(Node* node) const
	{
		if (node == nullptr) return;
		PostOrderTraversal(node->left);
		PostOrderTraversal(node->right);
		cout << node->data << endl;
	}

public:
	void SetLeftSubTree(BinaryTree* subtree)
	{
		root->left = subtree->root;
	}
	void SetLeftSubTree(int parentData, int data)
	{
		Node* parent = FindNode(root, parentData);
		if (parent) parent->left = new Node(data);
	}

	void SetRightSubTree(BinaryTree* subtree)
	{
		root->right = subtree->root;
	}
	void SetRightSubTree(int parentData, int data)
	{
		Node* parent = FindNode(root, parentData);
		if (parent) parent->right = new Node(data);
	}

	void PreOrderTraversal() const
	{
		PreOrderTraversal(root);
	}

	void InOrderTraversal() const
	{
		InOrderTraversal(root);
	}

	void PostOrderTraversal() const
	{
		PostOrderTraversal(root);
	}

	void printTree(Node* node, int depth) const {
		if (node == nullptr) return;

		// 우측 자식 먼저 출력 (트리의 오른쪽이 위로 가게)
		printTree(node->right, depth + 1);

		// 현재 노드 출력
		for (int i = 0; i < depth; ++i) cout << "    ";
		cout << node->data << endl;

		// 좌측 자식 출력
		printTree(node->left, depth + 1);
	}
};

 

출력 결과

int main()
{
	BinaryTree tree(1);
	tree.SetLeftSubTree(1, 2);
	tree.SetRightSubTree(1, 3);
	tree.SetLeftSubTree(2, 4);
	tree.SetRightSubTree(2, 5);
	tree.SetLeftSubTree(3, 6);
	tree.SetRightSubTree(3, 7);

	cout << "Pre-Order Traversal: \n";
	tree.PreOrderTraversal();

	cout << "In-Order Traversal: \n";
	tree.InOrderTraversal();

	cout << "Post-Order Traversal: \n";
	tree.PostOrderTraversal();

	cout << "\n트리 출력:\n";
	tree.printTree();

	return 0;
}

image


이진 탐색 트리

BST의 주요 특징

시간 복잡도:

BST의 단점:

BinarySearchTree.h

#pragma once

enum class Color
{
	Red = 0,
	Black = 1,
};

struct Node
{
	Node* parent = nullptr;
	Node* left = nullptr;
	Node* right = nullptr;
	int key = {};
	Color color = Color::Black;
};
class BinarySearchTree
{
public:
	void Print() { Print(_root, 10, 0); }
	void Print(Node* node, int x, int y);

	void printTree() const { printTree(_root, 0); }
	void printTree(Node* node, int depth) const;

	Node* Search(Node* node, int key);
	Node* Search2(Node* node, int key);

	Node* Min(Node* node);
	Node* Max(Node* node);
	Node* Next(Node* node);

	void Insert(int key);
	void Delete(int key);
	void Delete(Node* node);
	void Replace(Node* u, Node* v);

private:
	Node* _root = nullptr;
};

BinarySearchTree.cpp

#include "BinarySearchTree.h"
#include <iostream>
#include <Windows.h>

using namespace std;

void SetCursorPosition(int x, int y)
{
	HANDLE output = ::GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { static_cast<SHORT>(x), static_cast<SHORT>(y) };
	::SetConsoleCursorPosition(output, pos);
}

void BinarySearchTree::Print(Node* node, int x, int y)
{
	if (node == nullptr)
		return;

	SetCursorPosition(x, y);

	cout << node->key;
	Print(node->left, x - (5 / (y + 1)), y + 1);
	Print(node->right, x + (5 / (y + 1)), y + 1);
}

void BinarySearchTree::printTree(Node* node, int depth) const
{
	if (node == nullptr) return;

	// 우측 자식 먼저 출력 (트리의 오른쪽이 위로 가게)
	printTree(node->right, depth + 1);

	// 현재 노드 출력
	for (int i = 0; i < depth; ++i) cout << "    ";
	cout << node->key << endl;

	// 좌측 자식 출력
	printTree(node->left, depth + 1);
}

Node* BinarySearchTree::Search(Node* node, int key)
{
	while (node && key != node->key)
	{
		if (key < node->key)
			node = node->left;
		else
			node = node->right;
	}

	return node;
}

Node* BinarySearchTree::Search2(Node* node, int key)
{
	if (node == nullptr || key == node->key)
		return node;

	if (key < node->key)
		return Search(node->left, key);
	else
		return Search(node->right, key);
}

Node* BinarySearchTree::Min(Node* node)
{
	while (node->left)
		node = node->left;

	return node;
}

Node* BinarySearchTree::Max(Node* node)
{
	while (node->right)
		node = node->right;

	return node;
}

Node* BinarySearchTree::Next(Node* node)
{
	if (node->right)
		return Min(node->right);

	Node* parent = node->parent;

	while (parent && node == parent->right)
	{
		node = parent;
		parent = parent->parent;
	}

	return parent;
}

void BinarySearchTree::Insert(int key)
{
	Node* newNode = new Node();
	newNode->key = key;

	if (_root == nullptr)
	{
		_root = newNode;
		return;
	}

	Node* node = _root;
	Node* parent = nullptr;

	while (node)
	{
		parent = node;
		if (key < node->key)
			node = node->left;
		else
			node = node->right;
	}

	newNode->parent = parent;

	if (key < parent->key)
		parent->left = newNode;
	else
		parent->right = newNode;
}

void BinarySearchTree::Delete(int key)
{
	Node* deleteNode = Search(_root, key);
	Delete(deleteNode);
}

void BinarySearchTree::Delete(Node* node)
{
	if (node == nullptr)
		return;

	if (node->left == nullptr)
		Replace(node, node->right);
	else if (node->right == nullptr)
		Replace(node, node->left);
	else
	{
		// 다음 데이터 찾기
		Node* next = Next(node);
		node->key = next->key;
		Delete(next);
	}
}

// u 서브트리를 v 서브트리로 교체
// 그리고 delete u
void BinarySearchTree::Replace(Node* u, Node* v)
{
	if (u->parent == nullptr)
		_root = v;
	else if (u == u->parent->left)
		u->parent->left = v;
	else
		u->parent->right = v;

	if (v)
		v->parent = u->parent;

	delete u;
}

출력 결과

int main()
{
	BinarySearchTree bst;

	bst.Insert(30);
	bst.Insert(10);
	bst.Insert(20);
	bst.Insert(25);
	bst.Insert(26);
	bst.Insert(40);
	bst.Insert(50);

	bst.Delete(20);
	bst.Delete(26);

	bst.Print();
	system("pause");
}

image


AVL Tree

AVL 트리란?
AVL 트리는 높이 균형 이진 탐색 트리(Height-Balanced BST)의 일종으로, 모든 노드에서 왼쪽과 오른쪽 서브트리의 높이 차이가 1 이하가 되도록 균형을 유지하는 트리입니다. 1962년 Adelson-Velsky와 Landis가 고안했으며, 이름도 그들의 이니셜을 따왔습니다.

 

AVL 트리의 주요 특징

 

AVL 트리의 장점

AVL 트리의 단점

AVL 트리는 언제 유용한가?

AVLTree.h

#pragma once
#include <iostream>
class AVLTree
{
public:
	struct Node
	{
		Node* parent = nullptr;
		Node* left = nullptr;
		Node* right = nullptr;
		int key = {};
		int height = 1;
	};

public:
	void Print() { Print(_root, 10, 0); }
	void SetCursorPosition(int x, int y);
	void Print(Node* node, int x, int y);

	void printTree() const { printTree(_root, 0);
	std::cout << "--------------------------" << std::endl;
	}
	void printTree(Node* node, int depth) const;

	Node* Search(Node* node, int key);
	Node* Search2(Node* node, int key);

	Node* Min(Node* node);
	Node* Max(Node* node);
	Node* Next(Node* node);

	void Insert(int key);
	void Remove(int key);

	Node* GetRoot() { return _root; }

private:
	void UpdateHeight(Node* node);
	int GetHeight(Node* node);
	int GetBalance(Node* node);

	Node* RotateRight(Node* node);
	Node* RotateLeft(Node* node);

	Node* Rotate(Node* node, int key);
	Node* GetUnBalanceNode(Node* node);

	Node* Insert(Node* node, int key);
	Node* Remove(Node* node, int key);
	Node* RemoveHelper(Node* node);

	Node* _root = nullptr;
};

AVLTree.cpp

#include "AVLTree.h"
#include <iostream>
#include <Windows.h>

using namespace std;

void AVLTree::SetCursorPosition(int x, int y)
{
	HANDLE output = ::GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos = { static_cast<SHORT>(x), static_cast<SHORT>(y) };
	::SetConsoleCursorPosition(output, pos);
}

void AVLTree::Print(Node* node, int x, int y)
{
	if (node == nullptr)
		return;

	SetCursorPosition(x, y);

	cout << node->key;
	Print(node->left, x - (5 / (y + 1)), y + 1);
	Print(node->right, x + (5 / (y + 1)), y + 1);
}

void AVLTree::printTree(Node* node, int depth) const
{
	if (node == nullptr) return;

	// 우측 자식 먼저 출력 (트리의 오른쪽이 위로 가게)
	printTree(node->right, depth + 1);

	// 현재 노드 출력
	for (int i = 0; i < depth; ++i) cout << "    ";
	cout << node->key << endl;

	// 좌측 자식 출력
	printTree(node->left, depth + 1);
}

AVLTree::Node* AVLTree::Search(Node* node, int key)
{
	while (node && key != node->key)
	{
		if (key < node->key)
			node = node->left;
		else
			node = node->right;
	}

	return node;
}

AVLTree::Node* AVLTree::Search2(Node* node, int key)
{
	if (node == nullptr || key == node->key)
		return node;

	if (key < node->key)
		return Search(node->left, key);
	else
		return Search(node->right, key);
}

AVLTree::Node* AVLTree::Min(Node* node)
{
	while (node->left)
		node = node->left;

	return node;
}

AVLTree::Node* AVLTree::Max(Node* node)
{
	while (node->right)
		node = node->right;

	return node;
}

AVLTree::Node* AVLTree::Next(Node* node)
{
	if (node->right)
		return Min(node->right);

	Node* parent = node->parent;

	while (parent && node == parent->right)
	{
		node = parent;
		parent = parent->parent;
	}

	return parent;
}



void AVLTree::UpdateHeight(Node* node)
{
	int lh = GetHeight(node->left);
	int rh = GetHeight(node->right);
	node->height = max(lh, rh) + 1;
}

int AVLTree::GetHeight(Node* node)
{
	return node ? node->height : 0;
}

int AVLTree::GetBalance(Node* node)
{
	return node ? GetHeight(node->left) - GetHeight(node->right) : 0;
}

//		[childNode]
//	[node]			[3]
// [1] [2]

//		[node]
//	[1]		[childNode]
//			[2]		[3]
AVLTree::Node* AVLTree::RotateRight(Node* node)
{
	Node* childNode = node->left;

	node->left = childNode->right;

	if (childNode->right != nullptr)
		childNode->right->parent = node;

	childNode->parent = node->parent;

	if (node->parent == nullptr)
		_root = childNode;
	else if (node == node->parent->right)
		node->parent->right = childNode;
	else
		node->parent->left = childNode;

	childNode->right = node;
	node->parent = childNode;

	UpdateHeight(node);
	UpdateHeight(childNode);

	return childNode;
}

AVLTree::Node* AVLTree::RotateLeft(Node* node)
{
	Node* childNode = node->right;

	node->right = childNode->left;

	if (childNode->left != nullptr) 
		childNode->left->parent = node;

	childNode->parent = node->parent;

	if (node->parent == nullptr)
		_root = childNode;
	else if (node == node->parent->left)
		node->parent->left = childNode;
	else
		node->parent->right = childNode;

	childNode->left = node;
	node->parent = childNode;

	UpdateHeight(node);
	UpdateHeight(childNode);

	return childNode;
}

AVLTree::Node* AVLTree::Rotate(Node* targetNode, int data)
{
	int balance = GetBalance(targetNode);
	bool isRootNode = false;
	if (targetNode == _root)
		isRootNode = true;

	if (balance < -1 && data > targetNode->right->key)	  // LL
	{
		targetNode = RotateLeft(targetNode);
	}
	else if (balance > 1 && data < targetNode->left->key) // RR
	{
		targetNode = RotateRight(targetNode);
	}
	else if (balance > 1 && data > targetNode->left->key) // LR
	{
		targetNode->left = RotateLeft(targetNode->left);
		targetNode = RotateRight(targetNode);
	}
	else if (balance < -1 && data < targetNode->right->key) // RL
	{
		targetNode->right = RotateRight(targetNode->right);
		targetNode = RotateLeft(targetNode);
	}

	if (isRootNode)
	{
		_root = targetNode;
	}

	return targetNode;
}

AVLTree::Node* AVLTree::GetUnBalanceNode(Node* node) {
	if (!node) return nullptr;
	int balance = GetBalance(node);
	if (balance > 1 || balance < -1)
		return node;
	return nullptr;
}


void AVLTree::Insert(int key)
{
	_root = Insert(_root, key);
}

AVLTree::Node* AVLTree::Insert(Node* node, int key)
{
	if (!node) return new Node{ nullptr, nullptr, nullptr, key, 1 };
	if (key < node->key) 
	{
		node->left = Insert(node->left, key);
		if (node->left) node->left->parent = node;
	}
	else if (key > node->key)
	{
		node->right = Insert(node->right, key);
		if (node->right) node->right->parent = node;
	}
	else
	{
		return node;
	}
	
	UpdateHeight(node);
	return Rotate(node, key);
}

void AVLTree::Remove(int key)
{
	_root = Remove(_root, key);
}

AVLTree::Node* AVLTree::Remove(Node* node, int key)
{
	if (!node) return nullptr;

	if (key == 5)
		int a = 0;
	if (key < node->key)
	{
		node->left = Remove(node->left, key);
	}
	else if (key > node->key)
	{
		node->right = Remove(node->right, key);
	}
	else
	{
		node = RemoveHelper(node);
	}
	if (!node) return nullptr;

	UpdateHeight(node);
	Node* unbalanced = GetUnBalanceNode(node);
	if (unbalanced)
		return Rotate(unbalanced, key);
	return node;
}

AVLTree::Node* AVLTree::RemoveHelper(Node* node)
{
	if (!node->left || !node->right) // 자식 노드가 하나 이하일 경우
	{
		Node* temp = node->left ? node->left : node->right;
		if (!temp)
		{
			// 자식이 아예 없는 경우
			delete node;
			return nullptr;
		}
		else
		{
			// 자식이 하나 있는 경우 -> 그 자식으로 자신을 대체
			*node = *temp;
			delete temp;
			return node;
		}
	}
	else
	{
		// 자식이 둘 다 있는 경우
		Node* temp = Min(node->right); // 오른쪽 서브트리에서 가장 작은 값 (후계자)
		node->key = temp->key;
		node->right = Remove(node->right, temp->key);
		return node;
	}
}

출력 결과

int main() {
	AVLTree tree;

	cout << "== 삽입 ==" << endl;
	tree.Insert(10);
	tree.printTree();
	tree.Insert(20);
	tree.printTree();
	tree.Insert(30);
	tree.printTree();
	tree.Insert(25);
	tree.printTree();
	tree.Insert(5);
	tree.printTree();
	tree.Insert(1);
	tree.printTree();
	tree.Insert(7);
	tree.printTree();
	tree.Insert(60);
	tree.printTree();
	tree.Insert(15);
	tree.printTree();

	cout << endl;

	cout << "== 삭제 ==" << endl;
	tree.Remove(30); // 자식 2개 있는 노드 삭제
	tree.Remove(10); // 루트 노드 삭제
	tree.Remove(1);  // 리프 노드 삭제

	tree.printTree();
	cout << endl;

	return 0;
}

imageimage

알고리즘 · 자료구조 자료구조 알고리즘 워밍업클럽4기 CS전공지식

답변 0