930 likes | 1.72k Views
자료구조. 제 5 장 트리. 5.1 서론. 5.1 서론. ▶ 트리 트리 : 하나이상의 노드 (node) 로 이루어진 유한집합 . ① 하나의 루트 노드 (root node) ② 나머지 노드들은 n( ≥ 0) 개의 분리 집합 (disjoint set) T 1 , T 2 , … , T n 으로 분할 (T i : 서브트리 ) • 노드의 차수 (degree) : 노드의 서브트리 수 • 단말 (terminal) 노드 : 차수 = 0, leaf • 비단말 노드 : 차수 0
E N D
자료구조 제 5 장 트리
5.1 서론 • ▶ 트리 • 트리 : 하나이상의 노드(node)로 이루어진 유한집합. • ① 하나의 루트 노드 (root node) • ② 나머지 노드들은 n(≥0)개의 분리 집합(disjoint set) T1, T2,…, Tn으로 분할 (Ti : 서브트리) • •노드의 차수(degree) : 노드의 서브트리 수 • •단말(terminal) 노드 : 차수 = 0, leaf • •비단말 노드 : 차수 0 • •노드 레벨 : 루트-레벨1 • •트리의 차수 = max노드의 차수 • •자손(child), 형제(sibling) • •선조(ancestor) : 루트까지의 경로상에 있는 모든 노드 • •자손(descendants) : 한 노드의 서브트리에 • 존재하는 모든 노드 • •트리의 높이 (height, depth) = max노드 레벨
5.1 서론 • ancestor of M = { H, D, A } • descendentorof D = { H, I, J, M }
5.1 서론 • ▶ 트리 표현 • 트리 : (A(B(E(K,L),F),C(G),D(H(M),I,J))) • - 리스트 표현 : 차수가 k인 트리의 노드 구조 • - 보조정리 5.1 • T의 차수 : k, 노드수 : n 일때 nk개의 자식필드중 • n(k-1)+1개의 필드(n≥1)가 0이다.
5.1 서론 • 왼쪽자식-오른쪽 형제 표현 그림 5.6 : 그림 5.2 트리의 왼쪽자식-오른쪽 형제 표현
5.1 서론 • - 차수가 2인 트리표현 : 이진 트리(binary tree) • 오른쪽형제 포인터를 • 45° 시계방향으로 • 회전 : 이진 트리 • 그림 5.7: 그림 5.2 트리의 왼쪽자식-오른쪽자식 표현
5.1 서론 • 그림 5.8 트리표현
5.2 이진 트리(Binary tree) • ▶ 추상 데이타 타입 • [정의] 노드의 유한 집합 • - 공백 • - 루트와 두개의 분리된 이진 트리 • (왼쪽 서브트리, 오른쪽 서브트리)
5.2 이진 트리(Binary tree) • ----------------------------------------- • template <class KeyType> • class BinaryTree{ • // objects: 공백이거나 루트 노드, 왼쪽 이진 트리, 오른쪽 • //이진 트리로 구성되는 노드들의 유한집합 • public: • BinaryTree();// 공백 이진 트리를 생성 • Boolean IsEmpty(); • // 이진 트리가 공백이면 TRUE(1) 반환; 그렇지 않으면 • // FALSE(0) 반환 • BinaryTree(BinaryTreebt1, Element<KeyType> item, • BinaryTreebt2);// 왼쪽 서브트리가 bt1, 오른쪽 서브트 • //리가 bt2, 루트 노드가 item을 갖는 이진트리를 생성 • BinaryTreeLchild(); • // IsEmpty()이면 에러 반환; 그렇지 않으면 *this의 왼쪽 • //서브트리 반환 • Element<KeyType> Data(); • // IsEmpty()이면 에러 반환; 그렇지 않으면 *this의 루트 • //노드의 데이타 반환 • BinaryTreeRchild(); • // IsEmpty()이면 에러 반환; 그렇지 않으면 *this의 오른 • //쪽 서브트리 반환 • }; • ----------------------------------------- • ADT5.1: 추상 데이타 타입 BinaryTree
5.2 이진 트리(Binary tree) • - 이진 트리와 일반 트리의 차이점 • •공백 이진 트리 존재 • •서브트리의 순서 중요 • 그림 5.9: 서로 다른 두 이진 트리 • 그림 5.10 : 편향 트리와 완전 이진 트리 • * 트리는 모두 2진 트리로 표현 가능
5.2 이진 트리(Binary tree) • ▶ 이진 트리의 특성 • - 보조정리 5.2[최대 노드수] • ⑴ 레벨 i에서의 최대 노드수 : 2i-1(i 1) • ⑵ 깊이가 k인 이진 트리가 가질수 있는 최대 노드수 • : 2k - 1(k 1) • - 보조정리 5.3 • [단말노드 수와 차수가 2인 노드 수와의 상관관계] • 공백이 아닌 모든 이진 트리 T에 대하여, • n0 : 단말 노드수 • n2 : 차수가 2인 노드 수 • n0 = n2 + 1 • ∵ n : 전체 노드수, B :전체 간선수(n1+2n2) • n1 : 차수가 1인 노드수 • n = n0 + n1+ n2 • - n = B + 1 = n1 + 2n2 +1 • ---------------------------- • n0 = n2 + 1
5.2 이진 트리(Binary tree) • - 포화 이진 트리(full binary tree) • •깊이가 K이고 • •노드수가 2K-1 인 이진 트리 • •노드 번호 1, ··· , 2k-1 까지 순차적 부여 가능 • 그림 5.11: 순차적 노드 번호를 붙인 깊이 4의 포화 이진 트리
5.2 이진 트리(Binary tree) • - 완전 이진 트리(Complete binary tree) • •이진 트리 (n 노드, 깊이 k) • •각 노드는 깊이가 k인 포화 이진 트리의 노드 번호 1부터 n까지 순차적으로 대응 • n 노드 완전 이진 트리의 높이 : log2(n+1)
5.2 이진 트리(Binary tree) • - 보조정리 5.4 • 완전이진트리(n), i: 노드번호, 트리깊이= log2(n+1) • ① PARENT(i) :i/2 if i 1 • ② LCHILD(i) : 2i if 2i n • otherwise no left child • ③ RCHILD(i) : 2i+1 if 2i+1 n • otherwise no right child
5.2 이진 트리(Binary tree) • ▶ 이진 트리의 표현 방법 • - 이진 트리의 배열 표현 • ① 완전 이진 트리 : 낭비 공간 없음 • ② 편향 이진 트리(깊이 k) : 2k-1중 k만 사용(최악의 경우)
5.2 이진 트리(Binary tree) • - 링크 표현 • 그림 5.13 : 노드 표현 • class Tree; //전방 선언 • class TreeNode { • friend class Tree; • private: • TreeNode*LeftChild; • char data; • TreeNode*RightChild; • }; • class Tree { • public: • // 트리 연산들 • ... • private: • TreeNode*root; • };
5.2 이진 트리(Binary tree) • 그림 5.14 : 그림 5.10의 이진 트리에 대한 링크 표현
5.3 이진 트리의 순회와 트리 반복자 • - 완전한 순회는 노드의 선형 순서를 생성. • L : 왼쪽 이동 • D : 노드방문(데이타 출력) • R : 오른쪽 이동 • •순회 방법 : LDR, LRD, DLR, DRL, RDL, RLD • •왼쪽을 오른쪽보다 먼저 방문(LR) • LDR(inorder) : infix • DLR(preorder) : prefix • LRD(postorder) : postfix • note : "Data"의 위치 • •산술식의 이진트리 표현 • 그림 5.16
5.3 이진 트리의 순회와 트리 반복자 • ▶ 중위 순회(Inordertraversal) • LDR • ① 왼쪽 서브트리 순회 • ② 루트 방문 (print root) • ③ 오른쪽 서브트리 순회 • ----------------------------------------- • 1 void Tree::inorder() • 2 // 전체 트리를 순회하는 Driver는 Workhorse를 호출하며 • 3 // Driver는 Tree 클래스의 공용 멤버 함수로 선언됨. • 4 { • 5 inorder(root); • 6 } • 7 void Tree::inorder(TreeNode*CurrentNode) • 8 // Workhorse는 이진 트리의 한 노드에 대한 포인터인 • 9 //CurrentNode가 루트인 서브 트리를 순회함. • //이 Workhorse는 Tree 클래스의 전용 멤버 함수로 선언 • 10 { • 11 if (CurrentNode) { • 12 inorder(CurrentNode->LeftChild); • 13 cout<< CurrentNode->data; • 14 inorder(CurrentNode->RightChild); • 15 } • 16 } • ----------------------------------------- • 프로그램 5.1: 이진 트리의 중위 순회
5.3 이진 트리의 순회와 트리 반복자 • Output : A/B*C*D+E (수식의 중위 표기)
5.3 이진 트리의 순회와 트리 반복자 • ▶ 전위 순회(Preorder traversal) • DLR • ① 루트 방문 • ② 왼쪽 서브트리 순회 • ③ 오른쪽 서브트리 순회 • ----------------------------------------- • 1 void Tree::preorder() • 2 // 전체 트리를 순회하는 Driver는 Workhorse를 호출하며 • 3 // Driver는 Tree 클래스의 공용 멤버 함수로 선언됨. • 4 { • 5 preorder(root); • 6 } • 7 void Tree::preorder(TreeNode*CurrentNode) { • 8 // Workhorse는 CurrentNode(이진 트리의 한 노드에 대한 • 9 // 포인터)를 루트로하는 서브 트리를 순회한다. 이 • 10 // Workhorse는 Tree 클래스의 전용 멤버 함수로 선언됨. • 11 if (CurrentNode) { • 12 cout<< CurrentNode->data; • 13 preorder(CurrentNode->LeftChild); • 14 preorder(CurrentNode->RightChild); • 15 } • 16 } • ----------------------------------------- • 프로그램 5.2: 이진 트리의 전위 순회 • Output : +**/ABCDE(수식의 전위 표기)
5.3 이진 트리의 순회와 트리 반복자 • ▶ 후위 순회(Postorder traversal) • LRD • ① 왼쪽 서브트리 순회 • ② 오른쪽 서브트리 순회 • ③ 루트 방문 • ----------------------------------------- • 1 void Tree::postorder() • 2 // 전체 트리를 순회하는 Driver는 Workhorse를 호출하며 • 3 // Driver는 Tree 클래스의 공용 멤버 함수로 선언됨. • 4 { • 5 postorder(root); • 6 } • 7 void Tree::postorder(TreeNode*CurrentNode) • 8 // Workhorse는 CurrentNode(이진 트리의 한 노드에 대한 • 9 // 포인터)를루트로 하는 서브 트리를 순회한다. 이 • 10 // Workhorse는 Tree 클래스의 전용 멤버 함수로 선언됨. • 11 if (CurrentNode) { • 12 postorder(CurrentNode->LeftChild); • 13 postorder(CurrentNode->RightChild); • 14 cout<< CurrentNode->data; • 15 } • 16 } • ----------------------------------------- • 프로그램 5.3: 이진 트리의 후위 순회 • Output : AB/C*D*E+ (수식의 후위 표기)
5.3 이진 트리의 순회와 트리 반복자 • ▶ 반복적 중위 순회 • •스택을 사용 • •링크로 표현된 이진 트리 • •알고리즘의 작성 방법 • ① recursive method • ~ an internal stack • ② non-recursive method • ~ an extenalstack • ③ non-recursive stacklessmethod • ~ parent field • ④ nonrecursivestacklessmethod • ~ thread, no parent field • ⑤ nonrecursivestacklessmethod • ~ unthreaded, no parent field
5.3 이진 트리의 순회와 트리 반복자 • 반복적 중위 순회 • recursive ------> iterative • ↑ • stack-based • ----------------------------------------- • 1 void Tree::NonrecInorder() • 2 // 스택을 이용한 비순환적 트리 순회 • 3 { • 4 Stack<TreeNode*> s; // 스택의 선언과 초기화 • 5 TreeNode*CurrentNode= root; • 6 while(1) { • 7 while(CurrentNode) { // 왼쪽 자식 필드로 이동 • 8 s.add(CurrentNode); // 스택에 삽입 • 9 CurrentNode= CurrentNode->LeftChild; • 10 } • 11 if (!s.IsEmpty()) { //* 스택이 공백이 아님 • 12 CurrentNode= *s.Delete(CurrentNode); // 스택에서 //의 삭제 • 13 cout<< CurrentNode->data << endl; • 14 CurrentNode= CurrentNode->RightChild; • 15 } • 16 else break; • 17 } • 18 } • ----------------------------------------- • 프로그램 5.4: 비순환적 중위 순회
5.3 이진 트리의 순회와 트리 반복자 • ----------------------------------------- • class InorderIterator { • public: • char *Next(); • InorderIterator(Tree tree) : t(tree) { • CurrentNode= t.root; • }; • private: • Tree t; • Stack <TreeNode*> s; • TreeNode*CurrentNode; • }; • ----------------------------------------- • 프로그램 5.5: 중위 반복자 클래스의 정의
5.3 이진 트리의 순회와 트리 반복자 • ----------------------------------------- • char *InorderIterator::Next() • { • while(CurrentNode) { • s.add(CurrentNode); • CurrentNode= CurrentNode->LeftChild; • } • if (!s.IsEmpty()) { • CurrentNode= *s.Delete(CurrentNode); • char &temp = CurrentNode->data; • CurrentNode= CurrentNode->RightChild; • //CurrentNode를 수정 • return &temp; • } • else return 0; // 트리가 순회되었고 더 이상 원소 없음 • } • ----------------------------------------- • 프로그램 5.6: 다음 중위 원소를 가져오는 코드
5.3 이진 트리의 순회와 트리 반복자 • ▶ 레벨 순서 순회 • •큐를 사용. • 그림 5.16을 레벨 순회한 결과 : +*E*D/CAB • ----------------------------------------- • void Tree::LevelOrder() • // 이진 트리의 레벨 순서 순회 • { • QUEUE<TreeNode*> q; • TreeNode*CurrentNode= root; • while(CurrentNode) { • cout<<CurrentNode->data<<endl; • if (CurrentNode->LeftChild) • q.add(CurrentNode->LeftChild); • if (CurrentNode->RightChild) • q.add(CurrentNode->RightChild); • CurrentNode= *q.Delete(CurrentNode); • } • } • ----------------------------------------- • 프로그램 5.7: 이진 트리의 레벨 순서 순회
5.3 이진 트리의 순회와 트리 반복자 • ▶ 스택없는 순회 • - 각 노드에 parent 필드 추가. • - 각 노드에 두 비트가 필요. • 이진 트리를 스레드(thread)이진 트리로 표현.
5.4 이진 트리의 추가 연산 • ▶ 이진 트리의 복사 • ----------------------------------------- • // 복사 생성자(copy constructor) • Tree::Tree(constTree &s) // Driver • { • root = copy(s.root); • } • TreeNode*Tree::copy(TreeNode*orignode) // Workhorse • // 이 함수는 루트가 orignode인 트리를 복사하여 복사된 트 • // 리의 포인터를 반환한다. • { • if (orignode) { • TreeNode*temp = new TreeNode; • temp->data = orignode->data; • temp->LeftChild= copy(orignode->LeftChild); • temp->RightChild= copy(orignode->RightChild); • return temp; • } • else return 0; • } • ----------------------------------------- • 프로그램 5.9: 이진 트리의 복사
5.4 이진 트리의 추가 연산 • ▶ 이진 트리의 동일성 • - 동일한 구조 • - 동일한 정보 • ----------------------------------------- • // Driver - Tree 클래스의 friend로 가정. • int operator==(constTree& s, Tree& t) • { • return equal(s.root, t.root); • } • // Workhorse - TreeNode클래스의 friend로 가정. • int equal(TreeNode*a, TreeNode*b) • // 이 함수는 a와 b의 서브트리들이 동일하지 않으면 0을, 동일하면 1을 반환한다. • { • if ((!a) && (!b)) return 1; // a, b 둘다 0 • if (a && b && (a->data == b->data) // data가 같다 • && equal(a->LeftChild, b->LeftChild) // 왼쪽 서브트리 //가 같다 • && equal(a->RightChild, b->RightChild)) //오른쪽 서브 • //트리가 같다 • return 1; • return 0; • } • ----------------------------------------- • 프로그램 5.10: 이진 트리 동일성
5.4 이진 트리의 추가 연산 • ▶ 만족성 문제 • 명제 해석(propositional calculus) • - 불리언 변수 x1, x2, ...., xn 과 연산자 ∧,∨,¬로 이루어진식 • - { T, F } 로 사상 • ⑴ 변수 자체도 하나의 식 • ⑵ x,y가 식 일때, ¬x, x∧y, x∨y 도 식 • ⑶ 연산순서는 ¬,∧,∨.
5.4 이진 트리의 추가 연산 • (x1∧¬x2)∨(¬x1∧x3)∨¬x3 • 그림 5.18 : 이진 트리로 표현된 명제식 • - 중위순회 : x1∧¬x2∨¬x1∧x3∨¬x3 • x1 x2 x3 formula • T T T ? • T T F ? evaluation : • T F T ? postorder traversal • T F F ? • . • :
5.4 이진 트리의 추가 연산 • - 후위 순회 계산 • data : boolean variable or boolean operator • value : T, F
5.4 이진 트리의 추가 연산 • enumBoolean {FALSE, TRUE}; • enumTypesOfData {LogicalNot, LogicalAnd, LogicalOr, • LogicalTrue, LogicalFalse}; • class SatTree; // 전방 선언 • class SatNode { • friend class SatTree; • private: • SatNode*LeftChild; • TypesOfDatadata; • Boolean value; • SatNode*RightChild; • }; • class SatTree { • public • void PostOrderEval(): • void rootvalue() { cout<< root->value;} ; • private: • SatNode*root; • void PostOrderEval(SatNode*); • };
5.4 이진 트리의 추가 연산 • ----------------------------------------- • for n변수에 대한 2n개의 가능한 참값 조합 • { • 다음 조합을 생성; • 그들의 값을 변수에 대입; • 명제식을 나타내는 트리를 후위 순회로 계산; • if (formula.rootvalue()) { cout<< combination; return; } • } • cout<< "no satisfiablecombination"; • ----------------------------------------- • 프로그램 5.11: 만족성 알고리즘의 첫번째 버전
5.4 이진 트리의 추가 연산 • ----------------------------------------- • void SatTree::PostOrderEval() // Driver • { PostOrderEval(root); } • void SatTree::PostOrderEval(SatNode*s) // Workhorse • { • if (s) { • PostOrderEval(s->LeftChild); • PostOrderEval(s->RightChild); • switch(s->data) { • case LogicalNot: s->value = !s->RightChild->value; break; • case LogicalAnd: s->value = • s->RightChild->value && s->LeftChild->value; • break; • case LogicalOr: • s->value = s->RightChild->value || • s->LeftChild->value; • break; • case LogicalTrue: s->value = TRUE; break; • case LogicalFalse: s->value = FALSE; • } • } • } • ----------------------------------------- • 프로그램 5.12: 명제식의 계산
5.5 스레드 이진 트리 • ▶스레드 • •n 노드 이진 트리의 연결 표현 • - 총 링크 필드의 수 : 2n • - 널 링크 필드의 수 : n+1 • 널 링크 수 > 실제 포인터 수 • •스레드(Thread) • - 널 링크 필드에 삽입된 포인터(널 필드의 활용) • ① P->RightChild= 0 • P->RightChild: 중위순회시 P의 successor에 • 대한 포인터 • ② P->LeftChild= 0 • P->LeftChild: 중위순회시 P의 predecessor에 • 대한 포인터
5.5 스레드 이진 트리 • - 중위 순회 • H D I B E A F C G
5.5 스레드 이진 트리 • - 스레드 이진 트리의 표현 • LeftThread== F : LeftChild←pointer • == T : LeftChild←thread • RightThread== F : RightChild←pointer • == T : RightChild←thread • dangling 포인터 문제 → 헤드 노드 • 공백 이진 트리 : 헤드 노드
- 스레드 이진 트리를 위한 클래스 정의 • ----------------------------------------- • class ThreadedNode { • friend class ThreadedTree; • friend class ThreadedInorderIterator; • private: • Boolean LeftThread; • ThreadedNode*LeftChild; • char data; • ThreadedNode*RightChild; • Boolean RightThread; • }; • class ThreadedTree { • friend class ThreadedInorderIterator; • public: • : // 트리 조작 연산들 • private: • ThreadedNode*root; • }; • class ThreadedInorderIterator { • public: • char *Next(); • ThreadedInorderIterator(ThreadedTreetree):t(tree) • CurrentNode= t.root; ; • private: • ThreadedTreet; • ThreadedNode*CurrentNode; • }; • -----------------------------------------
5.5 스레드 이진 트리 • - 스레드 이진 트리의 메모리 표현 • 주의 : 헤드 노드의 왼쪽 서브트리
5.5 스레드 이진 트리 • ▶ 스레드 이진 트리의 중위 순회 • •스택을 이용하지 않고 순회 • •중위 순회의 후속자 • RightThread == T : RightChild • == F : 오른쪽 자식의 왼쪽 자식 링크를 따라 • LeftThread == TRUE인 노드 • - 스레드 이진 트리에서 중위 후속자를 찾는 함수 • ----------------------------------------- • char *ThreadedInorderIterator::Next() { • // 스레드 이진 트리에서 CurrentNode의 중위 후속자를 찾는다 • ThreadedNode *temp = CurrentNode->RightChild; • if (!CurrentNode->RightThread) • while (!temp->LeftThread) temp = temp->LeftChild; • CurrentNode = temp; • if (CurrentNode==t.root) return 0; • else return &CurrentNode->data; • } • ----------------------------------------- • - 스레드 이진 트리의 중위 순회 • ----------------------------------------- • void ThreadedInorderIterator::Inorder() { • for (char *ch=Next(); ch; ch = Next()) cout<< *ch << endl; • } • -----------------------------------------
5.5 스레드 이진 트리 • ▶ 스레드 이진 트리에서의 노드 삽입 • - s의 오른쪽 자식으로 r을 삽입
5.5 스레드 이진 트리 • - s의 오른쪽 자식으로 r을 삽입 • ----------------------------------------- • void ThreadTree::InsertRight(ThreadedNode *s, • ThreadedNode *r) • // 스레드 이진 트리에서 r을 s의 오른쪽 자식으로 삽입 • { • r->RightChild = s->RightChild; • r->RightThread = s->RightThread; • r->LeftChild = s; • r->LeftThread = TRUE; // 왼쪽 자식은 스레드 • s->RightChild = r; // r을 s에 연결 • s->RightThread = FALSE; • if (!r->RightThread) { • ThreadedNode *temp = InorderSucc(r); • // r의 중위 후속자를 반환 • temp->LeftChild = r; • } • } • -----------------------------------------
5.6 히프(heap) • ▶ 우선 순위 큐 • •완전 이진 트리의 응용 • •히프(heap):우선 순위 큐(priorty queue)를 구현하는 방법 • •서비스 순서: • Max priority queue : highest priority first • in job scheduler • Min priority queue : lowest priority first • template <class Type> • class MaxPQ { • public: • virtual void Insert(const Element<Type>&) = 0; • virtual Element<Type> *DeleteMax(Element<Type>&) = 0; • }; • Element<Type> : Key 데이타 멤버를 가진 struct