• 카테고리

    질문 & 답변
  • 세부 분야

    프로그래밍 언어

  • 해결 여부

    해결됨

다중 구조체로 구성된 자료구조에서 위치를 이동하는 함수 구현에 대한 질문입니다.

22.02.02 14:00 작성 조회수 104

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

강민철님의 프로필

강민철

2022.02.03

안녕하세요 :)

우선 첫 번째 코드에 있는 

// 요점
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 함수와 같으므로 코드도 다를 바 없어야 한다고 봅니다.

 

오랫동안 오류를 붙잡고 고민하고, 시행착오를 겪으셨던 그 시간들이 모이고 모여 

큰 보상으로 다가가실 겁니다.

 

감사합니다.

 

새해 복 많이 받으세요!

 

KoKo님의 프로필

KoKo

질문자

2022.02.04

냅 답변 감사드립니다! 새해 복 많이 받으세요!