SlideShare ist ein Scribd-Unternehmen logo
1 von 30
Downloaden Sie, um offline zu lesen
project #4
   상호참조 생성기



             자료구조 01
                조 : C3
             조장 : 김창헌
             조원 : 이상현
                 김시백
                 박가혜
                 김정무
순 서




1. 프로젝트 설명
 1) 트리(trees)
2. 프로젝트 전체일정
3. 업무분담
4. 초안
5. 문제점
6. 최종안
7. 느낀점
1. 프로젝트 설명
1) 트리( Tree )란?
트리는 비선형 자료구조 ( 배열이나 리스트는 선형 구조이다. 기차 처럼 내 다음에 누가 있고 내 다음에

누가 있는.. )

Node( vertex )와 Link( edge )로 구성됨 ( Node를 vertex라고도 하고 Link를 edge라고도 표현한다. )
. Node는 정보( data )를 포함하고 있음

. Link는 두 Node간의 연결 관계를 나타냄

다음의 특징을 만족해야 함
. Root가 하나 존재 : Root는 최상위 Node

. Root를 제외한 모든 Node는 하나의 부모를 가져야 함

. Node는 여러개의 자식을 가질 수 있음

. 한 Node로 부터 다른 Node로 가는 경로는 유일해야 함

•   전형적인 예 ) Windows의 디렉토리 구조 ( 이걸 볼때마다 이건 트리구조다. 하고 생각하자. )


트리의 예




I는 Leaf Node가 아니다. 그림이 잘못되었답니다.
트리의 용어들
 용 어                                  내 용                       설명

 Node               vertex      정보를 나타냄            그림에서 A, B, C, D, ... 와 같은 객체를 노
                                                   드라고 한다.

 Link               edge        Node의 연결관계를 나타     그림에서 선으로 나타낸것을 링크라고 한다.
                                냄

 Root Node                      부모가 없는 최상위 노드      하나만 존재해야 한다.

                    Terminal
                    Node                           그림에서 하늘색으로 색칠된 노드들이 Leaf(
 Leaf Node                      자식이 없는 최하위 노드
                    External                       잎 ) Node이다.
                    Node
                    Non-Term                       자식이 하나라도 있는 노드 그림에서 A, B,
 Internal Node      inal Node   Leaf Node가 아닌 노드   C, I 가 인터널 노드가 된다.
                                                   B를 시작으로 트리를 떼어내면 Sub tree가
 Sub tree                       트리의 부분집합           된다.

                                                   그림에서 E 에서 C로 가는 경로는
                                                   E->B->A->C이다. 이 경로는 유일해야 한다.
                                한 Node로 부터    다른   ( 다른 경로는 없다. ) 중복이 없어야 한다.
 Path                
                                Node로 가는 경로        만약, G-C가 연결되어 있다면 이것은 트리라
                                                   고 할 수 없다. ( 이런 자료구조는 그래프라
                                                   고 부른다. )

                    L e a s t   두 노드의 공통적인 선조      그림에서 H와 J를 보면 두 노드의 공통 선
 최소 공통 선
 조                  Common      중 가장 레벨이 높은 선      조는 A, C가 있는데 그중 C는 Level이 2 이
                    Ancestors   조 노드               므로 C가 H, J의 최소 공통 선조가 된다.
                                자신의 아래로 연결된 노
 자식                 Children    드                  C의 자식 노드는 H와 I가 있다.
 부모                 Parent      자신의 위로 연결된 노드      I의 부모는 C이고 C의 노드는 A이다.
                    G r a n d                      조부모란 할아버지급 노드를 말한다. 즉 I의
 조부모                Parent      자신의 부모의 부모노드       조부모는 A이고 J의 조부모는 C이다.
                                Root에서 특정 노드로      그림에서 I를 보면 경로에 A, C, I 3개의 노
 레벨                 Level       가는 경로의 노드 수        드를 갖는다. 그래서 Level은 3이다.
 높이                 Height      가장 높은 레벨           J가 4로써 제일 높은 레벨이 된다.


7.2 이진 트리 ( Binary Tree )
        •   이진 트리의 정의
        •   Full Binary Tree


이진 트리 ( Binary Tree )의 정의
이진 트리의 정의
모든 Internal Node가 두개 이하의 자식을 갖는 트리
Left Child와 Right Child 두개의 자식을 가질 수 있음
가장 쓰임새가 많은 트리
이진 트리의 용도
Parse Tree : 수식 계산에 사용
Heap : 정렬( Sorting )에 사용
Binary Search Tree : 검색에 사용


Full Binary Tree
Full Binary Tree
마지막 레벨을 제외한 모든 레벨에 노드가 꽉차있음
Complete Binary Tree
모든 레벨에 노드가 꽉 차있음 




<full binary tree와 complete binary tree>


왼쪽의 그림을 보자. 자식이 많아 봐야 2개이다. 이런 트리를 이진트리라고 하는 것이다. 그런데 이진
트리 중에서도 Full binary tree와 Complete binary tree가 있는데, 왼쪽은 마지막 레벨 즉, H, I, J 4레벨
에서는 노드가 꽉 차있지 않지만 마지막 레벨을 제외한 모든 레벨에 노드가 꽉 차 있으므로 Full binary
tree라고 하고, 오른쪽 그림 처럼 모든 레벨에 노드가 꽉 차있으면 Complete( 완벽한 ) binary tree라고
부른다.


Full Binary Tree의 성질
레벨과 노드의 수 관계
레벨이 d일때, 트리의 노드수 N은 다음을 만족
    d-1          d
2         ≤ N ≤ 2 - 1
N개의 노드를 가진 이진 트리의 레벨 d는 다음과 같다.
d = [ log2N ] + 1 ( [ ]는 소수점 이하는 버림한다. )
숫자놀이를 해보자. ( Level = d, Node count = N )
2d-1 ≤ N ≤ 2d - 1 ( 노드의 갯수는 이 범위안에 속한다. )
                2-1               2
d = 2일때, 2            ≤ 노드의 갯수 ≤ 2 - 1
2 ≤ N ≤ 3 이 된다.
d = 3 일때, 23-1 ≤ 노드의 갯수 ≤ 23 - 1
4 ≤ N ≤ 7 이 된다.
d = 4 일때 8 ≤ N ≤ 15
그림을 보고 이해해 보도록 하자.
( 최소 A, B, C, D 4개, 최대 A, B, C, D, E, F, G 이렇게 7개가 된다. 아마 최대갯수가 홀 수 인것은
                                           n                 n
Root 노드가 홀 수 이기 때문인거 같다. 2 으로 표현되는 것은 자식의 노드가 2 만큼 늘어나기 때문인
것 같은데, 최소 노드의 갯수가 2n-1인 것은 예를들어, Level이 4이면 Level 1인 Root노드를 기준으로 2
                                               n
개씩 3번 나누어졌기 때문에, 8개가 되고 최대 노드의 갯수가 2 - 1 인 것은 2 번씩 나뉜 만큼의 수에
서 Root노드를 뺀 갯수가 되는 것이다. )
Level d를 구하는 이 증명은 자료구조 시험문제에 가끔 나오니 알아두도록 하자.
2d-1 ≤ N ≤ 2d - 1
부등식에 log2를 취하면,
                      d
d - 1 ≤ log2N ≤ log2( 2 - 1 )
  log2( 2d - 1 )는 log22d - log21즉, log2와2가 소거되어 d가 되고 - log21은 - 0이 된다. ( 2n = 1일때
  n은 0 )
결국 수식을 정리해 보면
d - 1 ≤ log2N <d 이렇게 되는데, 부등식에 1을 더하면
d ≤ log2N + 1 <d + 1 여기에서 log2N + 1의 소수점 이하 부분을 잘라낸 것과 같다는 의미가 된다. (
log2 = 0.3010 )
그래서 d = [ log2N ] + 1 이다.


7.3 이진 트리의 구현 ( Project : Tree )
      •   이진 트리 구현 방법
      •   이진 트리 모델링
      •   Skeleton


이진 트리의 구현 방법
배열로 구현하는 방법
. Full Binary Tree인 경우에만 사용가능
. 이진 트리의 특징을 이용한 인덱스 조작
. Heap Sort 에서 자세히 살펴볼 것임
연결 리스트로 구현하는 방법
. Node 클래스가 하나의 노드를 나타냄
. 이 노드는 pLeft와pRight로 두 자식을 가리킴
모든 자료구조의 기본이 되는 배열과 연결 리스트가 또 나왔다.
이진트리를 이중 연결리스트( Doubly Linked List )로 구현하는 법에 대해서 알아보자.




이진 트리 모델링
Head( 시작 )과 Tail( 끝) 을 나타내는 가짜 노드 사용
. m_pNodeHead / m_pNodeTail
. Root Node == m_pNodeHead->pLeft
. 모든 Leaf Node의 pLeft와 pRight == m_pNodeTail
m_pNodeHead와 m_pNodeTail를 보면.. ( 이 객체가 무엇을 의미하는지는 변수명을 보자. m은 클래스의
멤버 변수라는 뜻이고, Node의 Head와 Node의 Tail이라는 뜻이다. )
일단 비어있는( Empty )한 트리로 시작하게 되는데 헤드 노드의 pLeft와 pRight는 꼬리 노드의 시작주
소를 가리키고 있고, 꼬리 노드의 pLeft와 pRight는 자기 자신의 시작주소를 가리키고 있다.
( 여기서 클래스와 객체의 구분은 class는 멤버 변수와 함수들을 정의 해 놓은 것이고 객체는 그 클래스
를 이용해 만들어 낸 것을 말한다.
class People { m_pName, m_pAge, void SetName() }; 이게 클래스이고,
People p1( "김철수", 26 ); 여기서 p1이 객체가 되는것이다. )


오른쪽 그림을 보면 A, B, C, D로 나타낸 개념적 트리가 있는데 이것을 물리적 트리로 오른쪽 그림과
같이 표현할 수 있다.
m_pNodeHead( 가짜 노드 )는 리스트의 시작을 의미하고 pLeft에 Root노드 A를 링크하게 된다. pRight
에는 m_pNodeTail( 가짜 노드 )를 가리키게 된다. m_pNodeHead의 pRight는 잘 사용하지 않을 것이다.
이제 Root 노드 A를 보자. A노드의 pLeft에는 B노드가 pRight에는 C노드가 있고 다시 B노드의 pLeft에
는 D노드가 있으며 pRight가 없는 노드들은 모두 m_pNodeTail( 끝 )을 가리키고 있다.


Binary Tree Skeleton
구성
      •   노드를 나타내는 struct Node
      •   시작과 끝을 나타내는 m_pNodeHead, m_pNodeTail
      •   Ctors / Dtors
      •   제거를 위한 RemoveAll


Binary Tree도 어떤 객체들의 컨테이너 역할을 하는 것이기 때문에 template를 사용 하였다고 한다.


Binary Tree 클래스의 뼈대를 간단히 살펴보면 위의 구성 내용과 같다.
template<class TYPE >
class BinaryTree
{
public:
    BinaryTree();
    ~BinaryTree();
    void RemoveAll();


protected:
    struct Node
    {
        TYPE data;
        Node* pLeft;
        Node* pRight;
    };


    Node* m_pNodeHead;
    Node* m_pNodeTail;
    void RemoveSubtree( Node* pNode );
};


일단 struct Node{ TYPE data, Node* pLeft, Node* pRight };가 있고,
이를 class BinaryTree로 표현 하려면 [Head] - [Node1] - [Node2] - [ ... ] - [Tail]이 필요하다. Node구
조체를 class안에 포함 시키고 시작 주소를 나타내는 Node* m_pNodeHead와 Node* m_pNodeTail을
추가시켜 주자.
그리고 BinaryTree 클래스의 기능에 필요한 멤버 함수들을 추가시켜 주자. 여기에서는 모든 노드를 소거
시키는 기능과 SubTree를 소거 시키는 기능이 추가되어 있다.


Insert()기능과 Remove()기능이 없는 이유는 Tree에는 다양한 Tree가 있는데 이는 나중에 이 기본 클래
스에서 계승 받아 구현하려고 하기 때문이다. 일단 그렇게 알아두자. void RemoveAll()이라는 전체적으
로 필요한 기능만 추가되어 있는 것이다.


Ctors / Dtors ( 생성자와 제거자의 약자 )
Constructor
시작과 끝을 나타내는 가짜노드 두개를 만듦
Desturctor
RemoveAll을 호출한 뒤, 가짜 노드를 지움
Tree의 모든 Node를 지우기 위해서 모든 노드를 방문하는 방법이 필요함


생성자에서는 그러니까.. 나중에 void Main()함수에서 이 클래스를 사용해 객체를 만들때, BinaryTree
b1(); 객체 b1이 생성될때 어떠한 구조로 생성이 되는가?


BinaryTree인 b1은 메모리상에 Head 가짜노드와 Tail 가짜노드가 만들어 지고 그 Head와Tail의 pLeft,
pRight는 저~기 위쪽 그림과 같이 연결되어 있다.
그럼 이 b1의 객체에 b1.Insert( "홍길동" ); 하게 되면 그 노드가 삽입되어 연결되는 것이다.
생성자에서 어떤 구조를 형성하는지 잘 생각해보고 넘어가자.
template<class TYPE >
BinaryTree< TYPE >::BinaryTree()
{
    m_pNodeHead =new Node;
    m_pNodeTail =new Node;


    m_pNodeHead->pLeft = m_pNodeTail;
    m_pNodeHead->pRight = m_pNodeTail;


    m_pNodeTail->pLeft = m_pNodeTail;
    m_pNodeTail->pRight = m_pNodeTail;
}


제거자에서는 이 BinaryTree b1객체가 사라질때 b1객체에서 사용한 모든 메모리를 해제시켜주는 역할을
하게 된다.
[Head] - [Node1] - [Node2] - [ ... ] - [Tail] 이렇게 형성된 메모리를 전부 날려버리는 것이다.
template<class TYPE >
BinaryTree< TYPE >::~BinaryTree()
{
    RemoveAll();    // 중간 노드들 제거
    if( m_pNodeHead )delete m_pNodeHead;    // 가짜 노드 Head와
    if( m_pNodeTail )delete m_pNodeTail;    // Tail 제거
}


7.4 Tree Traversal ( 트리 순회 )
      •   Pre-order Traversal
      •   In-order Traversal
      •   Post-order Traversal
      •   Level-order Traversal


Tree Traversal이 필요한 이유?
      •   비선형 구조여서 전체 노드를 방문하는 방법이 필요함.
여러가지 방법이 존재함 ( 선형구조라면 ->으로 간다던가 <-으로 간다던가 하는데.. )
. Stack 기반    : Pre-order, Post-order, In-order ( 이런 방법들이 있다. )
. Queue 기반    : Level-order
전산학 학부생 같은경우 이 내용이 시험에 자주 나올 만큼 중요한 부분이다. 정보처리 기사 시험에서도
자주 나오는 내용이다.


Pre-Order Traversal
      •   알고리즘
      1. Root를 방문한다
      1. 왼쪽 Subtree를 방문한다.
      1. 오른쪽 Subtree를 방문한다.
1을 방문하고 2를 방문하고 3을 방문한다. ( 2안에서 또 subTree가 있다면 왼쪽부터 방문하게 된다. ==
재귀호출의 맛보기.. )




void BinaryTree::PreOrderTraverse( Node* pNode )    // pNode부터 시작해서 방문
{
    if( pNode != m_pNodeTail )    // 재귀 호출 함수는 종료 조건이 꼭 필요하다.
    {
        Visit( pNode );    // Do Something
        PreOrderTraverse( pNode->pLeft );        // 방문했으면 Do Something에 결린다. 그리고 pNode가
Tail이 아니면 또 왼쪽으로 링크된 노드에 방문하게 되어있다.
        PreOrderTraverse( pNode->pRight );    // 위 함수가 종료되면 여기부터 시작
    }
}
이런식으로 방문하게 되는 Tree가 Pre-order Traversal( 앞 순서 방문 )이 된다.
Root를 방문하는 타이밍에 대해 자세히 보자.


In-Order Traversal
        •   알고리즘
        1. 왼쪽 Subtree를 방문한다.
        1. Root를 방문한다.
        1. 오른쪽 Subtree를 방문한다.
1을 방문하고 2.Root를 방문하고 3을 방문한다.
void BinaryTree::InOrderTraverse( Node* pNode )
{
    if( pNode != m_pNodeTail )
    {
        InOrderTraverse( pNode->pLeft );
        Visit( pNode );    // Do Something
        InOrderTraverse( pNode->pRight );
    }
}




InOrderTraverse( pNode->pLeft );를 계속 만나게 되므로일단 G까지 내려 간다. 그다음 Tail에 다다랐을
때 함수가 종료되고 다음 라인 Visit( pNode ); 함수를 만나게 되어 방문하게 된다. Do Something(
printf라던가.. )를      하고 나서 pRight함수가          발동되는데 G노드에는 pRight도 Tail if( pNode !=
m_pNodeTail ) 이니까 재귀호출 함수를 빠져나가게 된다. 그럼 D노드의 Visit( pNode ); 함수부터 시작
하게 되고, D노드의 pRight를 방문하게 된다.


Post-Order Traversal
        •   알고리즘
        1. 왼쪽 Subtree를 방문한다.
        1. 오른쪽 Subtree를 방문한다.
        1. Root를 방문한다.
void BinaryTree::InOrderTraverse( Node* pNode )
{
    if( pNode != m_pNodeTail )
    {
        InOrderTraverse( pNode->pLeft );
        InOrderTraverse( pNode->pRight );
        Visit( pNode );
    }
}




InOrderTraverse( A );를 호출하였다면
InOrderTraverse( A->pLeft );        // B가 링크되어 있다. 재귀호출 함수에 의해
InOrderTraverse( B->pLeft );
InOrderTraverse( D->pLeft );
InOrderTraverse( G->pLeft );        // pLeft, pRight 모두 Tail.. Visit하여 G를 출력
InOrderTraverse( D->pRight );
InOrderTraverse( H->pLeft );    // pLeft, pRight 모두 Tail.. Visit하여 H를 출력
D의 pLeft, pRight를 방문함수( 재귀호출 )를 이행하였으므로 그 다음라인 Visit( D );    // D를 출력
InOrderTraverse( B->pRight );
// ...


그림을 놓고 이해를 하는게 빠르다.
Pre-Order Traversal : Stack 버전
재귀 호출은
일반적으로 stack을 사용하여 non-recursive 방식으로 바꿀 수 있다.
Post-Order, In-Order는 다소 복잡하다.
재귀호출을 사용하면 코드가 깔끔하지만 성능이 않좋아서 옛날에 사용..
이 함수는 재귀호출이 아니다.
void BinaryTree::PreOrderTraverse_Stack( Node* pNode )
{
    ListStack< Node*> stack;
    stack.Push( pNode );
    while(!stack.IsEmpty())
    {
        pNode = stack.Pop();
        if( pNode != m_pNodeTail )
        {
            Visit( pNode );
            stack.Push( pNode->pRight );
            stack.Push( pNode->pLeft );
        }
    }
}


여기서 Node* 를 사용하여 노드포인터( pNode )를 가져오는 것은 만약 가져오는 객체의 크기가
100byte라면 가져오기 난감할 것이다. 포인터로 주소를 가져오게 되면 그 객체에 접근할 수가 있다. 함
수 구현 첫번째 라인을 보면 Node* 포인터를 스택에 담는것을 알 수 있다. 큰 객체를 다 담아버리게 되
면 말도 안되는 메모리를 사용하게 되므로 그 객체를 포인팅 하고 있는 포인터를 담아두면 언제든지 그
객체에 접근할 수 있기 때문이다. stack에 넣는다는것은 프링글즈를 생각하면 되는데.. 소스 코드를 보면
일단 함수 호출을 통해 가지고 온 노드를 stack에 한번 Push하고 다시 빼내어 방문하게 되는것을 볼 수
있다.
pRight를 먼저 Push하는 이유는 stack이기 때문이다. 먼저 Push하면 바닥쪽에 깔리고 나중에 Push하면
윗쪽으로 올라가기 때문에 Pop할때는 pLeft부터 나오기 때문이다.


재귀호출 방식을 이용하는게 좋으나, 알고리즘 구현 방식에도 다양한 방법이 있다는 것을 상기하자.


Level-Order Traversal
가장 직관적인 Traversal
위에서 아래로, 좌에서 우로 진행 ( A ~ I )
Queue로 진행을 하는데, 앞 소스와 거의 똑같은데 Stack과 Queue의 차이점에 의해 방문순서가 이렇게
된다.
void BinaryTree::LevelOrderTraverse( Node* pNode )
{
    ListQueue< Node*> queue;
    queue.Put( pNode );
    while(!queue.IsEmpty())
    {
        pNode = queue.Get();
        if( pNode != m_pNodeTail )
        {
            Visit( pNode );
            queue.Put( pNode->pLeft );
            queue.Put( pNode->pRight );
        }
    }
}
A노드를 가져오고 queue에 Put하고 다시 꺼내어 그 노드를 방문하고 pLeft를 queue에 Put하고 pRight
를 Put하고 꺼내어 Visit하게 된다. Queue는 은행 번호표를 생각하면 되는데 queue에 Put된 순서로 빠
져나오기 때문에 A~I의 순서가 나오게 된다.


7.5 수식 트리 ( Parse Tree )
        •   수식 트리 개념
        •   수식 트리 구축법


수식트리의 개념
정의
. 수식을 연산순서에 따라 트리로 구성
. Root에 Oeprator, Child에 Operand를 배치
. 모든 Operator는 Non-Terminal, Operand는 Terminal Node




( ( A + B ) * ( C - D ) ) / E + ( F * G )
A B를 더하고 C와 D를 뺼셈하고 그 값을 곱하고...


수식 트리를
      1. In-Order Traverse하면 Infix Notation
      1. Pre-Order Traverse 하면 Prefix Notation
      1. Post-Order Traverse 하면 Postfix Notation


후위 표기( Post-Order )에서 수식 트리 구성
후위 표기는 컴퓨터가 이해하기 가장 편리하게 되어있다.
알고리즘
1. Operand는 Node를 만들어 Stack에 Push
2. Operator를 만나면 Node를 생성하여
1. Stack에서 Pop한 노드를 오른쪽 자식으로 하고
2. Stack에서 또 Pop한 노드를 왼쪽 자식으로 하고
3. Operator Node와 링크된 두 자식 노드 자체를 Stack에 Push
3. Stack에 마지막으로 남은 노드가Root 이다.
위에 그림과 같이 되게 된다.


7.6 수식 트리의 구현
      •   Skeleton
      •   BuildParseTree


ParseTree skeleton
구성
. template의 instance로 부터 계승을 받음
. BuildParseTree : ParseTree 구성
. Traversal Functions
상위 클래스에서 이미 구현된 부분은 구현할 필요가 없다. 이게 계승의 장점이다.
class ParseTree :public BinaryTree< String >    // 이진 트리 TYPE : String
{
public:
    void BuildParseTree(const String& strPostfix );
    bool IsOperator(char c )
    {
        return( c =='+'|| c =='-'|| c =='*'|| c =='/');
    }


    void PreOrderTraverse( Node* pNode =0);
    void PostOrderTraverse( Node* pNode =0);
    void InOrderTraverse( Node* pNode =0);
    void LevelOrderTraverse( Node* pNode =0);
    void Visit( Node* pNode );
};


// Main함수에서 만드는거나.. 함수 안에서 만드는 거나.. 그게 그거
void ParseTree::BuildParseTree(const String& strPostfix )
{
    Node* pNode;    // Node의 포인터를 준비
    int i =0;
    ListStack< Node*> NodeStack;    // 스택 리스트를 준비
    RemoveAll();


    while( strPostfix[ i ])    // String을 읽자.
    {
        while( strPostfix[ i ]==' ')
            i++;    // 공백 문자는 무시
        pNode =new Node;
        if( IsOperator( strPostfix[ i ]))    // 연산자이면
        {
            pNode->data = strPostfix[ i ];
            i++;
            pNode->pRight = NodeStack.Pop();
            pNode->pLeft = NodeStack.Pop();
        }
        else    // 피연산자( 숫자 )이면
        {
            do
            {
                pNode->data += strPostfix[ i ];
                i++;
            }
            while( strPostfix[ i ]!=' '&& i < strPostfix.GetLength());
            pNode->pLeft = m_pNodeTail;
            pNode->pRight = m_pNodeTail;
        }
        NodeStack.Push( pNode );
    }
    m_pNodeHead->pLeft = NodeStack.Pop();    // Root
}


7. 결론
트리는 노드와 링크로 구성된 비선형 자료구조
이진 트리는 두개 이하의 자식을 가지는 트리
이진 트리의 Traversal은 트리의 모든 노드를 방문하기 위한 방법이다.
InOrder, PreOrder, PostOrder, LevelOrder가 있다.
스택을 이용하여 후위 표기로 부터 수식 트리( Parse Tree )를 만들었다.
Traversal 방법에 따라 다른 수식 표기가 나옴
[출처http://blog.naver.com/hkn10004?Redirect=Log&logNo=20109208779
2. 프로젝트 전체일정
   기 간            5 / 8                 5 / 10
         -   조원별 업무 분담 및 계획 작성
         -   프로젝트 문제파악         - 알고리즘에 대해 토의하고 이해
   내 용   -   프로젝트에 필요한 학습내용 토의
                                - 문제점 토의
         -   프로젝트 진행방향 토의


  기 간             5 / 15                5 / 17

                               - 프로젝트 검토

  내 용     - 소스 구현 및 보고서 작성     - 최종보고서 작성
                               - 발표준비



3. 업무분담

  이 름                      업   무

  김창헌    보고서 작성 및 회의 진행, 프로젝트에 필요한 학습내용 숙지




  김시백    알고리즘 설계와 소스에 대한 문제점 검토와 보완




  박가혜    소스구현 및 코딩 문제의 해결을 위한 방안 제시




  김정무    알고리즘 설계 및 프로젝트 수행 시 필요한 자료 수집




  이상현    알고리즘 설계 및 프로젝트 수행 시 필요한 자료 수집
4. 초안
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


#define MAX 1000


char SIGN[] = " .,!?:"trn";


typedef struct List
{
    char *word;
    int count;
    struct List *next;
} List;


List head={0,};
List* tail = &head;


int word_count = 0; // # of words
List **W = 0;


void array(List *head, List **W)
{
    List *temp = head->next;
    int i = 0;
    while (temp)
    {
          W[i++] = temp;
          temp = temp->next;
    }
}


void printW(List **W, int n)
{
    int i;
    for(i=0; i < n; i++) {
          printf("%20s : %dn",W[i]->word, W[i]->count);
    }
}
List* F_Token(const char* token)
{
    List *temp = head.next;
    while (temp)
    {
           if (strcmp(temp->word,token)==0)
               return temp;
           temp = temp->next;
    }
    return 0;
}



void InsertList(char *token)
{
    List *temp = F_Token(token);
    if (temp) // Found,
    {
           temp->count++;
    }
    else
    {
           int len = strlen(token)+1;
           temp = (List*)malloc(sizeof(List));
           temp->word = (char*)malloc(sizeof(char)*len);
           strncpy(temp->word, token, len);
           temp->count = 1;
           temp->next = 0;
           tail->next = temp;
           tail=temp;


           word_count++;
    }
}


void free_token()
{
    List *temp;
    while (temp = head.next)
    {
           if (temp->word)
               free(temp->word);


           head.next = temp->next;
free(temp);
    }
    tail = &head;
}


typedef List *T;


void swap(T *a, T *b)
{
    T t = *a;
    *a = *b;
    *b = t;
}


int comp(const void *pa, const void *pb)
{
    const T a = *(const T *)pa;
    const T b = *(const T *)pb;


    return strcmp(a->word,b->word);
}


int partition(T *A, int left, int right, int pivot)
{
    int i, index;
    T value = A[pivot];


    swap(&A[pivot],&A[right]);
    index = left;
    for (i = left; i < right; i++) {
          if (comp(&A[i],&value) < 0) {
                 swap(&A[i],&A[index]);
                 ++index;
          }
    }
    swap(&A[index],&A[right]);
    return index;
}


void quick_p(T *A, int left, int right)
{
    int pivot;
    int index;
if (right > left) {
         pivot = (right+left)/2;
         index = partition(A, left, right, pivot);
         quick_p(A, left, index-1);
         quick_p(A, index+1, right);
    }
}


void quicksort(T *A, int n)
{
    quick_p(A,0,n-1);
}


int main(void)
{
    char *str;
    char *token;
    long length;
    int i=0;


    FILE* fp = fopen("C:test.txt", "rt");


    if (fp==NULL)
    {
        puts("File open fail!");
        return -1;
    }


    fseek(fp, 0, SEEK_END);
    length = ftell(fp);
    rewind(fp);


    str = (char*)malloc(sizeof(char)*length+1);
    memset(str, 0, sizeof(char)*length+1);


    puts("*****************상호참조 생성기*****************");
        puts("****************단어 : 빈도수******");


    fread(str, 1, length, fp);
    fclose(fp);
    token=strtok(str,SIGN);
    while (token!=NULL)
    {
        InsertList(token);
token=strtok(NULL,SIGN);
     }


     W = (List **)malloc(sizeof(List*)*word_count);
     array(&head, W);


     quicksort(W,word_count);
//       qsort(W,word_count,sizeof(T),comp);


     printW(W,word_count);


     free(W);
     free_token();
          system("pause");
     return 0;
}
5. 문제점




단어별 라인이 나오지 않음.
단어의 대소문자 구별해서 나옴.
단어의 총 개수가 출력되지 않음.
6. 최종안
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


#define MAX 1000


char SIGN[] = " .,!?:"trn";


typedef struct List
{
    char *word;
    int count;
    struct List *next;
} List;


List head={0,};
List* tail = &head;


int word_count = 0; // # of words
List **W = 0;



void list2array(List *head, List **W)
{
    List *temp = head->next;
    int i = 0;
    while (temp)
    {
          W[i++] = temp;
          temp = temp->next;
    }
}


void printW(List **W, int n)
{
    int count=0;
    int i;
    for(i=0; i < n; i++) {
          printf("%20s : %dn",W[i]->word, W[i]->count);
                                          count++;
    }
printf("tttotal=%d",count);



}



List* FindToken(const char* token)
{
     List *temp = head.next;
     while (temp)
     {
            if (strcmp(temp->word,token)==0)
                return temp;
            temp = temp->next;
     }
     return 0;
}



void InsertList(char *token)
{
     List *temp = FindToken(token);
     if (temp) // Found,
     {
            temp->count++;
     }
     else
     {
            int len = strlen(token)+1;
            temp = (List*)malloc(sizeof(List));
            temp->word = (char*)malloc(sizeof(char)*len);
            strncpy(temp->word, token, len);
            temp->count = 1;
            temp->next = 0;
            tail->next = temp;
            tail=temp;


            word_count++;
     }
}




void print_token()
{
    List *temp = head.next;
    FILE* fw = fopen("daulOuput.txt", "wt");


    while (temp)
    {
        fprintf(fw, "%s : %dn", temp->word, temp->count);
        temp = temp->next;
    }
    fclose(fw);
}



void free_token()
{
    List *temp;
    while (temp = head.next)
    {
        if (temp->word)
              free(temp->word);


        head.next = temp->next;
        free(temp);
    }
    tail = &head;
}



typedef List *T; // 정렬할 자료형 T


void swap(T *a, T *b)
{
    T t = *a;
    *a = *b;
    *b = t;
}


int comp(const void *pa, const void *pb)
{
    const T a = *(const T *)pa;
    const T b = *(const T *)pb;


    return strcmp(a->word,b->word);
}
int partition(T *A, int left, int right, int pivot)
{
    int i, index;
    T value = A[pivot];


    swap(&A[pivot],&A[right]);
    index = left;
    for (i = left; i < right; i++) {
          if (comp(&A[i],&value) < 0) {
                 swap(&A[i],&A[index]);
                 ++index;
          }
    }
    swap(&A[index],&A[right]);
    return index;
}


void quickpart(T *A, int left, int right)
{
    int pivot;
    int index;


    if (right > left) {
          pivot = (right+left)/2;
          index = partition(A, left, right, pivot);
          quickpart(A, left, index-1);
          quickpart(A, index+1, right);
    }
}


void quicksort(T *A, int n)
{
    quickpart(A,0,n-1);
}


int main(void)
{
    char *str;    // File total Length,
    char *token; // token,
    long length; // 파일전체길이
    int i=0;


    FILE* fp = fopen("cmake.txt", "rt"); //텍스트모드로 읽기
// Open not file,
     if (fp==NULL)
     {
          puts("File open fail!");
          return -1;
     }


     fseek(fp, 0, SEEK_END);
     length = ftell(fp);
     rewind(fp);


     str = (char*)malloc(sizeof(char)*length+1);
     memset(str, 0, sizeof(char)*length+1);
 puts("*****************상호참조 생성기*****************");
          puts("****************단어 : 빈도수******");



     fread(str, 1, length, fp); // 자료주소값, 바이트크기,개수,파일포인터
     fclose(fp); //파일 닫기


     // 단어 수집
     token=strtok(str,SIGN); //문자열자르기
     while (token!=NULL)
     {
          InsertList(token);
          // 두번째토큰, 호출부터는 첫 번째 인수자리에 NULL 기입
          token=strtok(NULL,SIGN);
     }


     // 단어의 포인터 배열
     W = (List **)malloc(sizeof(List*)*word_count);
     list2array(&head, W); // 리스트에서 배열로


     quicksort(W,word_count); // 정렬
//       qsort(W,word_count,sizeof(T),comp); // 정렬


     printW(W,word_count); // 정렬 출력


     free(W);
     free_token();
     return 0;
}
7. 느낀점




초안에서의 문제점은 아래와 같다.
단어별 라인이 나오지 않음.->개선X
:fscanf를 이용하여,라인출력을 하고자 하였으나, 실패함.



단어의 대소문자 구별해서 나옴.->개선X
strtok을 이용하여, 단어를 구분해주었는데, 여기서 대소문자 구별하는 방법을 찾아내지 못함.




단어의 총 개수가 출력되지 않음.->개선O

Weitere ähnliche Inhalte

Andere mochten auch

Andere mochten auch (8)

3er Symposium Ginecologia, Obstetricia y Reproduccion Santiago Dexeus
3er Symposium Ginecologia, Obstetricia y Reproduccion Santiago Dexeus3er Symposium Ginecologia, Obstetricia y Reproduccion Santiago Dexeus
3er Symposium Ginecologia, Obstetricia y Reproduccion Santiago Dexeus
 
Leccion4 f redes
Leccion4 f redesLeccion4 f redes
Leccion4 f redes
 
PHP MySQL database connections
PHP MySQL database connectionsPHP MySQL database connections
PHP MySQL database connections
 
Php
PhpPhp
Php
 
Php & My Sql
Php & My SqlPhp & My Sql
Php & My Sql
 
Espanol4
Espanol4Espanol4
Espanol4
 
Php & My Sql
Php & My SqlPhp & My Sql
Php & My Sql
 
Bases de PHP - Partie 1
Bases de PHP - Partie 1Bases de PHP - Partie 1
Bases de PHP - Partie 1
 

Ähnlich wie Project#4상호참조 생성기 Hwp

자료구조 Project4
자료구조 Project4자료구조 Project4
자료구조 Project4KoChungWook
 
[Swift] Data Structure - Binary Tree
[Swift] Data Structure - Binary Tree[Swift] Data Structure - Binary Tree
[Swift] Data Structure - Binary TreeBill Kim
 
CS Study - Data Structure 자료 구조. Tree 발표자료
CS Study - Data Structure 자료 구조. Tree 발표자료CS Study - Data Structure 자료 구조. Tree 발표자료
CS Study - Data Structure 자료 구조. Tree 발표자료Jiwoo Choi
 
이산치수학 Project5
이산치수학 Project5이산치수학 Project5
이산치수학 Project5KoChungWook
 
Datastructure tree
Datastructure treeDatastructure tree
Datastructure tree송미 이
 
Data Structure 2
Data Structure 2Data Structure 2
Data Structure 2yonsei
 
자료구조 05 최종 보고서
자료구조 05 최종 보고서자료구조 05 최종 보고서
자료구조 05 최종 보고서pkok15
 
[D2CAMPUS] Algorithm tips - ALGOS
[D2CAMPUS] Algorithm tips - ALGOS[D2CAMPUS] Algorithm tips - ALGOS
[D2CAMPUS] Algorithm tips - ALGOSNAVER D2
 

Ähnlich wie Project#4상호참조 생성기 Hwp (11)

자료구조 Project4
자료구조 Project4자료구조 Project4
자료구조 Project4
 
[Swift] Data Structure - Binary Tree
[Swift] Data Structure - Binary Tree[Swift] Data Structure - Binary Tree
[Swift] Data Structure - Binary Tree
 
CS Study - Data Structure 자료 구조. Tree 발표자료
CS Study - Data Structure 자료 구조. Tree 발표자료CS Study - Data Structure 자료 구조. Tree 발표자료
CS Study - Data Structure 자료 구조. Tree 발표자료
 
이산치수학 Project5
이산치수학 Project5이산치수학 Project5
이산치수학 Project5
 
자구4번
자구4번자구4번
자구4번
 
자료구조04
자료구조04자료구조04
자료구조04
 
2012 Dm 04
2012 Dm 042012 Dm 04
2012 Dm 04
 
Datastructure tree
Datastructure treeDatastructure tree
Datastructure tree
 
Data Structure 2
Data Structure 2Data Structure 2
Data Structure 2
 
자료구조 05 최종 보고서
자료구조 05 최종 보고서자료구조 05 최종 보고서
자료구조 05 최종 보고서
 
[D2CAMPUS] Algorithm tips - ALGOS
[D2CAMPUS] Algorithm tips - ALGOS[D2CAMPUS] Algorithm tips - ALGOS
[D2CAMPUS] Algorithm tips - ALGOS
 

Mehr von Kimjeongmoo

Project#6 오탈자 검사 D0 Hwp
Project#6 오탈자 검사 D0 HwpProject#6 오탈자 검사 D0 Hwp
Project#6 오탈자 검사 D0 HwpKimjeongmoo
 
Project#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 HwpProject#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 HwpKimjeongmoo
 
Project#3다항식의연산 Hwp
Project#3다항식의연산 HwpProject#3다항식의연산 Hwp
Project#3다항식의연산 HwpKimjeongmoo
 
Project#2말의여행 Hwp
Project#2말의여행 HwpProject#2말의여행 Hwp
Project#2말의여행 HwpKimjeongmoo
 
Project#1파스칼 삼각형
Project#1파스칼 삼각형Project#1파스칼 삼각형
Project#1파스칼 삼각형Kimjeongmoo
 
Project#7 Group Codes Hwp
Project#7 Group Codes HwpProject#7 Group Codes Hwp
Project#7 Group Codes HwpKimjeongmoo
 
Project#6 2 비트 덧셈기 Hwp
Project#6 2 비트 덧셈기 HwpProject#6 2 비트 덧셈기 Hwp
Project#6 2 비트 덧셈기 HwpKimjeongmoo
 
Project#5 통신망에서 길 찾기 Hwp
Project#5 통신망에서 길 찾기 HwpProject#5 통신망에서 길 찾기 Hwp
Project#5 통신망에서 길 찾기 HwpKimjeongmoo
 
Project#4 Syntax Of Languages Hwp
Project#4 Syntax Of Languages HwpProject#4 Syntax Of Languages Hwp
Project#4 Syntax Of Languages HwpKimjeongmoo
 
Project#3 How Fast Can We Sort Hwp
Project#3 How Fast Can We Sort HwpProject#3 How Fast Can We Sort Hwp
Project#3 How Fast Can We Sort HwpKimjeongmoo
 
Project#2 데이터베이스 시스템 Hwp
Project#2 데이터베이스 시스템 HwpProject#2 데이터베이스 시스템 Hwp
Project#2 데이터베이스 시스템 HwpKimjeongmoo
 
Project#1 지식 기반 시스템 Hwp
Project#1 지식 기반 시스템 HwpProject#1 지식 기반 시스템 Hwp
Project#1 지식 기반 시스템 HwpKimjeongmoo
 

Mehr von Kimjeongmoo (12)

Project#6 오탈자 검사 D0 Hwp
Project#6 오탈자 검사 D0 HwpProject#6 오탈자 검사 D0 Hwp
Project#6 오탈자 검사 D0 Hwp
 
Project#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 HwpProject#5 최단거리 찾기 D0 Hwp
Project#5 최단거리 찾기 D0 Hwp
 
Project#3다항식의연산 Hwp
Project#3다항식의연산 HwpProject#3다항식의연산 Hwp
Project#3다항식의연산 Hwp
 
Project#2말의여행 Hwp
Project#2말의여행 HwpProject#2말의여행 Hwp
Project#2말의여행 Hwp
 
Project#1파스칼 삼각형
Project#1파스칼 삼각형Project#1파스칼 삼각형
Project#1파스칼 삼각형
 
Project#7 Group Codes Hwp
Project#7 Group Codes HwpProject#7 Group Codes Hwp
Project#7 Group Codes Hwp
 
Project#6 2 비트 덧셈기 Hwp
Project#6 2 비트 덧셈기 HwpProject#6 2 비트 덧셈기 Hwp
Project#6 2 비트 덧셈기 Hwp
 
Project#5 통신망에서 길 찾기 Hwp
Project#5 통신망에서 길 찾기 HwpProject#5 통신망에서 길 찾기 Hwp
Project#5 통신망에서 길 찾기 Hwp
 
Project#4 Syntax Of Languages Hwp
Project#4 Syntax Of Languages HwpProject#4 Syntax Of Languages Hwp
Project#4 Syntax Of Languages Hwp
 
Project#3 How Fast Can We Sort Hwp
Project#3 How Fast Can We Sort HwpProject#3 How Fast Can We Sort Hwp
Project#3 How Fast Can We Sort Hwp
 
Project#2 데이터베이스 시스템 Hwp
Project#2 데이터베이스 시스템 HwpProject#2 데이터베이스 시스템 Hwp
Project#2 데이터베이스 시스템 Hwp
 
Project#1 지식 기반 시스템 Hwp
Project#1 지식 기반 시스템 HwpProject#1 지식 기반 시스템 Hwp
Project#1 지식 기반 시스템 Hwp
 

Project#4상호참조 생성기 Hwp

  • 1. project #4 상호참조 생성기 자료구조 01 조 : C3 조장 : 김창헌 조원 : 이상현 김시백 박가혜 김정무
  • 2. 순 서 1. 프로젝트 설명 1) 트리(trees) 2. 프로젝트 전체일정 3. 업무분담 4. 초안 5. 문제점 6. 최종안 7. 느낀점
  • 3. 1. 프로젝트 설명 1) 트리( Tree )란? 트리는 비선형 자료구조 ( 배열이나 리스트는 선형 구조이다. 기차 처럼 내 다음에 누가 있고 내 다음에 누가 있는.. ) Node( vertex )와 Link( edge )로 구성됨 ( Node를 vertex라고도 하고 Link를 edge라고도 표현한다. ) . Node는 정보( data )를 포함하고 있음 . Link는 두 Node간의 연결 관계를 나타냄 다음의 특징을 만족해야 함 . Root가 하나 존재 : Root는 최상위 Node . Root를 제외한 모든 Node는 하나의 부모를 가져야 함 . Node는 여러개의 자식을 가질 수 있음 . 한 Node로 부터 다른 Node로 가는 경로는 유일해야 함 • 전형적인 예 ) Windows의 디렉토리 구조 ( 이걸 볼때마다 이건 트리구조다. 하고 생각하자. ) 트리의 예 I는 Leaf Node가 아니다. 그림이 잘못되었답니다.
  • 4. 트리의 용어들 용 어 내 용 설명 Node vertex 정보를 나타냄 그림에서 A, B, C, D, ... 와 같은 객체를 노 드라고 한다. Link edge Node의 연결관계를 나타 그림에서 선으로 나타낸것을 링크라고 한다. 냄 Root Node   부모가 없는 최상위 노드 하나만 존재해야 한다. Terminal Node 그림에서 하늘색으로 색칠된 노드들이 Leaf( Leaf Node 자식이 없는 최하위 노드 External 잎 ) Node이다. Node Non-Term 자식이 하나라도 있는 노드 그림에서 A, B, Internal Node inal Node Leaf Node가 아닌 노드 C, I 가 인터널 노드가 된다. B를 시작으로 트리를 떼어내면 Sub tree가 Sub tree   트리의 부분집합 된다. 그림에서 E 에서 C로 가는 경로는 E->B->A->C이다. 이 경로는 유일해야 한다. 한 Node로 부터 다른 ( 다른 경로는 없다. ) 중복이 없어야 한다. Path   Node로 가는 경로 만약, G-C가 연결되어 있다면 이것은 트리라 고 할 수 없다. ( 이런 자료구조는 그래프라 고 부른다. ) L e a s t 두 노드의 공통적인 선조 그림에서 H와 J를 보면 두 노드의 공통 선 최소 공통 선 조 Common 중 가장 레벨이 높은 선 조는 A, C가 있는데 그중 C는 Level이 2 이 Ancestors 조 노드 므로 C가 H, J의 최소 공통 선조가 된다. 자신의 아래로 연결된 노 자식 Children 드 C의 자식 노드는 H와 I가 있다. 부모 Parent 자신의 위로 연결된 노드 I의 부모는 C이고 C의 노드는 A이다. G r a n d 조부모란 할아버지급 노드를 말한다. 즉 I의 조부모 Parent 자신의 부모의 부모노드 조부모는 A이고 J의 조부모는 C이다. Root에서 특정 노드로 그림에서 I를 보면 경로에 A, C, I 3개의 노 레벨 Level 가는 경로의 노드 수 드를 갖는다. 그래서 Level은 3이다. 높이 Height 가장 높은 레벨 J가 4로써 제일 높은 레벨이 된다. 7.2 이진 트리 ( Binary Tree ) • 이진 트리의 정의 • Full Binary Tree 이진 트리 ( Binary Tree )의 정의 이진 트리의 정의 모든 Internal Node가 두개 이하의 자식을 갖는 트리 Left Child와 Right Child 두개의 자식을 가질 수 있음 가장 쓰임새가 많은 트리 이진 트리의 용도 Parse Tree : 수식 계산에 사용 Heap : 정렬( Sorting )에 사용 Binary Search Tree : 검색에 사용 Full Binary Tree
  • 5. Full Binary Tree 마지막 레벨을 제외한 모든 레벨에 노드가 꽉차있음 Complete Binary Tree 모든 레벨에 노드가 꽉 차있음  <full binary tree와 complete binary tree> 왼쪽의 그림을 보자. 자식이 많아 봐야 2개이다. 이런 트리를 이진트리라고 하는 것이다. 그런데 이진 트리 중에서도 Full binary tree와 Complete binary tree가 있는데, 왼쪽은 마지막 레벨 즉, H, I, J 4레벨 에서는 노드가 꽉 차있지 않지만 마지막 레벨을 제외한 모든 레벨에 노드가 꽉 차 있으므로 Full binary tree라고 하고, 오른쪽 그림 처럼 모든 레벨에 노드가 꽉 차있으면 Complete( 완벽한 ) binary tree라고 부른다. Full Binary Tree의 성질 레벨과 노드의 수 관계 레벨이 d일때, 트리의 노드수 N은 다음을 만족 d-1 d 2 ≤ N ≤ 2 - 1 N개의 노드를 가진 이진 트리의 레벨 d는 다음과 같다. d = [ log2N ] + 1 ( [ ]는 소수점 이하는 버림한다. ) 숫자놀이를 해보자. ( Level = d, Node count = N ) 2d-1 ≤ N ≤ 2d - 1 ( 노드의 갯수는 이 범위안에 속한다. ) 2-1 2 d = 2일때, 2 ≤ 노드의 갯수 ≤ 2 - 1 2 ≤ N ≤ 3 이 된다. d = 3 일때, 23-1 ≤ 노드의 갯수 ≤ 23 - 1 4 ≤ N ≤ 7 이 된다. d = 4 일때 8 ≤ N ≤ 15 그림을 보고 이해해 보도록 하자. ( 최소 A, B, C, D 4개, 최대 A, B, C, D, E, F, G 이렇게 7개가 된다. 아마 최대갯수가 홀 수 인것은 n n Root 노드가 홀 수 이기 때문인거 같다. 2 으로 표현되는 것은 자식의 노드가 2 만큼 늘어나기 때문인 것 같은데, 최소 노드의 갯수가 2n-1인 것은 예를들어, Level이 4이면 Level 1인 Root노드를 기준으로 2 n 개씩 3번 나누어졌기 때문에, 8개가 되고 최대 노드의 갯수가 2 - 1 인 것은 2 번씩 나뉜 만큼의 수에 서 Root노드를 뺀 갯수가 되는 것이다. )
  • 6. Level d를 구하는 이 증명은 자료구조 시험문제에 가끔 나오니 알아두도록 하자. 2d-1 ≤ N ≤ 2d - 1 부등식에 log2를 취하면, d d - 1 ≤ log2N ≤ log2( 2 - 1 ) log2( 2d - 1 )는 log22d - log21즉, log2와2가 소거되어 d가 되고 - log21은 - 0이 된다. ( 2n = 1일때 n은 0 ) 결국 수식을 정리해 보면 d - 1 ≤ log2N <d 이렇게 되는데, 부등식에 1을 더하면 d ≤ log2N + 1 <d + 1 여기에서 log2N + 1의 소수점 이하 부분을 잘라낸 것과 같다는 의미가 된다. ( log2 = 0.3010 ) 그래서 d = [ log2N ] + 1 이다. 7.3 이진 트리의 구현 ( Project : Tree ) • 이진 트리 구현 방법 • 이진 트리 모델링 • Skeleton 이진 트리의 구현 방법 배열로 구현하는 방법 . Full Binary Tree인 경우에만 사용가능 . 이진 트리의 특징을 이용한 인덱스 조작 . Heap Sort 에서 자세히 살펴볼 것임 연결 리스트로 구현하는 방법 . Node 클래스가 하나의 노드를 나타냄 . 이 노드는 pLeft와pRight로 두 자식을 가리킴 모든 자료구조의 기본이 되는 배열과 연결 리스트가 또 나왔다. 이진트리를 이중 연결리스트( Doubly Linked List )로 구현하는 법에 대해서 알아보자. 이진 트리 모델링 Head( 시작 )과 Tail( 끝) 을 나타내는 가짜 노드 사용 . m_pNodeHead / m_pNodeTail . Root Node == m_pNodeHead->pLeft . 모든 Leaf Node의 pLeft와 pRight == m_pNodeTail
  • 7. m_pNodeHead와 m_pNodeTail를 보면.. ( 이 객체가 무엇을 의미하는지는 변수명을 보자. m은 클래스의 멤버 변수라는 뜻이고, Node의 Head와 Node의 Tail이라는 뜻이다. ) 일단 비어있는( Empty )한 트리로 시작하게 되는데 헤드 노드의 pLeft와 pRight는 꼬리 노드의 시작주 소를 가리키고 있고, 꼬리 노드의 pLeft와 pRight는 자기 자신의 시작주소를 가리키고 있다. ( 여기서 클래스와 객체의 구분은 class는 멤버 변수와 함수들을 정의 해 놓은 것이고 객체는 그 클래스 를 이용해 만들어 낸 것을 말한다. class People { m_pName, m_pAge, void SetName() }; 이게 클래스이고, People p1( "김철수", 26 ); 여기서 p1이 객체가 되는것이다. ) 오른쪽 그림을 보면 A, B, C, D로 나타낸 개념적 트리가 있는데 이것을 물리적 트리로 오른쪽 그림과 같이 표현할 수 있다. m_pNodeHead( 가짜 노드 )는 리스트의 시작을 의미하고 pLeft에 Root노드 A를 링크하게 된다. pRight 에는 m_pNodeTail( 가짜 노드 )를 가리키게 된다. m_pNodeHead의 pRight는 잘 사용하지 않을 것이다. 이제 Root 노드 A를 보자. A노드의 pLeft에는 B노드가 pRight에는 C노드가 있고 다시 B노드의 pLeft에 는 D노드가 있으며 pRight가 없는 노드들은 모두 m_pNodeTail( 끝 )을 가리키고 있다. Binary Tree Skeleton 구성 • 노드를 나타내는 struct Node • 시작과 끝을 나타내는 m_pNodeHead, m_pNodeTail • Ctors / Dtors • 제거를 위한 RemoveAll Binary Tree도 어떤 객체들의 컨테이너 역할을 하는 것이기 때문에 template를 사용 하였다고 한다. Binary Tree 클래스의 뼈대를 간단히 살펴보면 위의 구성 내용과 같다. template<class TYPE > class BinaryTree {
  • 8. public:     BinaryTree();     ~BinaryTree();     void RemoveAll(); protected:     struct Node     {         TYPE data;         Node* pLeft;         Node* pRight;     };     Node* m_pNodeHead;     Node* m_pNodeTail;     void RemoveSubtree( Node* pNode ); }; 일단 struct Node{ TYPE data, Node* pLeft, Node* pRight };가 있고, 이를 class BinaryTree로 표현 하려면 [Head] - [Node1] - [Node2] - [ ... ] - [Tail]이 필요하다. Node구 조체를 class안에 포함 시키고 시작 주소를 나타내는 Node* m_pNodeHead와 Node* m_pNodeTail을 추가시켜 주자. 그리고 BinaryTree 클래스의 기능에 필요한 멤버 함수들을 추가시켜 주자. 여기에서는 모든 노드를 소거 시키는 기능과 SubTree를 소거 시키는 기능이 추가되어 있다. Insert()기능과 Remove()기능이 없는 이유는 Tree에는 다양한 Tree가 있는데 이는 나중에 이 기본 클래 스에서 계승 받아 구현하려고 하기 때문이다. 일단 그렇게 알아두자. void RemoveAll()이라는 전체적으 로 필요한 기능만 추가되어 있는 것이다. Ctors / Dtors ( 생성자와 제거자의 약자 ) Constructor 시작과 끝을 나타내는 가짜노드 두개를 만듦 Desturctor RemoveAll을 호출한 뒤, 가짜 노드를 지움 Tree의 모든 Node를 지우기 위해서 모든 노드를 방문하는 방법이 필요함 생성자에서는 그러니까.. 나중에 void Main()함수에서 이 클래스를 사용해 객체를 만들때, BinaryTree b1(); 객체 b1이 생성될때 어떠한 구조로 생성이 되는가? BinaryTree인 b1은 메모리상에 Head 가짜노드와 Tail 가짜노드가 만들어 지고 그 Head와Tail의 pLeft, pRight는 저~기 위쪽 그림과 같이 연결되어 있다. 그럼 이 b1의 객체에 b1.Insert( "홍길동" ); 하게 되면 그 노드가 삽입되어 연결되는 것이다. 생성자에서 어떤 구조를 형성하는지 잘 생각해보고 넘어가자.
  • 9. template<class TYPE > BinaryTree< TYPE >::BinaryTree() {     m_pNodeHead =new Node;     m_pNodeTail =new Node;     m_pNodeHead->pLeft = m_pNodeTail;     m_pNodeHead->pRight = m_pNodeTail;     m_pNodeTail->pLeft = m_pNodeTail;     m_pNodeTail->pRight = m_pNodeTail; } 제거자에서는 이 BinaryTree b1객체가 사라질때 b1객체에서 사용한 모든 메모리를 해제시켜주는 역할을 하게 된다. [Head] - [Node1] - [Node2] - [ ... ] - [Tail] 이렇게 형성된 메모리를 전부 날려버리는 것이다. template<class TYPE > BinaryTree< TYPE >::~BinaryTree() {     RemoveAll();    // 중간 노드들 제거     if( m_pNodeHead )delete m_pNodeHead;    // 가짜 노드 Head와     if( m_pNodeTail )delete m_pNodeTail;    // Tail 제거 } 7.4 Tree Traversal ( 트리 순회 ) • Pre-order Traversal • In-order Traversal • Post-order Traversal • Level-order Traversal Tree Traversal이 필요한 이유? • 비선형 구조여서 전체 노드를 방문하는 방법이 필요함. 여러가지 방법이 존재함 ( 선형구조라면 ->으로 간다던가 <-으로 간다던가 하는데.. ) . Stack 기반    : Pre-order, Post-order, In-order ( 이런 방법들이 있다. ) . Queue 기반    : Level-order 전산학 학부생 같은경우 이 내용이 시험에 자주 나올 만큼 중요한 부분이다. 정보처리 기사 시험에서도 자주 나오는 내용이다. Pre-Order Traversal • 알고리즘 1. Root를 방문한다 1. 왼쪽 Subtree를 방문한다. 1. 오른쪽 Subtree를 방문한다.
  • 10. 1을 방문하고 2를 방문하고 3을 방문한다. ( 2안에서 또 subTree가 있다면 왼쪽부터 방문하게 된다. == 재귀호출의 맛보기.. ) void BinaryTree::PreOrderTraverse( Node* pNode )    // pNode부터 시작해서 방문 {     if( pNode != m_pNodeTail )    // 재귀 호출 함수는 종료 조건이 꼭 필요하다.     {         Visit( pNode );    // Do Something         PreOrderTraverse( pNode->pLeft );        // 방문했으면 Do Something에 결린다. 그리고 pNode가 Tail이 아니면 또 왼쪽으로 링크된 노드에 방문하게 되어있다.         PreOrderTraverse( pNode->pRight );    // 위 함수가 종료되면 여기부터 시작     } } 이런식으로 방문하게 되는 Tree가 Pre-order Traversal( 앞 순서 방문 )이 된다. Root를 방문하는 타이밍에 대해 자세히 보자. In-Order Traversal • 알고리즘 1. 왼쪽 Subtree를 방문한다. 1. Root를 방문한다. 1. 오른쪽 Subtree를 방문한다.
  • 11. 1을 방문하고 2.Root를 방문하고 3을 방문한다. void BinaryTree::InOrderTraverse( Node* pNode ) {     if( pNode != m_pNodeTail )     {         InOrderTraverse( pNode->pLeft );         Visit( pNode );    // Do Something         InOrderTraverse( pNode->pRight );     } } InOrderTraverse( pNode->pLeft );를 계속 만나게 되므로일단 G까지 내려 간다. 그다음 Tail에 다다랐을 때 함수가 종료되고 다음 라인 Visit( pNode ); 함수를 만나게 되어 방문하게 된다. Do Something( printf라던가.. )를 하고 나서 pRight함수가 발동되는데 G노드에는 pRight도 Tail if( pNode != m_pNodeTail ) 이니까 재귀호출 함수를 빠져나가게 된다. 그럼 D노드의 Visit( pNode ); 함수부터 시작 하게 되고, D노드의 pRight를 방문하게 된다. Post-Order Traversal • 알고리즘 1. 왼쪽 Subtree를 방문한다. 1. 오른쪽 Subtree를 방문한다. 1. Root를 방문한다.
  • 12. void BinaryTree::InOrderTraverse( Node* pNode ) {     if( pNode != m_pNodeTail )     {         InOrderTraverse( pNode->pLeft );         InOrderTraverse( pNode->pRight );         Visit( pNode );     } } InOrderTraverse( A );를 호출하였다면 InOrderTraverse( A->pLeft );        // B가 링크되어 있다. 재귀호출 함수에 의해 InOrderTraverse( B->pLeft ); InOrderTraverse( D->pLeft ); InOrderTraverse( G->pLeft );        // pLeft, pRight 모두 Tail.. Visit하여 G를 출력 InOrderTraverse( D->pRight ); InOrderTraverse( H->pLeft );    // pLeft, pRight 모두 Tail.. Visit하여 H를 출력 D의 pLeft, pRight를 방문함수( 재귀호출 )를 이행하였으므로 그 다음라인 Visit( D );    // D를 출력 InOrderTraverse( B->pRight ); // ... 그림을 놓고 이해를 하는게 빠르다.
  • 13. Pre-Order Traversal : Stack 버전 재귀 호출은 일반적으로 stack을 사용하여 non-recursive 방식으로 바꿀 수 있다. Post-Order, In-Order는 다소 복잡하다. 재귀호출을 사용하면 코드가 깔끔하지만 성능이 않좋아서 옛날에 사용.. 이 함수는 재귀호출이 아니다. void BinaryTree::PreOrderTraverse_Stack( Node* pNode ) {     ListStack< Node*> stack;     stack.Push( pNode );     while(!stack.IsEmpty())     {         pNode = stack.Pop();         if( pNode != m_pNodeTail )         {             Visit( pNode );             stack.Push( pNode->pRight );             stack.Push( pNode->pLeft );         }     } } 여기서 Node* 를 사용하여 노드포인터( pNode )를 가져오는 것은 만약 가져오는 객체의 크기가 100byte라면 가져오기 난감할 것이다. 포인터로 주소를 가져오게 되면 그 객체에 접근할 수가 있다. 함 수 구현 첫번째 라인을 보면 Node* 포인터를 스택에 담는것을 알 수 있다. 큰 객체를 다 담아버리게 되 면 말도 안되는 메모리를 사용하게 되므로 그 객체를 포인팅 하고 있는 포인터를 담아두면 언제든지 그 객체에 접근할 수 있기 때문이다. stack에 넣는다는것은 프링글즈를 생각하면 되는데.. 소스 코드를 보면 일단 함수 호출을 통해 가지고 온 노드를 stack에 한번 Push하고 다시 빼내어 방문하게 되는것을 볼 수 있다. pRight를 먼저 Push하는 이유는 stack이기 때문이다. 먼저 Push하면 바닥쪽에 깔리고 나중에 Push하면 윗쪽으로 올라가기 때문에 Pop할때는 pLeft부터 나오기 때문이다. 재귀호출 방식을 이용하는게 좋으나, 알고리즘 구현 방식에도 다양한 방법이 있다는 것을 상기하자. Level-Order Traversal 가장 직관적인 Traversal 위에서 아래로, 좌에서 우로 진행 ( A ~ I )
  • 14. Queue로 진행을 하는데, 앞 소스와 거의 똑같은데 Stack과 Queue의 차이점에 의해 방문순서가 이렇게 된다. void BinaryTree::LevelOrderTraverse( Node* pNode ) {     ListQueue< Node*> queue;     queue.Put( pNode );     while(!queue.IsEmpty())     {         pNode = queue.Get();         if( pNode != m_pNodeTail )         {             Visit( pNode );             queue.Put( pNode->pLeft );             queue.Put( pNode->pRight );         }     } } A노드를 가져오고 queue에 Put하고 다시 꺼내어 그 노드를 방문하고 pLeft를 queue에 Put하고 pRight 를 Put하고 꺼내어 Visit하게 된다. Queue는 은행 번호표를 생각하면 되는데 queue에 Put된 순서로 빠 져나오기 때문에 A~I의 순서가 나오게 된다. 7.5 수식 트리 ( Parse Tree ) • 수식 트리 개념 • 수식 트리 구축법 수식트리의 개념 정의 . 수식을 연산순서에 따라 트리로 구성 . Root에 Oeprator, Child에 Operand를 배치
  • 15. . 모든 Operator는 Non-Terminal, Operand는 Terminal Node ( ( A + B ) * ( C - D ) ) / E + ( F * G ) A B를 더하고 C와 D를 뺼셈하고 그 값을 곱하고... 수식 트리를 1. In-Order Traverse하면 Infix Notation 1. Pre-Order Traverse 하면 Prefix Notation 1. Post-Order Traverse 하면 Postfix Notation 후위 표기( Post-Order )에서 수식 트리 구성 후위 표기는 컴퓨터가 이해하기 가장 편리하게 되어있다. 알고리즘 1. Operand는 Node를 만들어 Stack에 Push 2. Operator를 만나면 Node를 생성하여 1. Stack에서 Pop한 노드를 오른쪽 자식으로 하고 2. Stack에서 또 Pop한 노드를 왼쪽 자식으로 하고 3. Operator Node와 링크된 두 자식 노드 자체를 Stack에 Push 3. Stack에 마지막으로 남은 노드가Root 이다. 위에 그림과 같이 되게 된다. 7.6 수식 트리의 구현 • Skeleton • BuildParseTree ParseTree skeleton 구성 . template의 instance로 부터 계승을 받음 . BuildParseTree : ParseTree 구성 . Traversal Functions
  • 16. 상위 클래스에서 이미 구현된 부분은 구현할 필요가 없다. 이게 계승의 장점이다. class ParseTree :public BinaryTree< String >    // 이진 트리 TYPE : String { public:     void BuildParseTree(const String& strPostfix );     bool IsOperator(char c )     {         return( c =='+'|| c =='-'|| c =='*'|| c =='/');     }     void PreOrderTraverse( Node* pNode =0);     void PostOrderTraverse( Node* pNode =0);     void InOrderTraverse( Node* pNode =0);     void LevelOrderTraverse( Node* pNode =0);     void Visit( Node* pNode ); }; // Main함수에서 만드는거나.. 함수 안에서 만드는 거나.. 그게 그거 void ParseTree::BuildParseTree(const String& strPostfix ) {     Node* pNode;    // Node의 포인터를 준비     int i =0;     ListStack< Node*> NodeStack;    // 스택 리스트를 준비     RemoveAll();     while( strPostfix[ i ])    // String을 읽자.     {         while( strPostfix[ i ]==' ')             i++;    // 공백 문자는 무시         pNode =new Node;         if( IsOperator( strPostfix[ i ]))    // 연산자이면         {             pNode->data = strPostfix[ i ];             i++;             pNode->pRight = NodeStack.Pop();             pNode->pLeft = NodeStack.Pop();         }         else    // 피연산자( 숫자 )이면         {             do             {                 pNode->data += strPostfix[ i ];                 i++;             }
  • 17.             while( strPostfix[ i ]!=' '&& i < strPostfix.GetLength());             pNode->pLeft = m_pNodeTail;             pNode->pRight = m_pNodeTail;         }         NodeStack.Push( pNode );     }     m_pNodeHead->pLeft = NodeStack.Pop();    // Root } 7. 결론 트리는 노드와 링크로 구성된 비선형 자료구조 이진 트리는 두개 이하의 자식을 가지는 트리 이진 트리의 Traversal은 트리의 모든 노드를 방문하기 위한 방법이다. InOrder, PreOrder, PostOrder, LevelOrder가 있다. 스택을 이용하여 후위 표기로 부터 수식 트리( Parse Tree )를 만들었다. Traversal 방법에 따라 다른 수식 표기가 나옴 [출처http://blog.naver.com/hkn10004?Redirect=Log&logNo=20109208779
  • 18. 2. 프로젝트 전체일정 기 간 5 / 8 5 / 10 - 조원별 업무 분담 및 계획 작성 - 프로젝트 문제파악 - 알고리즘에 대해 토의하고 이해 내 용 - 프로젝트에 필요한 학습내용 토의 - 문제점 토의 - 프로젝트 진행방향 토의 기 간 5 / 15 5 / 17 - 프로젝트 검토 내 용 - 소스 구현 및 보고서 작성 - 최종보고서 작성 - 발표준비 3. 업무분담 이 름 업 무 김창헌 보고서 작성 및 회의 진행, 프로젝트에 필요한 학습내용 숙지 김시백 알고리즘 설계와 소스에 대한 문제점 검토와 보완 박가혜 소스구현 및 코딩 문제의 해결을 위한 방안 제시 김정무 알고리즘 설계 및 프로젝트 수행 시 필요한 자료 수집 이상현 알고리즘 설계 및 프로젝트 수행 시 필요한 자료 수집
  • 19. 4. 초안 #include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX 1000 char SIGN[] = " .,!?:"trn"; typedef struct List { char *word; int count; struct List *next; } List; List head={0,}; List* tail = &head; int word_count = 0; // # of words List **W = 0; void array(List *head, List **W) { List *temp = head->next; int i = 0; while (temp) { W[i++] = temp; temp = temp->next; } } void printW(List **W, int n) { int i; for(i=0; i < n; i++) { printf("%20s : %dn",W[i]->word, W[i]->count); } }
  • 20. List* F_Token(const char* token) { List *temp = head.next; while (temp) { if (strcmp(temp->word,token)==0) return temp; temp = temp->next; } return 0; } void InsertList(char *token) { List *temp = F_Token(token); if (temp) // Found, { temp->count++; } else { int len = strlen(token)+1; temp = (List*)malloc(sizeof(List)); temp->word = (char*)malloc(sizeof(char)*len); strncpy(temp->word, token, len); temp->count = 1; temp->next = 0; tail->next = temp; tail=temp; word_count++; } } void free_token() { List *temp; while (temp = head.next) { if (temp->word) free(temp->word); head.next = temp->next;
  • 21. free(temp); } tail = &head; } typedef List *T; void swap(T *a, T *b) { T t = *a; *a = *b; *b = t; } int comp(const void *pa, const void *pb) { const T a = *(const T *)pa; const T b = *(const T *)pb; return strcmp(a->word,b->word); } int partition(T *A, int left, int right, int pivot) { int i, index; T value = A[pivot]; swap(&A[pivot],&A[right]); index = left; for (i = left; i < right; i++) { if (comp(&A[i],&value) < 0) { swap(&A[i],&A[index]); ++index; } } swap(&A[index],&A[right]); return index; } void quick_p(T *A, int left, int right) { int pivot; int index;
  • 22. if (right > left) { pivot = (right+left)/2; index = partition(A, left, right, pivot); quick_p(A, left, index-1); quick_p(A, index+1, right); } } void quicksort(T *A, int n) { quick_p(A,0,n-1); } int main(void) { char *str; char *token; long length; int i=0; FILE* fp = fopen("C:test.txt", "rt"); if (fp==NULL) { puts("File open fail!"); return -1; } fseek(fp, 0, SEEK_END); length = ftell(fp); rewind(fp); str = (char*)malloc(sizeof(char)*length+1); memset(str, 0, sizeof(char)*length+1); puts("*****************상호참조 생성기*****************"); puts("****************단어 : 빈도수******"); fread(str, 1, length, fp); fclose(fp); token=strtok(str,SIGN); while (token!=NULL) { InsertList(token);
  • 23. token=strtok(NULL,SIGN); } W = (List **)malloc(sizeof(List*)*word_count); array(&head, W); quicksort(W,word_count); // qsort(W,word_count,sizeof(T),comp); printW(W,word_count); free(W); free_token(); system("pause"); return 0; }
  • 24. 5. 문제점 단어별 라인이 나오지 않음. 단어의 대소문자 구별해서 나옴. 단어의 총 개수가 출력되지 않음.
  • 25. 6. 최종안 #include <stdio.h> #include <string.h> #include <stdlib.h> #define MAX 1000 char SIGN[] = " .,!?:"trn"; typedef struct List { char *word; int count; struct List *next; } List; List head={0,}; List* tail = &head; int word_count = 0; // # of words List **W = 0; void list2array(List *head, List **W) { List *temp = head->next; int i = 0; while (temp) { W[i++] = temp; temp = temp->next; } } void printW(List **W, int n) { int count=0; int i; for(i=0; i < n; i++) { printf("%20s : %dn",W[i]->word, W[i]->count); count++; }
  • 26. printf("tttotal=%d",count); } List* FindToken(const char* token) { List *temp = head.next; while (temp) { if (strcmp(temp->word,token)==0) return temp; temp = temp->next; } return 0; } void InsertList(char *token) { List *temp = FindToken(token); if (temp) // Found, { temp->count++; } else { int len = strlen(token)+1; temp = (List*)malloc(sizeof(List)); temp->word = (char*)malloc(sizeof(char)*len); strncpy(temp->word, token, len); temp->count = 1; temp->next = 0; tail->next = temp; tail=temp; word_count++; } } void print_token()
  • 27. { List *temp = head.next; FILE* fw = fopen("daulOuput.txt", "wt"); while (temp) { fprintf(fw, "%s : %dn", temp->word, temp->count); temp = temp->next; } fclose(fw); } void free_token() { List *temp; while (temp = head.next) { if (temp->word) free(temp->word); head.next = temp->next; free(temp); } tail = &head; } typedef List *T; // 정렬할 자료형 T void swap(T *a, T *b) { T t = *a; *a = *b; *b = t; } int comp(const void *pa, const void *pb) { const T a = *(const T *)pa; const T b = *(const T *)pb; return strcmp(a->word,b->word); }
  • 28. int partition(T *A, int left, int right, int pivot) { int i, index; T value = A[pivot]; swap(&A[pivot],&A[right]); index = left; for (i = left; i < right; i++) { if (comp(&A[i],&value) < 0) { swap(&A[i],&A[index]); ++index; } } swap(&A[index],&A[right]); return index; } void quickpart(T *A, int left, int right) { int pivot; int index; if (right > left) { pivot = (right+left)/2; index = partition(A, left, right, pivot); quickpart(A, left, index-1); quickpart(A, index+1, right); } } void quicksort(T *A, int n) { quickpart(A,0,n-1); } int main(void) { char *str; // File total Length, char *token; // token, long length; // 파일전체길이 int i=0; FILE* fp = fopen("cmake.txt", "rt"); //텍스트모드로 읽기
  • 29. // Open not file, if (fp==NULL) { puts("File open fail!"); return -1; } fseek(fp, 0, SEEK_END); length = ftell(fp); rewind(fp); str = (char*)malloc(sizeof(char)*length+1); memset(str, 0, sizeof(char)*length+1); puts("*****************상호참조 생성기*****************"); puts("****************단어 : 빈도수******"); fread(str, 1, length, fp); // 자료주소값, 바이트크기,개수,파일포인터 fclose(fp); //파일 닫기 // 단어 수집 token=strtok(str,SIGN); //문자열자르기 while (token!=NULL) { InsertList(token); // 두번째토큰, 호출부터는 첫 번째 인수자리에 NULL 기입 token=strtok(NULL,SIGN); } // 단어의 포인터 배열 W = (List **)malloc(sizeof(List*)*word_count); list2array(&head, W); // 리스트에서 배열로 quicksort(W,word_count); // 정렬 // qsort(W,word_count,sizeof(T),comp); // 정렬 printW(W,word_count); // 정렬 출력 free(W); free_token(); return 0; }
  • 30. 7. 느낀점 초안에서의 문제점은 아래와 같다. 단어별 라인이 나오지 않음.->개선X :fscanf를 이용하여,라인출력을 하고자 하였으나, 실패함. 단어의 대소문자 구별해서 나옴.->개선X strtok을 이용하여, 단어를 구분해주었는데, 여기서 대소문자 구별하는 방법을 찾아내지 못함. 단어의 총 개수가 출력되지 않음.->개선O