Ch08. 트리(Tree)
08-1. 트리의 개요
비선형 자료구조
트리(Tree)의 접근
트리는 계층적 관계를 표현하는 자료구조이다
트리가 표현할 수 있는 것들

가지를 늘려가며 뻗어나간다


[의사 결정 트리]
트리 관련 용어의 소개

- 노드 : node
트리의 구성요소에 해당하는 A, B, C, D, E와 같은 요소 - 간선 : edge
노드와 노드를 연결하는 연결선 - 루트 노드 : root node
트리 구조에서 최상위에 존재하는 A와 같은 노드 - 단말 노드 : terminal node
아래로 또 다른 노드가 연결되어 있지 않은 C, D, E, F와 같은 노드 - 내부 노드 : internal node
단말 노드를 제외한 노드로 A, B와 같은 노드 
단말 노드는 잎사귀 노드(leaf node) 라고도 불리며,
내부 노드는 단말 노드가 아니라고 하여 비단말 노드(noninternal node) 라고도 불린다
- 노드 A는 노드 B, C, D의 부모 노드(parent node)이다
 - 노드 B, C, D는 노드 A의 자식 노드(child node)이다
 - 노드 B, C, D는 부모가 같으므로, 서로가 서로에게 형제 노드(sibling node)이다
 
- 조상노드(Ancestor node)
특정 노드의 위에 위치하는 모든 노드 - 후손 노드(Descendent node)
특정 노드의 아래에 위치하는 모든 노드 
이진 트리와 서브 트리
- 서브트리
큰 트리에 속하는 작은 트리 

- 이진 트리(binary tree)

- 루트 노드를 중심으로 두 개의 서브트리로 나뉘어진다
- 나뉘어진 두 서브 트리도 모두 이진트리이다 
Ex)

공집합(empty node)노드는 이진트리의 판단에 있어서 노드로 인정한다
포화 이진 트리(Full Binary Tree)와 완전 이진 트리(Complete Binary Tree)
- 레벨 : 층별로 숫자를 매긴 것
 - 높이 : 트리의 최고 레벨
 

- 포화 이진 트리 : 모든 레벨이 꽉 차있어서 노드를 더 추가하려면 레벨을 높여야 한다
 

- 완전 이진 트리 : 포화 이진 트리처럼 모든 레벨이 꽉 차있는 것은 아니지만, 빈 틈 없이 노드가 채워진 이진 트리
위에서 아래로, 왼쪽에서 오른쪽의 순서대로 채워진다
 

- 그냥 이진 트리
 
08-2. 이진 트리의 구현
이진 트리의 구현 방법 : 배열 or 연결리스트
트리를 표현하기에는 연결 리스트가 더 유연하다

하지만 트리가 완성된 이후 매우 빈번한 탐색이 이루어지는 트리일 경우 배열로 구현한다. 탐색이 매우 용이하고 빠르기 때문이다

헤더파일에 정의된 구조체의 이해
 #ifndef __BINARY_TREE_H__
 #define __BINARY_TREE_H__
 
 typedef int BData;
 
 typedef struct _bTreeNode
 {
     BTdata data;
     struct _bTreeNode * left;
     struct _bTreeNode * right;
 } BTreeNode;
 
 BTreeNode * MakeBTreeNode(void);
 BTData GetData(BTreeNode *bt);
 void SetData(BTreeNode *bt, BTData data);
 
 BTreeNode * GetLeftSubTree(BTreeNode * bt);
 BTreeNode * GetRightSubTree(BTreeNode * bt);
 
 void MakeLeftSubTree(BTreeNode * main, BTreeNode * sub);
 void MakeRightSubTree(BTreeNode * main, BTreeNode * sub);
 
 #endif
헤더파일에 선언된 함수들의 기능
BTreeNode * MakeBTreeNode(void);: 노드의 생성BTData GetData(BTreeNode * bt);: 노드에 저장된 데이터를 반환void SetData(BTreeNode * bt, BTData data);: 노드에 데이터를 저장BTreeNode * GetLeftSubTree(BTreeNode * bt);: 왼쪽 서브 트리 주소 값 반환BTreeNode * GetRightSubTree(BTreeNode * bt);: 오른쪽 서브 트리 주소 값 반환
이진 트리 자료구조의 ADT
BTreeNode * MakeBTreeNode(void);
- 이진 트리 노드를 생성하여 그 주소 값을 반환한다
 BTData GetData(BTreeNode * bt);
- 노드에 저장된 데이터를 반환한다
 void SetData(BTreeNode * bt, BTData data);
- 노드에 데이터를 저장한다. Data로 전달된 값을 저장한다
 BTreeNode * GetLeftSubTree(BTreeNode * bt);
- 왼쪽 서브 트리의 주소 값을 반환한다
 BTreeNode * GetRightSubTree(BTreeNode * bt);
- 오른쪽 서브 트리의 주소 값을 반환한다
 void MakeLeftSubTree(BTreeNode * main, BTreeNode * sub);
- 왼쪽 서브 트리를 연결한다
 void MakeRightSubTree(BTreeNode * main, BTreeNode * sub);
- 오른쪽 서브 트리를 연결한다
 
이진 트리의 구현
#include <stdio.h>
#include <stdlib.h>
#include "BinaryTree.h"
BTreeNode * MakeBTreeNode(void)
{
    BTreeNode *nd = (BTreeNode*)malloc(sizeof(BTreeNode));
    nd->left = NULL;
    nd->right = NULL;
    return nd;
}
BTData GetData(BTreeNode * bt)
{
    return bt->data;
}
void SetData(BTreeNode * bt, BTData data)
{
    bt->data = data;
}
BTreeNode * GetLeftSubTree(BTreeNode * bt)
{
    return bt->left;
}
BTreeNode * GetRightSubTree(BTreeNode * bt)
{
    return bt->right;
}
void MakeLeftSubTree(BTreeNode * main, BTreeNode * sub)
{
    if(main->left != NULL)
        free(main->left);
        
    main->left = sub;
}
void MakeRightSubTree(BTreeNode * main, BTreeNode * sub)
{
    if(main->right != NULL)
        free(main->right);
    
    main->right = sub;
}
08-3. 이진 트리의 순회(Traversal)
순회의 세 가지 방법
- 전위 순회(Preorder Traversal) : 루트 노드를 먼저

 
- 중위 순회(Inorder Traversal) : 루트 노드를 중간에

 
- 후위 순회(Postorder Traversal) : 루트 노드를 마지막에

 
순회의 재귀적 표현




- 1단계 : 왼쪽 서브 트리의 순회
 - 2단계 : 루트 노드의 방문
 - 3단계 : 오른쪽 서브 트리의 순회
 
void InorderTraverse(BTreeNode * bt) // 이진 트리 전체를 중위 순회하는 함수
{
    InorderTraverse(bt->left); // 1단계 왼쪽 서브 트리의 순회
    printf(“%d \n”, bt->data); // 2단계 루트 노드의 방문
    InorderTraverse(bt->right); // 3단계 오른쪽 서브 트리의 순회
}
재귀의 탈출 조건이 정의되어 있지 않다
노드의 방문이 그저 데이터의 출력이다
재귀의 탈출 조건

- 1단계 : 왼쪽 서브 트리의 순회 (노드N)
 - 2단계 : 루트 노드의 방문 (노드L)
 - 3단계 : 오른쪽 서브 트리의 순회 (공집합노드)
 
void InorderTraverse(BTreeNode * bt) // 이진 트리 전체를 중위 순회하는 함수
{
    if(bt == NULL) // bt가 NULL이면 재귀 탈출!
        return;
    InorderTraverse(bt->left); // 1단계 왼쪽 서브 트리의 순회
    printf(“%d \n”, bt->data); // 2단계 루트 노드의 방문
    InorderTraverse(bt->right); // 3단계 오른쪽 서브 트리의 순회
}
전위 순회
void PreorderTraverse(BTreeNode * bt)
{
    if(bt == NULL)
        return;
    
    printf(“%d \n”, bt->data); // 전위 순회이므로 루트 노드 먼저 방문
    PreorderTraverse(bt->left);
    PreorderTraverse(bt->right);
}
후위 순회
void PostorderTraverse(BTreeNode * bt) 
{
    if(bt == NULL)
        return;
   
    PostorderTraverse(bt->left);
    PostorderTraverse(bt->right);
    printf(“%d \n”, bt->data); // 후위 순회이므로 루트 노드 나중에 방문!
}
08-4. 수식 트리(Expression Tree)의 구현

루트 노드에 저장된 연산자의 연산을 하되, 두 개의 자식 노드에 저장된 두 피연산자를 대상으로 연산을 한다



중위 표기법의 수식 -> 후위 표기법의 수식 -> 수식 트리
수식 트리의 구현에 필요한 도구와 헤더파일의 정의
#ifndef __EXPRESSION_TREE_H__
#define __EXPRESSION_TREE_H__
#include "BinaryTree2.h"
BTreeNode * MakeExpTree(char exp[]);    // 수식 트리 구성
int EvaluateExpTree(BTreeNode * bt);    // 수식 트리 계산
void ShowPrefixTypeExp(BTreeNode * bt); //전위 표기법 기반 출력
void ShowInfixTypeExp(BTreeNode * bt);  // 중위 표기법 기반 출력
void ShowPostfixTypeExp(BTreeNode * bt);    //후위 표기법 기반 출력
#endif
후위 표기법
- 왼쪽에서 오른쪽으로 연산자가 나열된다
 - 두 피연산자는 연산자 앞에 나열된다
 







수식트리를 구성하는 방법
- 피연산자를 만나면 무조건 스택으로 옮긴다
 - 연산자를 만나면 스택에서 두 개의 피연산자를 꺼내어 자식 노드로 연결한다
 - 자식 노드를 연결해서 만들어진 트리는 다시 스택으로 옮긴다
 
BTreeNode * MakeExpTree(char exp[])
{
    Stack stack;
    BTreeNode * pnode;
    
    int expLen = strlen(exp);
    int i;
    StackInit(&stack);
    
    for(i = 0; i < expLen; i++)
    {
        pnode = MakeBTreeNode();
        
        if(isdigit(exp[i])) // 피연산자라면 ...
        {
            SetData(pnode, exp[i]-'0'); //문자를 정수로 바꿔서 저장
        }
        else    // 연산자라면...
        {
            MakeRightSubTree(pnode, SPop(&stack));
            MakeLeftSubTree(pnode, SPop(&stack));
            SetData(pnode, exp[i]);
        }
        
        SPush(&stack, pnode);
    }
    
    return SPop(&stack);
}
수식 트리의 순회
- 전위 순회하여 데이터를 출력한 결과 : 전위 표기법의 수식
 - 중위 순회하여 데이터를 출력한 결과 : 중위 표기법의 수식
 - 후위 순회하여 데이터를 출력한 결과 : 후위 표기법의 수식
 
void ShowPrefixTypeExp(BTreeNode * bt) // 전위 표기법의 수식으로
{
    PreorderTraverse(bt, ShowNodeData);
}
void ShowInfixTypeExp(BTreeNode * bt) // 중위 표기법의 수식으로
{
    InorderTraverse(bt, ShowNodeData);
}
void ShowPostfixTypeExp(BTreeNode * bt) // 후위 표기법의 수식으로
{
    PostorderTraverse(bt, ShowNodeData);
}
수식 트리의 계산
int EvaluateExpTree(BTreeNode * bt)
{
    int op1, op2;
    if(GetLeftSubTree(bt) == NULL && GetRightSubTree(bt) == NULL) // 단말 노드라면
        return GetData(bt);
    op1 = GetData(GetLeftSubTree(bt)); // 첫 번째 피연산자
    op2 = GetData(GetRightSubTree(bt)); // 두 번째 피연산자
    switch(GetData(bt)) // 연산자를 확인하여 연산을 진행
    {
        case ‘+’ :
            return op1+op2;
        case ‘-‘ :
            return op1-op2;
        case ‘*’ :
            return op1*op2;
        case ‘/‘ :
            return op1/op2;
    }
    
    return 0;
}
                Author And Source
이 문제에 관하여(Ch08. 트리(Tree)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@www_castlehi/Ch08.-트리Tree저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)
                            

