해결된 질문
작성
·
155
1
안녕하세요. 이 강의를 완강하고 부록 강의도 수강하다 의문이 생겨 질문드립니다. 이 강의와는 좀 거리가 있는 질문이긴 하지만 부록 강의에 올리면 질문 확인에 시간이 오래 걸릴것 같아 부득이하게 여기에 올립니다. 해당 코드는 제가 이진 트리(부록 강의 17.17)예제의 일부를 풀어본 것입니다.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define TMAX 20
#define MAXITEM 20
#define SPACING 30
void initialize(Tree* ptree);
bool empty_tree(const Tree* ptree);
bool full_tree(const Tree* ptree);
int item_count(const Tree* ptree);
int compare(const Item first, const Item second);
Node* make_node(const Item* pi);
// 요점
void move_to_index(movies** Head, int index); // 구현 목표
bool add_item(const Item* pi, Tree* ptree);
//
typedef struct
{
char character[TMAX];
char name[TMAX];
} Item;
typedef struct node
{
Item item;
struct node* left;
struct node* right;
} Node;
typedef struct tree
{
Node* root;
int size;
} Tree;
// 출력 함수
void print_item(Item item)
{
printf("%s (%s)\n", item.character, item.name);
}
void print_graph(Node* root, int space)
{
space += SPACING;
if (root == NULL)
{
for (int i = SPACING; i < space; i++)
printf(" ");
printf("NULL");
return;
}
print_graph(root->right, space);
printf("\n");
for (int i = SPACING; i < space; i++)
printf(" ");
print_item(root->item);
print_graph(root->left, space);
}
void print_tree(Tree* ptree)
{
printf("\n--------Print Start--------\n");
print_graph(ptree->root, 0); // 그래프 출력
printf("\n---------Print End---------\n");
}
// 출력 함수
int main()
{
Item items[] = {
{"Iron Man","Tony Stark"},
{"Thor","Thor Odinson"},
{"Ant Man","Hank Pym"},
{"Wasp","Janet van Dyne"},
{"Hulk","Bruce Banner"},
{"Captain America","Strve Rogers"},
{"Scarlet Witch","Wanda Maximoff"},
{"Black Widow","Natasha Romanoff"},
{"Dr. Strange","Stephen Strange"},
{"Daredevil","Matthew Murdock"},
{"Punisher", "Frank Castle"},
{"Batman","Bruce Wayne"}
};
int n = sizeof(items) / sizeof(items[0]);
Tree tree;
initialize(&tree);
for (int i = 0; i < n; ++i)
if (!add_item(&items[i], &tree))
break;
print_tree(&tree);
return 0;
}
// 편집 함수
static Node* make_node(const Item* pi)
{
Node* MakeNode = (Node*)malloc(sizeof(Node));
MakeNode->item = *pi;
MakeNode->left = MakeNode->right = NULL;
return MakeNode;
}
void initialize(Tree* ptree)
{
ptree->root = NULL;
ptree->size = 0;
}
bool empty_tree(const Tree* ptree)
{
if (ptree->size == 0)
return true;
return false;
}
bool full_tree(const Tree* ptree)
{
if (ptree->size == MAXITEM)
return true;
return false;
}
int item_count(const Tree* ptree)
{
return ptree->size;
}
int compare(const Item first, const Item second)
{
return strcmp(first.character, second.character);
}
우선 이 질문에 별로 필요하지 않는 부분은 지우고 하나의 소스파일에 모았습니다. 여기서 제가 구현하려고한 함수는 move_to_index입니다.
우선 add_item함수 코드입니다.
bool add_item(const Item* pi, Tree* ptree)
{
if (full_tree(ptree))
{
puts("Can't add more item.");
return false;
}
if (ptree->root == NULL)
{
ptree->root = make_node(pi);
ptree->size++; return true;
}
Node* AddItem = ptree->root; // 코드 가독성 위해 Node* 변수 사용
while (1)
{
int comp = compare(AddItem->item, *pi);
if (comp == 0)
{
puts("Can't add duplicated item.");
return false;
}
if (comp > 0)
{
if (AddItem->left != NULL)
AddItem = AddItem->left;
else
{
AddItem->left = make_node(pi); ptree->size++; return true;
}
}
else
{
if (AddItem->right != NULL)
AddItem = AddItem->right;
else
{
AddItem->right = make_node(pi); ptree->size++; return true;
}
}
}
}
해당 함수는 트리에 새로운 노드를 추가하는 함수로 별 이상없이 작동합니다. 하지만 저는 이 함수에서 노드를 추가할 위치까지 이동하는 while 반복문을 사용자가 목표로 하는 노드까지 이동하는 별개의 함수로 구현하여 이를 노드의 추가 뿐만 아니라 삭제, 검색등 여러 작업에 사용하려고 했습니다.
// 구현 실패
int goto_index(const Item* pi, Tree* ptree)
{
Node* Index = ptree->root;
while (1)
{
int comp = compare(Index->item, *pi);
if (comp == 0)
return 0;
if (comp > 0) // comp == 1
{
if (Index->left == NULL)
{
ptree->root = Index;
return 1;
}
Index = Index->left;
}
else // comp == -1
{
if (Index->right == NULL)
{
ptree->root = Index;
return -1;
}
Index = Index->right;
}
}
}
bool add_item(const Item* pi, Tree* ptree)
{
if (full_tree(ptree))
{
puts("Can't add more item.");
return false;
}
if (ptree->root == NULL)
{
ptree->root = make_node(pi);
ptree->size++;
return true;
}
int AddItem = goto_index(pi, ptree); // goto_index로 ptree의 포인터 이동 실패. 첫 노드 포인터 유지
if (AddItem == 0)
{
puts("Can't add duplicated item.");
return false;
}
if (AddItem > 0)
ptree->root->left = make_node(pi);
else
ptree->root->right = make_node(pi);
ptree->size++;
return true;
}
하지만 해당 코드는 물론이고 매개변수를 이차원 포인터나 Tree* 형 변수로 바꿔보는 등 여러 시도를 해봤지만 모두 작동하질 않았습니다. 이 문제의 원인이 무엇인가요? 뭔가 풀릴듯 하면서 풀리지 않아 참 답답합니다. 답변 부탁드립니다.
답변 1
0
안녕하세요 :)
우선 첫 번째 코드에 있는
// 요점
void move_to_index(movies** Head, int index); // 구현 목표
와 세 번째 코드의
// 구현 실패
int goto_index(const Item* pi, Tree* ptree)
중 전자는 오타라고 가정하고, 질문자님께서
int goto_index(const Item* pi, Tree* ptree) 를 구현한다고 가정하고 답변드리겠습니다.
네, 질문이 이해는 갑니다만,
제 생각에는 굳이 goto_index가 필요할까 싶습니다.
goto_index를 통해 특정 위치의 노드를 수정한다면
Binary Search Tree의 자료 정렬 상태가 어지럽혀져 본연의 성능을 낼 수 없게 되겠지요.
당연히 임의의 위치에 노드를 추가하면 검색도 오래 걸릴 것이구요.
한 마디로, 이진 탐색 트리의 정의에 어긋나게 될 겁니다.
개인적으로 goto_index를 만들지 않고, 그러니까 트리의 자료 정렬상태를 훼손하지 않고,
애초에 정렬된 상태에 새 노드를 추가하고(AddItem) 검색하고(TreeSearch) 삭제(DeleteItem)하는 것을 추천드립니다.
그렇게 되면 goto_index 함수의 기능은 검색., 즉 TreeSearch와 같아지겠지요.
정리하자면,
1. goto_index 함수를 통해 특정 위치에 노드를 삭제하고 추가하면 BST의 정의/성능에 위배되므로 하지 않는 것이 좋습니다.
2. goto_index 함수의 기능은 특정 노드의 검색, 즉 강의 내의 TreeSearch 함수와 같으므로 코드도 다를 바 없어야 한다고 봅니다.
오랫동안 오류를 붙잡고 고민하고, 시행착오를 겪으셨던 그 시간들이 모이고 모여
큰 보상으로 다가가실 겁니다.
감사합니다.
새해 복 많이 받으세요!
냅 답변 감사드립니다! 새해 복 많이 받으세요!