2. 과제수행일지
소속 조원
B2 조장 : 고충욱 자료조사 : 최완철,최수영 프로그래밍 : 강승우,최규준
과제수행기간 4일
I. 계획의 작성
연구제목 TREE
연구배경 트리에 대해 이해하고, 트리를 이용하여 상호참조 생성기를 만들 수 있다.
참고 서적 C로 배우는 쉬운 자료구조 이지영 출판사 한빛미디어
참고자료
http://blog.naver.com/ctpoyou?Redirect=Log&logNo=10493277
8-트리의 정의와 트리의 종류
참고 URL
http://internet512.chonbuk.ac.kr/datastructure/tree/tree8.ht
m-트리의 순회방법
II. 계획의 실행
첫째 날 2012년 5월 3일 목요일
오늘의 작업 조원의 업무 분담과 학습할 내용 및 과제에 대한 이해와 숙지
조장 : 고충욱
자료조사 : 강승우,최완철,최수영
프로그래밍 : 허규준
토의 내용
위와 같이 조원의 업무 분담을 하였으며 과제를 위한 자료조사와 프로그래밍에 대한 내
용을 인식하고 개별적으로 분담을 해서 조사를 하고 이해를 해 온 다음 그것을 조원들
에게 설명해주는 것으로 방향을 잡았다.
과제준비에서 이번과제의 내용을 보니 자료조사가 많이 필요할 것 같아서 자료조사를 중점으로 해야
느낀 점 겠다는 생각을 가지게 되었다.
3. 둘째 날 2012년 5월 8일 목요일
오늘의 작업 학습할 내용에 대한 설명 및 이해
Tree(나무구조)의 정의
⊙ 1개 이상의 유한한 개수의 노드의 집합
⊙ 루트 노드와 0개 이상의 겹치지 않는 하위 나무 구조들의 집합으로 이루어짐
path : edge에 의해 연결된 node들의 집합
leaf(잎) : 자식이 없는 node(최하위 계층)
forest :루트를 제외한 나머지 부분
subtree : 큰 tree에 속한 작은 tree
node의 degree : 하위 subtree의 개수, 어느 특정노드의 자식 수
node의 level : root node 부터 최하위 node까지의 중첩되지 않은 path의 node 개수
노드들의 자식 수 중에 가장 큰 자식 수
1) 노드 (Node)
노드는 트리를 구성하는 기본 요소를 말한다. 즉 아래의 그림에서 본다면 a에서 g까지
의 각 요소
모두가 노드가 된다.
2) 근(Root)
트리의 가장 높은 레벨에 있는 노드를 루트 노드 또는 근 노드라 한다. 이 루트 노드
는 모든 노드의
조상이 된다. 아래의 그림에서 a 노드는 루트 노드가 된다.
3) 레벨(level)
레벨은 각 노드가 근노드와 얼마만큼 떨어져 있는가를 알기 위해 상용한다. 예를 들어
토의 내용 아래의
그림에서 e 노드는 루트노드와 3계층 떨어져 있으므로 level은 3이 된다.
4) 부모 노드와 자식 노드
parent node, child node라 불린다. 서로 아래 위로 붙어 있는 노드로 상위의 노드가
부모 노드가 된다. 하위의 노드는 자식노드가 되고 여러개의 자식노드는 하나의 부모
노드만 가질 수 있다.
아래의 그림에서 e 노드와 g노드는 각각부모 노드 자식이 된다.
5) 형제 노드
sibling node라 불린다. 같은 부모 노드를 갖는 자식 노드들은 형제들의 집합으로 구성
된다.
아래의 그림에서 b노드와 c노드는 sibling nod가 된다.
Binary Tree(이진 나무, 이진 트리)의 정의
모든 내부 node들이 둘 이하의 자식node를 갖는 나무, 노드가 하나도 없는 공집합 이
거나
root node를 기준으로 왼쪽 이진나무, 오른쪽 이진나무로 이루어진 집합
⊙ Complete Binary tree(완전 이진나무)
가장 마지막 level을 제외한 모든 node들이 꽉 차있고 마지막 level은 왼쪽 부터 마지
막
node까지 빈 칸이 없는 tree
4. ⊙ Full Binary Tree(포화 이진나무)
마지막 level까지 완전히 꽉 차있는 이진 트리를 말함
이진 나무 순회(Tree Traverse)
위와 같은 트리가 있다고 한다면 각각 순회방법은 다음과 같습니다.
⊙ 전위 순회(preorder traverse) : 뿌리(root)를 먼저 방문
⊙ 중위 순회(inorder traverse) : 왼쪽 하위 트리를 방문 후 뿌리(root)를 방문
⊙ 후위 순회(postorder traverse) : 하위 트리 모두 방문 후 뿌리(root)를 방문
⊙ 층별 순회(level order traverse) : 위 쪽 node들 부터 아래방향으로 차례로 방문
전위 순회 : 0->1->3->7->8->4->9->10->2->5->11->6
중위 순회 : 7->3->8->1->9->4->10->0->11->5->2->6
후위 순회 : 7->8->3->9->10->4->1->11->5->6->2->0
층별 순회 : 0->1->2->3->4->5->6->7->8->9->10->11
★전위 순회는 뿌리->왼쪽 자식->오른쪽 자식 순
5. ★중위 순회는 왼쪽자식-> 뿌리-> 오른쪽 자식
★후위 순회는 왼쪽자식->오른쪽 자식-> 뿌리
★층별 순회는 그냥 노드의 순서대로
과제준비에서 트리에 대한 개념은 어느 정도 이해 할 수 있었으나 이것을 사용하고 구현하기 위해서
느낀 점 는 아직 많은 것이 부족한 것 같다. 더 많은 공부가 필요한 것 같다.
셋째 날 2012년 5월 10일 목요일
오늘의 작업 자료조사 내용
트리의 순회
이진 트리의 순회는 일정한 순서로 트리의 모든 노드들을 오직 한번씩만 방문하는 것이
다. 트리의 순회는 트리 구조로 된 정보의 검색이나 수식 처리등에 유용하게 사용된다.
특정 노드에서 자신의 왼쪽 서브 트리를 모두 방문하고, 자기 노드를
방문한 후에 오른쪽 서브 트리를 방문한다. 이 원리를 모든 노드에 재
귀적으로 적용하면 모든 노드들을 한번씩 방문할 수 있다.
토의 내용
특정 노드에서 먼저 자기 노드를 방문하고, 왼쪽 서브 트리를 모두
6. 방문하고, 마지막으로 오른쪽 서브 트리를 모두 방문한다. 이 원리를
모든 노드에 재귀적으로 적용하면 모든 노드를 한번씩 방문할 수 있
다.
특정 노드에서 자신의 왼쪽 서브 트리와 오른쪽 서브 트리를 차례로
방문한 후, 마지막으로 자신의 노드를 방문한다. 이 원리를 모든 노드
에 재귀적으로 적용하면 각 노드를 한번씩 방문할 수 있다.
과제준비에서 순회에 대해 공부하면서 어떤걸 써야지 효과적일지에 대해 생각을 많이 하게 되었다.
느낀 점 그리고 순회를 구현하기 위해서 어떻게 할지 더 많은 회의가 필요할 것 같다.
7. 넷째 날 2012년 5월 17일 목요일
오늘의 작업 소스파악
/************************************************************************
* cross_reference_generator.c
* 단어의 빈도수 및 단어가 위치한 줄번호를 출력하는 프로그램
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
typedef short bool;
#define FALSE 0
#define TRUE 1
typedef struct _WORDNODE *PWORDNODE;
typedef struct _LINENODE *PLINENODE;
typedef struct _LINENODE // 단어가 출현시 그 라인번호에 대한 구조체.
{
int line_number; // 라인번호.
PLINENODE next_node; // 다음 노드
초안 } LINENODE;
typedef struct _WORDNODE // Binary Search Tree의 노드 구조체.
{
char word[32]; // 단어.
int frequency; // 빈도수
PLINENODE pLine_node; // 단어가 나오는 라인번호 (Linked
List로 구현)
PWORDNODE left_child; // 왼쪽자식노드
PWORDNODE right_child; // 오른쪽자식노드
} WORDNODE;
/************************************************************************
* Function Prototype Declare
************************************************************************/
// 노드를 삽입
void InsertNode( PWORDNODE* pRoot, char* aKey, int aLineNumber );
/* 같은 단어가 존재시에는 해당 노드 반환 혹은
* 존재 하지 않을 시 삽입 할 위치의 parent node의 포인터 반환 */
PWORDNODE Search( PWORDNODE pRoot , char* key ,bool* exist_same);
8. /* 사전 순서대로 단어, 빈도수, 그리고 총 단어수 출력
* (inorder-traversal) */
void PrintTree( PWORDNODE pRoot );
int main( int argc , char* argv[] )
{
PWORDNODE pRoot = NULL ; // 트리의 루트
char string[256] = {0}; // 텍스트파일 한 라인의 버퍼
char* ptr_key; // 토큰된 단어에 대한 포인터
char* ptr_lwr_key; // lower key , 소문자로 변환된 단어
의 포인터
int line_number = 0 ; // 해당 라인 번호
FILE* in_file; // 읽어들일 파일 구조체.
if ( argc != 2 || strlen(argv[1]) == 0) {
printf("Usage : %s filenamen" , argv[0]);
return 0;
}
in_file = fopen(argv[1],"r"); // 파일을 읽기전용 모드로 연다.
if (!in_file) {
fprintf(stderr,"Can not open a file. : %s n",argv[1]);
exit(1);
}
while ( !feof(in_file) ) // in_file이 끝에 도달 할 때까지 아래 내용을 실행.
{
memset(string,0,sizeof(string)); // 한 라인에 대한
버퍼를 초기화.
fgets( string , sizeof(string),in_file); // 파일로 부터 라인을 읽어옴
++line_number; //
라인번호 증가.
/*************************************************************************
strtok , strlwr 사용법은 C reference manual참조 바람.
************************************************************************/
// 문자열에서 separator로 단어들을 추출
ptr_key = strtok( string, "
`~!@#$%^&*()_-=+[]{};:'"|/?,.<>tn" );
while( ptr_key != NULL )
{
ptr_lwr_key = strlwr(ptr_key);
// 해당 단어를 소문자로 변환
// 해당단어와 라인번호를 binary search 트리에 삽입
9. InsertNode( &pRoot , ptr_lwr_key , line_number );
// 다음 단어 추출.
ptr_key = strtok( NULL, "
`~!@#$%^&*()_-=+[]{};:'"|/?,.<>tn" );
}
}
if ( pRoot ) // 트리에 노드가 존재 한다면.
{
printf("-------------------------------------------------
-------------------n");
printf("%-19s|%-11s|%-19sn", " 단어" , " 빈도수"," 라인");
printf("-------------------------------------------------
-------------------n");
PrintTree( pRoot ); // 트리 내용 출력.
printf("-------------------------------------------------
-------------------n");
}
fclose(in_file); // 파일을 닫음.
return 0;
}
/************************************************************************
* 사전 순서대로 단어, 빈도수, 그리고 총 단어수 출력 (inorder-traversal)
************************************************************************/
void PrintTree( PWORDNODE pRoot )
{
PLINENODE tmpLineNode; // 해당 단어의 라인번호를 출력하기 위
한 임시 포인터
if ( pRoot ) // 루트가 널이 아니라면
{
tmpLineNode = pRoot->pLine_node ; // tmpLineNode는 라인노드
를 가르킴
PrintTree( pRoot->left_child ); // 왼쪽 자식노드 출
력.
// 중간, 즉 루트 노드 출력.
// 해당 노드의 단어와 빈도수 출력
printf(" %-18s|%9d |", pRoot->word , pRoot->frequency );
while ( tmpLineNode ) { // tmpLineNode가 널일때 까지.
// 라인번호 출력
printf("%4d",tmpLineNode->line_number);
// 임시포인터는 링키드리스트의 다음 노드를 가리키게 함
tmpLineNode = tmpLineNode->next_node;
}
printf("n");
10. PrintTree( pRoot->right_child ); // 오른쪽 자식노드 출력
}
}
/************************************************************************
* 같은 단어가 존재시에는 해당 노드 반환 혹은
* 존재 하지 않을 시삽입 할 위치의 parent node의 포인터 반환
************************************************************************/
PWORDNODE Search( PWORDNODE pRoot , char* key ,bool* exist_same)
{
PWORDNODE preNode = pRoot; // parent 노드를 기억 할 임시 변수
while ( pRoot ) // 해당노드가 널일때 까지.
{
preNode = pRoot; //
해당노드를 기억.
if ( strcmp( key, pRoot->word ) == 0 ) // 해당노드가 같은 단어의 노
드일경우
{
*exist_same = TRUE; // 같은것
이 있다는것을 true로 표시
return pRoot; // 그 노드
를 반환.
}
if ( strcmp( key, pRoot->word ) < 0) // 키가 해당노드의 단어보다
앞쪽이라면
pRoot = pRoot->left_child; // 왼쪽 자
식노드로 이동.
else
pRoot = pRoot->right_child; // 뒤쪽의 단어라면
오른쪽 자식노드로 이동
}
// 널이라면 그 전 노드(parent 노드)를 반환
return preNode;
}
/************************************************************************
* Tree에 노드를 삽입. Binary Search Tree로 구성.
************************************************************************/
void InsertNode( PWORDNODE* pRoot, char* aKey, int aLineNumber )
{
bool exist_same_word = FALSE; // 같은 단어가 트리내에 존재할시
true
// 같은 단어가 트리 내에 존재 하면 그 단어에 대한 노드,
// 없을 시에 새로 삽입될 위치의 parent 노드
PWORDNODE tmpWordNode = Search( *pRoot , aKey
,&exist_same_word);
PWORDNODE insertWordNode = NULL; // 트리에 삽입 될 노드
PLINENODE ptrLineNode = NULL; // 줄번호를 저장 할 노드.
11. /************************************************************
* key가 tree내에 존재할시.
************************************************************/
if ( exist_same_word )
{
// 반환된 노드 즉, tmpWordNode는 Key 에 대한 노드
// 노드 내의 빈도수 증가
++(tmpWordNode->frequency);
// 라인번호에 대한 링키드 리스트의 끝 부분으로 이동.
ptrLineNode = tmpWordNode->pLine_node;
while( ptrLineNode->next_node )
ptrLineNode = ptrLineNode->next_node;
// 새로운 라인노드를 붙여준다.
ptrLineNode->next_node =
(PLINENODE)malloc(sizeof(LINENODE));
ptrLineNode->next_node->line_number = aLineNumber ; // 라인번
호 저장
ptrLineNode->next_node->next_node = NULL; //
다음노드는 NULL로 처리.
}
else if ( tmpWordNode || !(*pRoot) )
{
/************************************************************
* key가 트리내에 없음.
************************************************************/
//////////////////////////////////////////////////////////////////////////
// 새로운 노드 생성, 라인번호와 단어를 저장.
insertWordNode = (PWORDNODE)malloc(sizeof(WORDNODE));
if (!insertWordNode) {
fprintf(stderr,"The Memory is fulln");
exit(1);
}
memset( insertWordNode , 0 , sizeof(WORDNODE)); //
초기화
strcpy( insertWordNode->word , aKey );
// 삽입될 노드에 키 복사
++(insertWordNode->frequency);
// 빈도수 증가.
// 새로운 라인노드를 붙여준다.
insertWordNode->pLine_node =
(PLINENODE)malloc(sizeof(LINENODE));
// 해당단어가 출현한 라인번호 저장.
insertWordNode->pLine_node->line_number = aLineNumber ;
// 다음노드는 NULL로 처리.
insertWordNode->pLine_node->next_node = NULL;
12. //////////////////////////////////////////////////////////////////////////
// 그 노드를 적절한 위치에 삽입
if (*pRoot) // 루트가 널이 아니라면
{ // 키가 해당노드의 단어보다 앞쪽이라면
if ( strcmp( aKey, tmpWordNode->word ) < 0)
tmpWordNode->left_child = insertWordNode; //
왼쪽 자식노드에 삽입.
else
tmpWordNode->right_child = insertWordNode;
// 아니라면 오른쪽 노드에 삽입.
}
else // 루트가 널이라면 루트에 해당 노드를 저장.
*pRoot = insertWordNode;
}
}
// cross_reference_generator.c END
단어 빈도수를 찾는 프로그램의 알고리즘은 먼저 단어를 저장할 문자열배열(data), 빈
도수를 나타내는(data), 단어가 등장한 라인을 나타내는 (data), 다음 노드(구조체)를
가르키는 두 개의 포인터(왼쪽,오른쪽 자식)으로 구성되있는 구조체를 사용 하여 이진트
리를 구성하며 프로그램이 진행된다.
먼저 파일의 한 라인을 읽어와서 “`~!@#$%^&*()_-=+[]{};:'"|/?,.<>tn” 앞
의 문자로 끊어 한 단어로 인식 하고 라인 별로 읽기 때문에 다음 라인으로 갈 때 마다
라인넘버를 1씩 증가 시켜 data로 사용 하게 된다.
한 단어가 트리에 이미 저장되어 있는지 중복 검사 후 중복 되는 단어가 있다면 해당
단어가 저장 되있는 구조체에서 빈도수만 증가 시키고, 중복되지 않는다면(처음 등장하
알고리즘 해석
는 단어) 새로운 구조체를 만들어 저장 하고 빈도수, 등장하는 라인도 추가 시킨다.
단어를 추가시키는 방법은 접근한 구조체의 단어보다 앞의 단어이면 왼쪽, 뒤의 단어이
면 오른쪽으로 저장한다.
파일의 모든 문자들을 읽은 후 트리의 저장이 완료 되면, 중위 순회 방법으로 트리의
단어,빈도수,라인넘버를 출력 한다 중위 순회는 출력의 우선순위가 왼쪽 노드의 값>현
재 노드의 값>오른쪽 노드의 값 으로 정해져 있고 이 순서대로 단어를 출력 하면 해당
노드보다 앞의 단어는 왼쪽으로 게속 저장했으므로 이 방법을 사용 하여 사전식순으로
단어를 출력 한다.
출력은 다음 노드의 값이 없을때 까지 반복 하고 프로그램을 종료합니다.
Ⅳ. 반성
과제를 마치면서 과제를 마무리하지 못해 알고리즘을 해석하면서 과제를 마무리하였다.
느낀 점 많은 아쉬움을 가진 프로젝트가 되었다.