data structure practice
과제 10-이진탐색트리 9장
greentealover1
2023. 5. 6. 19:53
#include <iostream>
using namespace std;
#define MAX_QUEUE_SIZE 100
class BinaryNode {
int data; // key 값이 됩니다.
BinaryNode* left; // 왼쪽 자식 노드 링크
BinaryNode* right; // 오른쪽 자식 노드 링크
public:
BinaryNode(int val = 0, BinaryNode* l = NULL, BinaryNode* r = NULL)
: data(val), left(l), right(r) {}
~BinaryNode() {}
bool isEmpty() { return this == NULL; };
void setData(int val) { data = val; }
void setLeft(BinaryNode* l) { left = l; }
void setRight(BinaryNode* r) { right = r; }
int getData() { return data; }
BinaryNode* getLeft() { return left; }
BinaryNode* getRight() { return right; }
bool isLeaf() { return left == NULL && right == NULL; }
int getheight() { return isEmpty() ? 0 : getheight(this); }//교재 9장 프로젝트1 의 BinaryNode::getheight실현부분
int getheight(BinaryNode* node) {
if (node == NULL) return 0;
int hleft = node->getheight(node->getLeft());
int hright = node->getheight(node->getRight());
return (hleft > hright) ? hleft + 1 : hright + 1;
}
};
class CircularQueue {
int front;
int rear;
BinaryNode* data[MAX_QUEUE_SIZE];
public:
CircularQueue() { front = rear = 0; }
bool isEmpty() { return front == rear; }
bool isFull() { return(rear + 1) % MAX_QUEUE_SIZE == front; }
void enqueue(BinaryNode* n) {
if (isFull()) cout << "error: 큐가 포화상태입니다" << endl;
else {
rear = (rear + 1) % MAX_QUEUE_SIZE;
data[rear] = n;
}
}
BinaryNode* dequeue() {
if (isEmpty()) cout << "error: 큐가 공백상태입니다" << endl;
else {
front = (front + 1) % MAX_QUEUE_SIZE;
return data[front];
}
}
};
class BinaryTree
{
protected: // Binary Search Tree class에 상속해 주는 멤버 변수입니다.
BinaryNode* root;
public:
BinaryTree() : root(NULL) {}
~BinaryTree() {}
void setRoot(BinaryNode* node) { root = node; } // 내가 원하는 node로 root를 만들어 줍니다.
BinaryNode* getRoot() { return root; }
bool isEmpty() { return root == NULL; }
int getCount() { return isEmpty() ? 0 : getCount(root); }; // 외부에서 사용하는 함수
void inorder() { // 외부에서 사용하면
cout << "inorder: "; // inorder를 사용했다고 콘솔창에 출력한 뒤에,
inorder(root); // root부터 inorder 함수의 recursion을 시작합니다.
}
int getCount(BinaryNode* node) { // 외부에서 위의 getCount() 사용하면, root가 캡슐화되어 있기 때문에, root부터 시작해서 getCount() recursion을 시작할 수 있습니다.
if (node == NULL) return 0;
return 1 + getCount(node->getLeft())
+ getCount(node->getRight());
}
int getheight() { return isEmpty() ? 0 : getheight(root); }//getheight 함수 부분
int getheight(BinaryNode* node) {
if (node == NULL) return 0;
int hleft = node->getheight(node->getLeft());
int hright = node->getheight(node->getRight());
return (hleft > hright) ? hleft + 1 : hright + 1;
}
void levelorder(); // Circular Queue를 이용해 순회(traversal) 합니다.
void inorder(BinaryNode* node);
};
class BinSrchTree : public BinaryTree // BinaryTree class를 상속 받아 옵니다.
{ // root와 모든 멤버 함수들이 상속됩니다.
public:
BinSrchTree(void) {}
~BinSrchTree(void) {}
BinaryNode* search(int key); // 외부에서 쓰는 함수
BinaryNode* search(BinaryNode* n, int key); // 내부에서 recursion을 위한 함수
void insert(BinaryNode* n);
void insert(BinaryNode* r, BinaryNode* n);
void remove(int key);
void remove(BinaryNode* parent, BinaryNode* node);
};
void BinaryTree::inorder(BinaryNode* node) {
if (node != NULL) {
if (node->getLeft() != NULL) inorder(node->getLeft());
cout << " [" << node->getData() << "] "; // node 가 포인터 객체니까 -> 써서 멤버 함수를 호출합니다.
if (node->getRight() != NULL) inorder(node->getRight());
}
}
void BinaryTree::levelorder() {
cout << endl << "levelorder: "; // level order를 시작한다고 알려주고,
if (!isEmpty()) { // 지금 트리가 비어있지 않으면,
CircularQueue q; // 큐를 만들어서
q.enqueue(root); // 큐에 루트를 넣은 뒤
while (!q.isEmpty()) { // 큐에 데이터가 있는 동안
BinaryNode* n = q.dequeue(); // 큐에서 꺼내 와서
if (n != NULL) { // 아래 연산을 수행합니다.
cout << " [" << n->getData() << "] ";
q.enqueue(n->getLeft());
q.enqueue(n->getRight());
}
}
}
cout << endl;
}
BinaryNode* BinSrchTree::search(int key) {
BinaryNode* node = search(root, key);
if (node != NULL) {
cout << "키값은 " << node->getData() << " 노드는 " << node << endl;
return node;
}
else {
cout << "failed. " << endl;
return node;
}
}
BinaryNode* BinSrchTree::search(BinaryNode* n, int key) { //search함수 부분
if (n == NULL) return NULL;//노드 n이 null(비어있으면) null반환
if (key == n->getData()) return n;//key값과 노드 n의 값이 같으면, 바로 반환
else if (key < n->getData())//key값보다 노드n의 값이 크면, 왼쪽 서브트리로 탐색(순환 호출)
return search(n->getLeft(), key);
else
return search(n->getRight(), key);//key값이 노드n보다 크면, 오른쪽 서브트리로 탐색(순환 호출)
}
void BinSrchTree::insert(BinaryNode* n) { // n 포인터 객체 삽입
if (n == NULL) return;
if (isEmpty()) root = n; // 트리가 비어 있으면 root에다 넣고,
else insert(root, n); // root가 아니면 insert 함수 recursion
}
void BinSrchTree::insert(BinaryNode* r, BinaryNode* n) {
if (n->getData() == r->getData()) return; // key값이 unique하기 때문에
else if (n->getData() < r->getData()) { // 내가 넣고자 하는 노드 n의 key 값이 지금 recursion을 하고 있는 노드 r의 키값보다 작으면,
if (r->getLeft() == NULL) r->setLeft(n); // 왼쪽 자식이 비어있으면 왼쪽에 n을 넣고(연결해 주고)
else insert(r->getLeft(), n); // 비어 있지 않으면, 왼쪽 자식 노드를 매개변수로 recursion
}
else {
if (r->getRight() == NULL) r->setRight(n);
else insert(r->getRight(), n);
}
}
void BinSrchTree::remove(int key) {
if (isEmpty()) return;
BinaryNode* parent = NULL;
BinaryNode* node = root;
while (node != NULL && node->getData() != key) {
parent = node;
node = (key < node->getData()) ? node->getLeft() : node->getRight();
}
if (node == NULL) {
cout << "Error: key is not in the tree!" << endl;
return;
}
else remove(parent, node);
}
void BinSrchTree::remove(BinaryNode* parent, BinaryNode* node) {
if (node->isLeaf()) { // leaf 노드일 때는 생각 없이 바로 지우면 됩니다.
if (parent == NULL) root = NULL;
else { // 지금 노드가 parent 노드의 왼쪽 자식인지 오른쪽 자식인지 판단해서 해당 위치를 null로 바꿔 줍니다.
if (parent->getLeft() == node)
parent->setLeft(NULL);
else
parent->setRight(NULL);
}
}
// 둘 중 한 자식 노드가 비어 있을 때, 역시 편하게 삭제합니다.
else if (node->getLeft() == NULL || node->getRight() == NULL) {
BinaryNode* child = (node->getLeft() != NULL) ? node->getLeft() : node->getRight(); // 자식 노드를 임시로 객체 하나 만든 뒤에,
if (node == root) // root를 지우는 경우는 자식을 곧바로 root로 바꿔 줍니다.
root = child;
else { // root가 아닌 경우,
if (parent->getLeft() == node) // 지우고자 하는 node가 node의 부모의 왼쪽인지, 오른쪽인지 판단해서
parent->setLeft(child); // 임시로 저장한 child를 부모 노드에 연결을 해 주면, 지금 삭제하려고 하는 node는 부모로부터 링크가 끊기게 됩니다.
else
parent->setRight(child);
}
}
else { // 왼쪽, 오른쪽 많은 노드를 가진 subtree가 있을 때,
BinaryNode* succp = node; // 오른쪽 subtree의 leftmost (subtree.first)로 바꿔 주기 위한 변수
BinaryNode* succ = node->getRight(); // 이렇게 되면 successor로 바꿔 줍니다.
BinaryNode* succp2 = node; // 왼쪽 subtree의 rightmost로 바꿔 주기 위한 변수
BinaryNode* succ2 = node->getLeft(); // 이렇게 되면 삭제하는 노드는 삭제하는 노드의 predecessor
int leftmost, rightmost;
while (succ->getLeft() != NULL) {// 삭제하고자 하는 노드보다 바로 뒤에 있는(큰) 노드
succp = succ;
succ = succ->getLeft();
}
while (succ2->getRight() != NULL) { // 삭제하고자 하는 노드보다 바로 앞에 있는(작은) 노드
succp2 = succ2;
succ2 = succ2->getRight();
}
leftmost = succ->getData() - node->getData();
rightmost = node->getData() - succ2->getData();
if (leftmost < rightmost) {
if (succp->getLeft() == succ)
succp->setLeft(succ->getRight());
else
succp->setRight(succ->getRight());
node->setData(succ->getData());
node = succ;
}
else if (leftmost > rightmost) {
if (succp2->getRight() == succ2)
succp2->setRight(succ2->getLeft());
else
succp2->setLeft(succ2->getLeft());
node->setData(succ2->getData());
node = succ2;
}
}
delete node;
}
int main()
{
BinSrchTree tree; // Binary Tree class를 상속 받아 옵니다. Base class에 해당하는 Binary Tree class의 객체는 자동으로 생성됩니다.
int temp_in = 0;
int num_of_nodes = 10;
int arr[10];
arr[0] = 35;
arr[1] = 18;
arr[2] = 7;
arr[3] = 26;
arr[4] = 12;
arr[5] = 3;
arr[6] = 68;
arr[7] = 22;
arr[8] = 30;
arr[9] = 99;
cout << "삽입 연산을 수행하여 이진탐색트리를 만듭니다." << endl;
cout << "[삽입연산] : ";
for (int i = 0; i < num_of_nodes; i++) {
tree.insert(new BinaryNode(arr[i])); // 동적으로 생성합니다. 몇 개의 노드를 생성할지 모르니까요.
cout << arr[i] << " ";
}
cout << endl << endl;
tree.inorder();
tree.levelorder();
cout << endl << "BST 정보 출력" << endl;
cout << "노드 개수: " << tree.getCount() << endl;
cout << "트리의 높이:" << tree.getheight() << endl;//test용으로 넣은 코드부분
cout << endl << "26을 찾습니다. " << endl;
tree.search(26);
cout << endl << "25를 찾습니다. " << endl;
tree.search(25);
cout << endl << "Original BST 정보 출력" << endl;
tree.levelorder();
cout << "3 삭제" << endl;
tree.remove(3);
tree.levelorder();
cout << "삭제 후 BST 정보 출력" << endl;
cout << "노드 개수: " << tree.getCount() << endl;
return 0;
}
과제 문제:
1. 첨부의 코드를 이해하고, 첨부 코드를 이용해 BinSrchTree 클래스의 search 함수를 구현하세요(주석 표기된 부분).
2. 1번을 수행한 뒤, 교재 9장 프로그래밍 프로젝트 1번 중 하나인 getHeight 함수(트리의 높이를 구하는 함수)를 구현하세요.