이진탐색트리의 완결판!!!!
프로그래밍 초짜가 이거 짜냐고 마음에 병들었다.. ㅋㅋㅋ 스트레스 많이 받아서..
밑에 이진탐색트리는 삽입/삭제/순환,반복순회(전위,중위,후위)/파일로부터 이진탐색트리 불러오기 파일로 저장하기 다 된다. 참고로 레포트 사이트에서 몇천원 주고 구해도 요렇게 기능 많은 소스는 못 받을 소스다.. 물론 내가 코딩한게 약간 이상한 부분도 많지만...
혹시나 나 처럼 자료구조 레포트로 이진탐색트리 코딩해야되는 사람들은 꼭~ 검색 밑에 소스가 걸리기를 바란다... 내가 허접하게 주석도 달았다. ㅋㅋ 필요하면 파일도 같이 다운 받아기를...
/*
-File name : binarytree.c
-Compiler : Microsoft Visual C++ (ANSI C)
다른 컴파일러에서는 오류가 발생할 수도 있음.
*/
/*********************************
* 자료구조 레포트 *
* 이름
* 학번 :
* 학년 :
* 학과 : 컴퓨터과학전공 *
* 작성일 : 05. 4. 7 ~ 05. 4. 20 *
*********************************/
/******************************************************************
* [*]프로그램에 사용된 주요 표준 함수 설명 *
* <> (void *)malloc(size_t n_size) *
* -n_size 만큼 메모리를 할당 일반형 포인터로 주소 반환한다. *
* 메모리 할당 실패시에는 NULL값을 반환한다. *
* <> int fscanf(FILE *s, const char *format, ...) *
* -*s로부터 정보를 받아 오며 스티림의 끝에 왔을 때는 EOF를 *
* 반환한다. EOF(End of file) *
* <> int fprintf(FILE *s, const char *format, ...) *
* -fscanf와는 반대로 *s(스트림)으로 *format을 입력한다. *
* <> void exit(int status) *
* -프로그램을 정상 종료 시킨다. *
******************************************************************/
/*
아래 '#'이 붙은 것들은 컴파일러 실행전 전처리가 먼저 읽어들여
프로그램에 삽입 또는 변환(기호상수)을 하면 최종적으로 컴파일러 처리하고 링커가
실행파일을 만든다.
*/
#include <stdio.h>
#include <stdlib.h> //malloc() 동적 메모리 할당 함수를 사용하기 위해 include.
#define MAX_SIZE 100
/*프로그램 곳곳에 퍼져 있는 최대 값을 기호상수(매크로정의)로 정의하므로서
한번에 값을 수정할 수 있어 혼란를 줄이고 어떤 의미인지 쉽게 의미 전달.
그러나 MAX_SIZE 100이 int형인지 정확히 정의할 수는 없다.*/
/* structure(구조체)는 java, c++의 class 개념과 매우 유사하며 사용법 역시 비슷하다.
java, c++ 에서는 new연산자로 구조체와 비슷한 개념의 class의 객체의 메모리를 할당한다면
c에서도 이와 유사하게 malloc()로 해당 구조체의 실체 메모리 공간을 할당할 수도 있다.*/
typedef struct {
int hakbun;
char name[20];
short int year; //정수 4 이상 입력은 대부분 없므으로 short형
char department[25];
} element;
struct tree {
element data;
struct tree *left_child, *right_child;
};
//가독성 또는 사용의 편리성을 위해 형정의
typedef struct tree node, *tree_pointer;
//tree_pointer형의 주소를 최대 MAX_SIZE만큼 저장하는 배열
tree_pointer queue[MAX_SIZE];
tree_pointer stack[MAX_SIZE];
int top = -1; //stack pointer -1로 초기
tree_pointer search_parent(tree_pointer tree, element data) {
tree_pointer parent;
//입력받은 학번을 트리 학번과 비교 삽입될 위치를 찾고 해당 위치의 부모노드 반환.
while(tree) {
//3가지의 경우가 있다. 데이터가 큰경우, 작은 경우, 예외.
if(tree->data.hakbun > data.hakbun ) {
parent = tree;
//반복문을 돌면서 tree에 NULL값이 들어감으로 parent에 먼저 값을 저장해 놓는다.
tree=tree->left_child;
}
else if(tree->data.hakbun < data.hakbun) {
parent = tree;
tree=tree->right_child;
}
else {
printf("이미 존재하는 학번입니다.\n");
exit(1);
}
}
return parent;
}
void add_node(tree_pointer *tree, element data) {
tree_pointer ptr, parent;
/*아래 malloc()에서 할당되는 메모리크기는 node구조체에
있는 각각의 변수형들의 byte 합과 동일하다.*/
if((ptr = (tree_pointer)malloc(sizeof(node)))==NULL) {
//ptr에 NULL이 들어가는 경우는 메모리 Full 또는 기타 예외상황
fprintf(stderr, "동적메모리 할당 실패!!\n");
/* stdout(모니터), stdin(키보드) 스트림과 마찬가지로 stderr도 기본 에러출력
스트림이며 특이한 것은 버퍼를 사용하지 않고 바로 모니터에 에러 메세지를
출력한다.*/
exit(1);
}
ptr->data=data;
ptr->left_child = ptr->right_child = NULL;
/*포인터 변수를 NULL로 초기화 시키지 않으면 쓰레기 값으로 인해
segment fault를 발생시킬 수 있다.*/
if(*tree) {
//printf("Tree pointer is not null\n");
parent = search_parent(*tree, data);
//search_parent()는 입력 받은 데이터가 삽입될 부모노드를 찾아 준다.
//입력된 학번과 search_parent()의해 구해진 부모 노드의 학번을 비교
if(parent->data.hakbun > data.hakbun)
parent->left_child = ptr; //입력된 학번이 작으면 부모의 왼쪽 sub트리.
else if(parent->data.hakbun < data.hakbun)
parent->right_child = ptr; //입력된 학번이 크면 부모의 오른쪽 sub트리.
else
printf("이미 존재하는 학번입니다.\n");
}
else {
//printf("Tree pointer is null\n");
*tree=ptr;
/*이중포인터에 *(역참조)연산자를 붙였음으로 값으로 갖고 있는 주소를
malloc()에서 반환 동적메모리 주소로 넣는다.*/
}
}
element delete_max(tree_pointer* ptr)
{
element max;
tree_pointer temp;
/*양쪽 자식을 모두 갖고 있는 경우 삭제 방법은 왼쪽 자식 sub트리 중 가장 큰 값을 삭제할
위치에 놓는 것이다. 그러므로 링크를 변경하는 힘든 작업은 없다.
다만 삭제될 왼쪽 sub트리 중 가장 큰 값의 노드의 왼쪽 자식 링크만 변경한다.*/
if ((*ptr)->right_child == NULL) {
max = (*ptr)->data; //삭제될 트리의 왼쪽 sub트리 중 값이 가장 노드를 max저장
temp = *ptr; //sub트리 가자어 오늘 단말 노드를 temp에 저장
*ptr = (*ptr)->left_child;
/* *ptr은 삭제될 트리의 왼쪽 sub트리 중 가장 큰 값을 갖고 있는 트리이며
자신의 원래 값은 max에 저장해 반환시키고 자신의 위치에는 자신의
왼쪽 자식을 넣는다.*/
free(temp); //연결이 끊어진 동적메모리를 해제시켜 메모리 낭비를 없앤다.
return max; //max 노드 값을 리턴해 삭제할 노드에 값을 바꿔준다.
} else {
//오른쪽 자식이 있을 경우 delete_max()에 오른쪽 자식을 넣고 재호출한다.
return delete_max(&(*ptr)->right_child);
// &(*ptr)의 의미는 이중포인터 값 주소를 가리키는 주소를 뜻한다.
}
}
void delete_node(tree_pointer* ptr, int value)
{
if (*ptr) {
//삭제될 학번과 트리의 학번을 비교 작으면 왼쪽 sub트리로 함수 재호출
if (value < (*ptr)->data.hakbun) {
delete_node(&(*ptr)->left_child, value);
//삭제될 학번과 트리의 학번을 비교 크면 오른쪽쪽 sub트리로 함수 재호출
} else if (value > (*ptr)->data.hakbun) {
delete_node(&(*ptr)->right_child, value);
}
//트리 노드의 좌/우 자식이 없으면 *ptr을 NULL 넣어 삭제된다.
else if ((*ptr)->left_child == NULL && (*ptr)->right_child == NULL)
*ptr = NULL;
//트리 노드의 왼쪽 자식이 없으면 *ptr에 오른쪽 자식을 넣어 자신은 삭제된다.
else if ((*ptr)->left_child == NULL)
*ptr = (*ptr)->right_child;
//트리 노드의 오른쪽 자식이 없으면 *ptr에 왼쪽 자식을 넣어 자신은 삭제된다.
else if ((*ptr)->right_child == NULL)
*ptr = (*ptr)->left_child;
else { // 트리에 좌/우 자식이 모두 붙어 있는 경우.
(*ptr)->data = delete_max(&(*ptr)->left_child);
//삭제될 위치에 왼쪽 sub트리 중에 가장 큰 값으로 바꾼다.
}
}
}
//~~~~~~~~~~~ I'm with you~~~~~~~~~~~ ^,.^;;;
//직접 Data를 입력 받는 함수
void input_data(tree_pointer *tree, element data) {
printf("학번 : ");
scanf("%d", &data.hakbun);
printf("이름 : ");
scanf("%s", &data.name);
printf("학년 : ");
scanf("%d", &data.year);
printf("학과(전공) : ");
scanf("%s", &data.department);
add_node(&(*tree), data);
}
// data.txt로부터 저장된 내용을 불러오는 함수
void load_file(tree_pointer *tree, element data) {
FILE *fp;
int count=0;
if((fp=fopen("data.txt", "r")) == NULL) {
fprintf(stderr, "파일 불러오기 실패!!");
exit(1);
}
printf("\n*******************************************************\n");
printf(" 파일로부터 불러온 학생 명단-\n");
while(fscanf(fp, "%d", &data.hakbun) != EOF) {
//위에도 적었지만 fscanf()는 파일에 끝에 도달하면 -1(EOF)를 반환한다.
printf("-------------------------------------------------------\n");
printf("* 학번 : %d", data.hakbun);
fscanf(fp, "%s", &data.name);
printf(", 이름 : %s", data.name);
fscanf(fp, "%d", &data.year);
printf(", 학년 : %d", data.year);
fscanf(fp, "%s", &data.department);
printf(", 전공 : %s\n", data.department);
add_node(&(*tree), data);
/* "&(*tree)"가 의미하는 것은 이중포인터에 *연산자를 하나 붙임으로서
이중포인터가 갖고 있는 주소 값을 의하고 괄호 밖에 &연산자를 붙임으로서
그 주소값을 가리키는 주소를 뜻한다. 즉 메인에서 정의된 tree포인터의 주소.*/
count++; //행(레코드 또는 튜플)단위로 카운트를 증가시킨다.
}
printf("*******************************************************\n");
printf("**data.txt로부터 [ %d ]명의 학생 정보를 로드했습니다.**\n", count);
printf("*******************************************************\n");
fclose(fp); //파일 스티림을 닫는다.
}
//이 프로그램에서 학생 정보 출력하는 모든 함수는 printStd()를 호출해서 사용한다.
void printStd(tree_pointer tree) {
printf("\n학번 : %d (Primary key)\n", tree->data.hakbun);
printf("----------\n");
printf("이름 : %s\n", tree->data.name);
printf("학년 : %d\n", tree->data.year);
printf("학과(전공) : %s\n", tree->data.department);
}
void pre_order(tree_pointer tree) {
if(tree) {
printStd(tree);
pre_order(tree->left_child); //스택에 쌓이면서 NULL이 나올 때까지 내려 간다.
pre_order(tree->right_child);
// Value→Left→Right
}
}
void in_order(tree_pointer tree) {
if(tree) {
in_order(tree->left_child);
printStd(tree);
in_order(tree->right_child);
// Left→Value→Right
}
}
void post_order(tree_pointer tree) {
if(tree) {
post_order(tree->left_child);
post_order(tree->right_child);
printStd(tree);
// Left→Right→Value
}
}
void queue_full(int *rear) {
if((*rear%MAX_SIZE) == 0)
*rear = MAX_SIZE-1;
//rear가 MAX_SIZE랑 똑같은 값이면 MAX_SIZE 보다 1 작은 수를 넣어준다.
else
*rear -= 1;
//rear가 MAX_SIZE와 값이 틀리면 그냥 1만 감소시켜준다.
fprintf(stderr, "메모리초과 rear %d로 리셋\n", *rear);
}
tree_pointer queue_empty(int *front) {
fprintf(stderr,"\nfront : %d\n", *front);
return queue[*front+1];
//front와 rear가 같아지면 front를 1증시켜 리턴시켜준다.
}
//deleteq()는 front를 1증가 시켜 메모리에 한 노드의 저장된 주소를
tree_pointer deleteq(int *front, int rear) {
if(*front == rear)
return queue_empty(front); //오류 키를 반환
queue[*front]=NULL;
*front = (*front+1) % MAX_SIZE; //나머지 연산으로 환형을 만든다.
//MAX_SIZE와 동일할 경우를 제외하고는 모두 (*front+1)값이 *front에 들어간다.
return queue[*front];
}
//add queue
int addq(int front, int *rear, tree_pointer item) {
*rear = (*rear+1) % MAX_SIZE;
//MAX_SIZE와 동일할 경우를 제외하고는 모두 (*rear+1)값이 나온다.
if(front == *rear) {
queue_full(rear);
return 0;
}
queue[*rear] = item;
return 0;
}
//반복적 레벨 순서 트리 순회(환형큐 사용)
int level_order(tree_pointer ptr) {
int front = 0, rear = 0;
//front, rear를 모두 0으로 초기화시킨다.(데이타를 배열의 1번 부터 들어간다.)
if(!ptr) return 0;
//인자 ptr의 값이 비어 있으면 리턴시켜 호출한 함수로 돌아간다.
addq(front, &rear, ptr);
/*현재 노드를 큐메모리에 추가한다. rear의 주소 값을 매개변수 넣어주는 이유는 addq()에서
증가된 rear의 값을 그대로 유지하기 위해서*/
for(;;) {
ptr = deleteq(&front, rear);
//front값을 1증가 시켜 addq로 입력시킨 노드를 ptr에 넣는다.
if(ptr) { //queue_empty()에서 NULL값을 반환할 때까지 반복.
printf("\n");
printStd(ptr); //현재에 ptr노드의 data 화면에 출력
if(ptr->left_child)
addq(front, &rear, ptr->left_child);
if(ptr->right_child)
addq(front, &rear, ptr->right_child);
}
else break;
}
printf("rear : %d\n", rear);
return 0;
}
tree_pointer stack_empty(int *top) {
fprintf(stderr, "\n스택포인터 %d에 위치!!\n", *top);
return stack[*top];
}
int push(int *top, tree_pointer item)
{
if(*top >= MAX_SIZE-1) {
fprintf(stderr,"스택 메모리 꽉!!\n");
return 0;
}
stack[++*top] = item;
//++가 *top 앞에 있므로 1증가한 자리에 item이 들어간다.
return 0;
}
tree_pointer pop(int *top) {
if(*top == -1)
return stack_empty(top);
return stack[(*top)--]; //*top번째 있는 원소를 반환하고 *top값을 감소시킨다.
}
//반복적 전위
void iter_preorder(tree_pointer node) {
for(;;) {
/*for문의 반복 조건은 node에 값이 항상 들어 있고 후위식은
한번 반복 수행 완료후 node의 값을 왼쪽 자식 node로 바꿔 준다.*/
for(;node; node=node->left_child) {
//우선 트리 왼쪽의 모든 노드를 스택에 다 넣어 준다.
printStd(node);
push(&top, node);//node를 스택에 넣고 top를 1 증가 시킨다.
}
node=pop(&top);//nodedp top가 가리키는 곳의 node를 넣고 top는 1 감소한다.
if(!node) break;
node=node->right_child;
//위에 for문에서 왼쪽 노드를 넣어 둔 것들의 오른쪽 자식을 하나씩 넣어 반복한다.
}
}
//반복적 중위
void iter_inorder(tree_pointer node) {
for(;;) {
for(;node; node=node->left_child)
push(&top, node);
node=pop(&top);
if(!node) break;
printStd(node);
node=node->right_child;
}
}
//반복적 후위
/*void iter_postorder(tree_pointer node) {
// ㅡ,.ㅡ;; ~ Happy~ Happy~ Happy~
}*/
//이진트리를 순환 방식으로 복사합니다.
tree_pointer copy(tree_pointer original) {
tree_pointer temp; //temp에 오리지널 트리가 저장되어 호출시킨 함수로 리턴.
if(original) {
if((temp = (tree_pointer)malloc(sizeof(node))) == NULL) {
fprintf(stderr,"동적 메모리 할당 실패!!\n"); exit(1);
}
temp->left_child = copy(original->left_child);
//순환적으로 original의 왼쪽 자식을 temp의 왼쪽 자식에 넣는다.
temp->right_child = copy(original->right_child);
//역시 함수의 순환(스택메모리)를 이용 오늘쪽 자식을 저장해 나간다.
temp->data = original->data;
return temp;
}
return NULL;
}
//원본과 복사본 비교 동일한 경우 1을 반환한다.
int equal(tree_pointer first, tree_pointer second) {
return((!first && !second)|| // 둘 다 똑같이 값이 없을 경우 참이다.
(first && second && (first->data.hakbun == second->data.hakbun)) &&
//둘 다 값이 들어 있고 주키(primary key) 학번 값이 똑같아야 참이다.
equal(first->left_child, second->left_child) &&
//위에 내용을 재귀함수로 sub트리 까지 비교한다.
equal(first->right_child, second->right_child));
/*전체적인 내용은 이렇다.
둘다 값이 없을 경우 또는(c, c++, java 등등 언어에서 or(||)연산은 앞에 조건이 맞으면
뒤는 검사하지 않고 참으로 판단한다. 그러므로 뒤에 and연산은 아무런 연산도 없다.)
둘다 값이 존재 그리고(and(&&) 연산은 앞, 뒤 조건이 모두 참일 때 참이다.)
두 개의 학번이 동일 그리고 sub트리까지 모두 같으면 참(1)을 반환한다.*/
//c언어 에서는 0을 제외하고 모든 것을 참으로 판단한다. -1도 참이다.
}
void traversal(tree_pointer tree) {
int sel;
while(sel) {
printf("\n***************************************\n");
printf("* <*>순환 순회(재귀함수) *\n");
printf("* (1). 전위, (2). 중위, (3). 후위 *\n");
printf("* <*>반복 순회(Loop제어문) *\n");
printf("* (4). 전위, (5). 중위, (6). 후위 *\n");
printf("* (7). 레벨순회(환형큐),(0). Exit *\n");
printf("***************************************\n");
while(1) {
printf("번호 선택 : ");
rewind(stdin);
scanf("%d", &sel);
if(0 > sel || sel > 7)
printf("\n\n**장난하냐? 0~7 사이만 누르라고 몇번을 말해!!**\n\n");
else break;
}
switch(sel) {
case 1 :
printf("\n전위순회 :\n");
pre_order(tree);
printf("\n**전위순회(Value→Left→Right)완료!**\n");
break;
case 2 :
printf("\n중위순회 : \n");
in_order(tree);
printf("\n**중위순회(Left→Value→Right)완료!**\n");
break;
case 3 :
printf("\n후위순회 : \n");
post_order(tree);
printf("\n**후위순회(Left→Right→Value)완료!**\n");
break;
case 4 :
printf("\n반복적 전위순회 : \n");
iter_preorder(tree);
printf("\n**반복적 전위순회완료!**\n\n");
break;
case 5 :
printf("\n반복적 중위순회 : \n");
iter_inorder(tree);
printf("\n**반복적 중위순회완료!**\n\n");
break;
case 6 :
printf("\n반복적 후위순회 : \n");
//iter_postorder(tree);
printf("요건 어려워.. 손으로 그려 봐요\n");
printf("\n**반복적 후위순회완료!**\n\n");
break;
case 7 :
printf("\n레벨순서순회 : \n");
level_order(tree);
printf("\n**레벨순서순회(hight level→low level)완료!**\n\n");
break;
default :
break;
}
}
printf("\n");
}
//전위순회 순으로 fp파일 스트림에 tree포인터의 내용을 쓴다.
void start_save(tree_pointer tree, FILE *fp) {
if(tree) {
fprintf(fp, "%d ", tree->data.hakbun);
//여기서 fp를 stdout(모니터)스트림으로 변경하면 일반 printf()와 동일한 효과.
fprintf(fp, "%s ", tree->data.name);
fprintf(fp, "%d ", tree->data.year);
fprintf(fp, "%s\n", tree->data.department);
start_save(tree->left_child, fp);
start_save(tree->right_child, fp);
}
}
void save_data(tree_pointer tree) {
FILE *fp;
if((fp=fopen("out_data.txt","w")) == NULL) {
fprintf(stderr, "파일 열기 실패!!");
exit(1);}
printf("\n\n**파일은 전위순회 순으로 저장됩니다**\n");
start_save(tree, fp);//함수의 순환방법으로 파일 저장.
printf("out_data.txt 파일에 저장 완료!!\n");
printf("Have a nice day~\n");
fclose(fp);//열린 파일 스트림을 닫아 준다.
}
int main() {
element data;
tree_pointer tree = NULL, clone=NULL;
/*밑에서 포인터 tree를 함수 매겨변수로 넣을 때 트리가 갖고 있는 주소 값을
넘기는 것이 아니고 주소값을 갖고 있는 메모리의 주소를 넘겨주는 이유는
초기 값이 NULL임으로 값을 넘겨주면 "Segment fault"를 발생시킨다.
즉, 따른 애가 사용하고 있는 메모리를 침범한다. 이 실수는 문법의 문제가 아니다 그래서
상황에 따라서는 컴파일러가 못 찾을 때있다. o/s(window,unix,linux)에서는 세그먼트펄트를
감지해서 에러메세지 출력하고 프로세스를 강제 종료시키거나 어떤 조치에 들어간다. */
//tree_pinter형은 sturct tree *동일.
char sel; //메뉴 선택 문자를 받기 위해 선언된 변수.
int value; //노드 삭제시 학번을 입력 받기 위해 선언된 변수.
printf("*♡*♥*♡*♥*♡*♥*♡*♥*♡*♥*♡*♥*♡*♥*\n");
printf("* [평 택 대 학 교] *\n");
printf("* ------ ------- -------- ------------ *\n");
printf("* B i n a r y S e a r c h *\n");
printf("* ttttttt rrrrrr eeeee eeeee *\n");
printf("* tt rr rr ee ee *\n");
printf("* tt rrrrr eeeee eeeee *\n");
printf("* tt rr rr ee ee *\n");
printf("* tt rr rr eeeee eeeee *\n");
printf("* (Field : 학번, 이름, 학년, 학과) *\n");
printf("*******************************************\n");
printf("\n\n*복사, 순회, 삭제는 Data 입력 또는 로드후 사용*\n");
while(sel != 'x') {//sel입력된 문자가 'x'가 아닌 경우 조건은 참.
printf("입력(i)/파일로드(f)/복사(c)/순회(t)/삭제(d)/종료(x) : ");
rewind(stdin); //fflush()함수와 동일한 역할을 하며 스트림 지시자를 초기화
scanf("%c", &sel);
switch(sel) {
case 'i' :
input_data(&tree, data); //데이타를 직접 입력받기 위한 함수 호출.
break;
case 'f' :
load_file(&tree, data); //외부 파일을 불러오는 함수 호출.
break;
case 'c' :
clone=copy(tree); //tree를 복사해 결과 주소를 clone 변수에 넣는다.
if(equal(tree,clone)) printf("\n\n**트리 복사 성공!!**\n");
else {printf("\n\n복사 실패!!\n"); break;}
printf("\n\n**복사된 트리 순회해 보기**\n");
traversal(clone); // 복사된 트리를 순회시켜 본다
free(clone); //복사된 트리에 할당된 메모리는 바로 clear 시켜준다.
printf("***복사된 트리 clone은 메모리 해제되었습니다***\n\n");
break;
case 'd' :
printf("사라져 버리길 원하는 학생 학번 : ");
scanf("%d", &value);
delete_node(&tree, value); //삭제될 트리와, 대상 학번을 매겨변수 넣고 호출.
break;
case 't' :
traversal(tree); //트리의 순회만을 모아 놓은 함수 호출.
break;
case 'x' :
break;
default :
printf("\n\n**\'i\', \'f\', \'c\', \'t\', \'d\', \'x\' 한가지 골라 눌러요!**\n\n");
break;
}
}
for(;tree!=NULL;) { //저장 질문은 포인터 tree에 뭔가 들어 있을 때만 질문한다.
printf("\n메모리에 적재된 트리 저장하고 종료할까요?(y or n) : ");
rewind(stdin);
scanf("%c", &sel);
if(sel == 'y')
{ save_data(tree); break; }
else if(sel == 'n') break;
else { printf("\n\n\'y\' 또는 \'n\'만 입력!!\n"); continue; }
}
if(tree != NULL)
free(tree); /*tree의 사용이 끝났으므로 할당된 메모리를 clear 시킨다.
그맇지만 free()로 clear 시키지 않아도 프로그램 종료시에는
O/S에서 할당된 메모리를 자동으로 clear 시킨다.*/
printf("\n\n*♡*♥*♡*♥*♡*♥*♡*♥*♡*♥*♡*♥*♡*♥*♡*\n");
printf("* [평 택 대 학 교] *\n");
printf("* ------ ------- -------- ------------ *\n");
printf("* B Y Y EEEE *\n");
printf("* BBB Y Y E E *\n");
printf("* B B Y EEEEEE ~ ~ ~ ~ ~ *\n");
printf("* B B Y E ~ ~ ~ ~ *\n");
printf("* BBB Y EEEE *\n");
printf("* *\n");
printf("* ♬♪You can get it if you really want♬♪ *\n");
printf("* ♬♪But you must try, try and try~♬♪ *\n");
printf("* ♬♪you'll succeed at last♬♪ *\n");
printf("**********************************************\n");
return 0;
}
// End of code
'자료구조 & 알고리즘' 카테고리의 다른 글
연결리스트 구현 문제요 (0) | 2007.03.14 |
---|---|
초보를 위한 프로그래밍 입문서 (0) | 2007.03.14 |
[C 알고리즘] 거품 정렬(Bubble Sort) (0) | 2007.03.14 |