CHAP 7:트리.

Slides:



Advertisements
Similar presentations
스택 스택 추상자료형 스택 스택의 구현 스택의 응용 한빛미디어(주).
Advertisements

자료구조: CHAP 7 트리(1) 순천향대학교 컴퓨터공학과 하 상 호.
1. 자료구조 개념 (1) 자료구조의 개요 1) 자료구조 ① 일련의 자료들을 조직하고 구조화하는 것 ② 자료의 표현과 그것과 관련된 연산 2) 자료구조에 따라 저장공간의 효율성과 프로그램의 실행시간이 달라짐 3) 어떠한 자료구조에서도 필요한 모든 연산들을 처리하는 것이.
Internet Computing KUT Youn-Hee Han
CHAP 1:자료구조와 알고리즘.
8. 파일 인덱스: 탐색트리 (Search Tree)
Chapter 7. Binary Search Trees - 보충 자료-
CHAP 1:자료구조와 알고리즘 C로 쉽게 풀어쓴 자료구조 생능출판사 2005.
제 8 장  파서 생성기 YACC 사용하기.
5장. 리스트 리스트 학습목표 목록이나 도표처럼 여러 데이터를 관리할 수 있는 자료형을 추상화
트리 이 현 직
6장 트리.
Internet Computing KUT Youn-Hee Han
C 11장. 포인터의 활용 #include <stdio.h> int main(void) { int num;
CHAP 10 : 그래프 C로 쉽게 풀어쓴 자료구조 생능출판사 학기 데이터구조.
CHAP 10 : 그래프 C로 쉽게 풀어쓴 자료구조 Slide 1 (of 62).
4장 스택.
제5장 제어명령
CHAP 7:트리 C로 쉽게 풀어쓴 자료구조 Slide 1 (of 50).
강의 #7 트리(Tree).
쉽게 배우는 알고리즘 5장. 검색트리
6장. printf와 scanf 함수에 대한 고찰
스택(stack) SANGJI University Kwangman Ko
자료구조: CHAP 7(3) 트리 순천향대학교 컴퓨터공학과 하 상 호.
자료구조: CHAP 7 이진 탐색 트리 순천향대학교 컴퓨터공학과 하 상 호.
CHAP 7:트리 C로 쉽게 풀어쓴 자료구조 생능출판사 2011.
CHAP 6:큐 C로 쉽게 풀어쓴 자료구조 Slide 1 (of 27).
다음 주 과제 7장 읽어오기 숙제 해서 다음 주(11월 12일) 제출하기. 큐(Queue) E304호,
Chapter 9 – 구조형과 리스트 처리 Outline 9.1 자신 참조 구조형 9.2 선형 연결 리스트 9.3 리스트 연산
자료구조 김현성.
쉽게 풀어쓴 C언어 Express 제17장 동적메모리와 연결리스트 C Express.
25장. 메모리 관리와 동적 할당.
C언어 응용 제 10 주 트리.
이진트리 순회 트리내의 모든 노드를 특정 순서에 따라 한번씩 방문 하는 것 방문 순서 R: Moving Right Child
CHAP 1:자료구조와 알고리즘 C로 쉽게 풀어쓴 자료구조 생능출판사 Slide 1 (of 28)
그래프(graph) 연결되어 있는 객체 간의 관계를 표현하는 자료구조 가장 일반적인 자료구조 형태
CHAP 7: 트 리 (1) 한 성 대 학 교 정보통신공학과 봉 정 식.
IT CookBook, 자바로 배우는 쉬운 자료구조
CHAP 8:우선순위큐 C로 쉽게 풀어쓴 자료구조 생능출판사 2011.
Tree & Heap SANGJI University Kwangman Ko
4장 제어문 선택문: if 문, if – else 문, switch 문
CHAP 7:트리 C로 쉽게 풀어쓴 자료구조 생능출판사 2005.
스케줄링 (Scheduling) 시스템 내부시간(time in the system): 스케줄링 문제
C언어 응용 제10주 실습 해보기 제8장 트리.
Tree & Heap SANGJI University Kwangman Ko
CHAP 7:트리 C로 쉽게 풀어쓴 자료구조 생능출판사 2005.
컴퓨터의 기초 제 2강 - 변수와 자료형 , 연산자 2006년 3월 27일.
다음 주 과제 3장 읽어오기 숙제 해서 제출하기. 자료구조와 알고리즘, 순환 E304호,
Chap. 1 Data Structure & Algorithms
제어문 & 반복문 C스터디 2주차.
3장. 변수와 연산자. 3장. 변수와 연산자 3-1 연산자, 덧셈 연산자 연산자란 무엇인가? 연산을 요구할 때 사용되는 기호 ex : +, -, *, / 3-1 연산자, 덧셈 연산자 연산자란 무엇인가? 연산을 요구할 때 사용되는 기호 ex : +, -, *, /
자료구조: CHAP 7(2) 트리 순천향대학교 컴퓨터공학과 하 상 호.
조 병 규 Software Quality Lab. 한 국 교 통 대 학 교
6장 반복제어문 for 문 while 문 do while 문 기타 제어문.
CHAP 8:우선순위큐.
CHAP 12 : 탐색.
CHAP 12 : 탐색 C로 쉽게 풀어쓴 자료구조 생능출판사 2011.
CHAP 10 : 그래프.
쉽게 풀어쓴 C언어 Express 제6장 조건문 C Express Slide 1 (of 28)
제 8 장 계산복잡도 개론 검색 문제 알고리즘 강의 슬라이드 8 검색 문제
스케줄링 (Scheduling) 시스템 내부시간(time in the system): 스케줄링 문제
쉽게 풀어쓴 C언어 Express 제6장 조건문 C Express.
탐색 선형 탐색, 이진 탐색, 이진 탐색 트리.
제 5 장 탐색트리.
Chapter 07 트리.
9장. C 언어의 핵심! 함수. 9장. C 언어의 핵심! 함수 9-1 함수의 정의와 선언 main 함수 다시 보기 : 함수의 기본 형태 { } 그림 9-1.
자료구조 자료구조의 개요 배열 선형 리스트 스택 큐 트리.
어서와 C언어는 처음이지 제16장.
[CPA340] Algorithms and Practice Youn-Hee Han
어서와 C언어는 처음이지 제22장.
Presentation transcript:

CHAP 7:트리

트리(TREE) 트리: 계층적인 구조를 나타내는 자료구조 트리는 부모-자식 관계의 노드들로 이루어진다. 응용분야: 리스트, 스택, 큐 등은 선형 구조 트리는 부모-자식 관계의 노드들로 이루어진다. 응용분야: 계층적인 조직 표현 컴퓨터 디스크의 디렉토리 구조 인공지능에서의 결정트리 (decision tree)

회사의 조직

파일 디렉토리 구조

결정 트리 (예) 골프에 대한 결정 트리

트리의 용어 노드(node): 트리의 구성요소 루트(root): 부모가 없는 노드(A) 서브트리(subtree): 하나의 노드와 그 노드의 자손 노드들로 이루어진 (전체 트리의) 부분 트리

트리의 용어 단말노드(leaf node or terminal node): 자식이 없는 노드(E,F,G,H,I,J) 비단말노드/내부노드(internal node): 적어도 하나의 자식을 가지는 노드 (A,B,C,D)

트리의 용어 깊이/레벨 1 2 자식, 부모, 형제, 조상, 자손 노드: 인간과 동일 1 2 C 자식, 부모, 형제, 조상, 자손 노드: 인간과 동일 깊이(depth): 루트에서 특정노드까지의 경로에 존재하는 edge의 수를 그 노드의 depth라 함 높이(height): 트리의 최대 깊이 차수(degree): 노드가 가지고 있는 자식 노드의 개수

예제 A는 루트 노드이다. B는 D와 E의 부모노드이다. C는 B의 형제 노드이다. D와 E는 B의 자식노드이다. 위의 트리의 높이는 3이다.

이진트리 (binary tree) 이진 트리(binary tree) : 모든 노드가 최대 2개의 자식 노드를 가지는 트리 모든 노드의 차수가 2 이하가 된다.

이진트리의 성질 높이가 h인 이진트리의 경우, 최소 h+1 개의 노드를 가지며 최대 2h+1-1개의 노드를 가진다. 1 + x1 + ••• + xn = (1–xn+1)/(1–x), x≠1 ← x =2, n = h 높이 = 2

이진트리의 성질 n개의 노드를 가지는 이진트리의 높이 최대 n - 1 최소 n = 2h+1 - 1 → h = log2(n+1) - 1 = log2n 높이 = 6 높이 = 2

이진트리의 분류 포화 이진 트리(full binary tree) 완전 이진 트리(complete binary tree) 기타 이진 트리

포화 이진 트리 트리의 각 노드가 자식이 없거나 또는 자식이 둘인 이진트리. 즉, 자식이 하나인 노드가 없는 이진트리 A full binary tree is a tree in which every node other than the leaves has two children.

완전 이진 트리 완전 이진 트리(complete binary tree): 마지막 레벨을 제외한 모든 레벨에는 노드가 꽉 채워져 있으며, 마지막 레벨에서는 왼쪽부터 오른쪽으로 노드가 순서대로 배치되어 있는 이진트리

이진트리의 표현 배열을 이용하는 방법 포인터를 이용하는 방법

배열 표현법 배열표현법: 이진트리의 각 노드에 완전이진트리에서와 같이 번호를 붙여서 그 번호를 배열의 인덱스로 삼아 노드의 데이터를 배열에 저장하는 방법

부모와 자식 인덱스 관계 노드 i의 부모 노드 인덱스 = i/2 노드 i의 왼쪽 자식 노드 인덱스 = 2i

링크 표현법 링크 표현법: 포인터를 이용하여 부모노드가 자식노드를 가리키게 하는 방법

링크의 구현 노드는 구조체로 표현 링크는 포인터로 표현 typedef struct TreeNode { int data; struct TreeNode *left, *right; } TreeNode;

링크 표현법 프로그램 #include <stdio.h> #include <stdlib.h> #include <memory.h> typedef struct TreeNode { int data; struct TreeNode *left, *right; } TreeNode; // n1 // / | // n2 n3 void main() { TreeNode *n1, *n2, *n3; n1= (TreeNode *)malloc(sizeof(TreeNode)); n2= (TreeNode *)malloc(sizeof(TreeNode)); n3= (TreeNode *)malloc(sizeof(TreeNode));

링크 표현법 프로그램 n1->data = 10; // 첫번째 노드를 설정한다. n1->left = n2; n1->right = n3; n2->data = 20; // 두번째 노드를 설정한다. n2->left = NULL; n2->right = NULL; n3->data = 30; // 세번째 노드를 설정한다. n3->left = NULL; n3->right = NULL; }

이진트리의 순회 순회(traversal): 트리의 노드들을 체계적으로 방문하는 것 3가지의 기본적인 순회방법 전위순회(preorder traversal)  : VLR 자손노드보다 루트노드를 먼저 방문한다. 중위순회(inorder traversal)  : LVR 왼쪽 자손, 루트, 오른쪽 자손 순으로 방문한다. 후위순회(postorder traversal) : LRV 루트노드보다 자손을 먼저 방문한다.

전위 순회 1. 루트 노드를 방문한다 2. 왼쪽 서브트리를 방문한다 3. 오른쪽 서브트리를 방문한다

전위순회 프로그램 순환 호출을 이용한다. preorder(x) if x≠NULL then print DATA(x); preorder(LEFT(x)); preorder(RIGHT(x));

전위 순회 응용 (예) 구조화된 문서출력

중위 순회 1. 왼쪽 서브트리를 방문한다 2. 루트 노드를 방문한다 3. 오른쪽 서브트리를 방문한다

중위순회 알고리즘 순환 호출을 이용한다. inorder(x) if x≠NULL then inorder(LEFT(x)); print DATA(x); inorder(RIGHT(x));

중위 순회 응용 (예) 수식 트리 4 6 5 7

후위 순회 1. 왼쪽 서브트리를 방문한다 2. 오른쪽 서브트리를 방문한다 3. 루트 노드를 방문한다

후위순회 알고리즘 순환 호출을 이용한다. postorder(x) if x≠NULL then postorder(LEFT(x)); postorder(RIGHT(x)); print DATA(x);

후위 순회 응용 (예) 디렉토리 용량 계산 “정지영상”디렉토리의 파일들의 총 합: 200 (정지영상의 서브디렉토리 없음) “정지영상”디렉토리의 파일들의 총 합: 200 (정지영상의 서브디렉토리 없음) “동영상”디렉토리의 파일들의 총 합: 500 (동영상의 서브디렉토리 없음) “그림”디렉토리의 파일들의 총 합: 100 (서브 디렉토리 제외) -- 그림 디렉토리 크기 총 합: 200 + 500 + 100 = 800

순회 프로그램 typedef struct TreeNode { int data; struct TreeNode *left, *right; } TreeNode; // 15 // 4 20 // 1 16 25 TreeNode n1 = {1, NULL, NULL}; TreeNode n2 = {4, &n1, NULL}; TreeNode n3 = {16, NULL, NULL}; TreeNode n4 = {25, NULL, NULL}; TreeNode n5 = {20, &n3, &n4}; TreeNode n6 = {15, &n2, &n5}; TreeNode *root = &n6;

// 중위 순회 inorder( TreeNode *root ){ if ( root ){ inorder( root->left ); // 왼쪽서브트리 순회 printf("%d", root->data ); // 노드 방문 inorder( root->right ); // 오른쪽서브트리 순회 } // 전위 순회 preorder( TreeNode *root ){ preorder( root->left ); // 왼쪽서브트리 순회 preorder( root->right ); // 오른쪽서브트리 순회

// 후위 순회 postorder( TreeNode // 후위 순회 postorder( TreeNode *root ){ if ( root ){ postorder( root->left ); // 왼쪽서브트리 순회 postorder( root->right ); // 오른쪽서브트리순회 printf("%d", root->data ); // 노드 방문 } void main() { inorder(root); preorder(root); postorder(root);

레벨 순회 레벨 순회(level order)는 각 노드를 레벨 순으로 검사하는 순회 방법 레벨 순회는 큐를 사용하는 순회법 B C D C D D

레벨 순회 알고리즘 level_order(root) initialize queue; enqueue(queue, root); while is_empty(queue)≠TRUE do x← dequeue(queue); if( x≠NULL) then print DATA(x); enqueue(queue, LEFT(x)); enqueue(queue, RIGHT(x));

수식 트리 수식트리: 산술식을 트리형태로 표현한 것 예) 비단말노드: 연산자(operator) 단말노드: 피연산자(operand) 예) 수식 a + b a - (b × c) (a < b) or (c < d) 전위순회 + a b - a × b c or < a b < c d 중위순회 a - b × c a < b or c < d 후위순회 a b + a b c × - a b < c d < or

수식트리계산 후위순회를 사용 서브트리의 값을 순환호출로 계산 비단말노드를 방문할때 양쪽서브트리의 값을 노드에 저장된 연산자를 이용하여 계산한다

수식트리 알고리즘 evaluate(exp) if exp = NULL then return 0; else x←evaluate(exp->left); y←evaluate(exp->right); op←exp->data; return (x op y);

프로그램 typedef struct TreeNode { int data; struct TreeNode *left, *right; } TreeNode; // + // * + // 1 4 16 25 TreeNode n1={1, NULL, NULL}; TreeNode n2={4, NULL, NULL}; TreeNode n3={'*', &n1, &n2}; TreeNode n4={16, NULL, NULL}; TreeNode n5={25, NULL, NULL}; TreeNode n6={'+', &n4, &n5}; TreeNode n7={'+', &n3, &n6}; TreeNode *exp= &n7;

int evaluate(TreeNode int evaluate(TreeNode *root) { if( root == NULL) return 0; if( root->left == NULL && root->right == NULL) return root->data; else { int op1 = evaluate(root->left); int op2 = evaluate(root->right); switch(root->data){ case '+': return op1+op2; case '-': return op1-op2; case '*': return op1*op2; case '/': return op1/op2; } void main() { printf("%d", evaluate(exp));

디렉토리 용량 계산 디렉토리의 용량을 계산하는데 후위 트리 순회 사용

디렉토리 용량 계산 프로그램 int calc_direc_size(TreeNode *root) { int size = 0, left_size = 0, right_size = 0; if (root != NULL) { left_size = calc_direc_size( root->left ); right_size = calc_direc_size(root->right ); size = root->data + left_size + right_size; } return size; void main() { TreeNode n4={500, NULL, NULL}; TreeNode n5={200, NULL, NULL}; TreeNode n3={100, &n4, &n5}; TreeNode n2={50, NULL, NULL}; TreeNode n1={0, &n2, &n3}; printf("디렉토리의 크기=%d\n",calc_direc_size(&n1)); 서브디렉토리의 개수가 최대 2개라고 가정하에 작동하는 프로그램.

이진트리연산: 노드 개수 탐색 트리안의 노드의 개수를 계산 각각의 서브트리에 대하여 순환 호출한 다음, 반환되는 값에 1을 더하여 반환 int get_node_count(TreeNode *node) { int count=0; if( node != NULL ) count = 1 + get_node_count(node->left)+ get_node_count(node->right); return count; } 6 3 2 1 1 1

이진트리연산: 높이 서브트리에 대하여 순환호출하고 서브 트리들의 반환값 중에서 최대값에 1을 더하여 반환 int get_height(TreeNode *node) { int height = 0; if(node == NULL) return height; if(node->left != NULL || node->right != NULL) height = 1 + max(get_height(node->left), get_height(node->right)); return height; } h = 1 + max(hleft, hright)

스레드 이진 트리 G C F A B D E 이진트리의 NULL 링크를 이용하여 순환호출 없이도 트리의 노드들을 순회 NULL 링크에 중위 순회시에 후속 노드인 중위 후속자(inorder successor)를 저장시켜 놓은 트리가 스레드 이진 트리(threaded binary tree) 4 G 2 6 C F 1 3 5 7 A B D E

스레드 이진 트리의 구현 오른쪽 링크에 자식노드의 주소가 저장되어 있는지 아니면 스레드(후속자 노드의 주소)가 저장되어 있는지를 구별해주는 태그 필드(is_thread)가 필요 typedef struct TreeNode { int data; struct TreeNode *left, *right; int is_thread; } TreeNode; 만약 오른쪽 링크가 자식노드가 아닌 후속자 노드를 가리키면 is_thread는 TRUE

스레드 이진 트리의 구현 중위 후속자를 찾는 함수 작성 // TreeNode *find_successor(TreeNode *p) { // q는 p의 오른쪽 포인터 TreeNode *q = p->right; // 만약 오른쪽 포인터가 NULL이거나 스레드이면 오른쪽 포인터를 반환 // NULL인 경우 후속자 노드가 없음을 의미 가장 마지막 노드를 의미 if( q==NULL || p->is_thread == TRUE) return q; // 만약 오른쪽 자식이면 다시 가장 왼쪽 노드로 이동 // 이 경우 오른쪽 자식(q)을 루트노드로 하는 서브트리의 가장 왼쪽 자손 // 노드가 p의 후속자 노드가 됨 while( q->left != NULL ) q = q->left; }

스레드 이진 트리의 구현 스레드 버전 중위 순회 함수 작성 void thread_inorder(TreeNode *t) { TreeNode *q = t; while (q->left) q = q->left;// 가장 왼쪽 노드로 간다. do printf("%c ", q->data); // 데이터 출력 q = find_successor(q); // 후속자 함수 호출 } while(q); // NULL이 아니면 }

이진탐색트리 탐색작업을 효율적으로 하기 위한 자료구조 모든 노드 x에 대하여 아래 조건을 만족하는 이진트리를 이진탐색트리(binary search tree)라 한다. key(x->left)≤key(x)≤key(x->right) 이진탐색를 중위순회하면 오름차순으로 정렬된 값을 얻을 수 있다.

이진탐색트리에서의 탐색연산 주어진 키 값이 현재 노드의 키 값보다 작으면 탐색은 현재 노드의 왼쪽 자식을 기준으로 다시 시작한다. 주어진 키 값이 현재 노드의 키 값보다 크면 탐색은 현재 노드의 오른쪽 자식을 기준으로 다시 시작한다. 주어진 키 값이 현재 노드의 키 값과 같으면 탐색 성공

이진탐색트리에서의 탐색연산 search(x, k) if x = NULL then return NULL; if k = x->key then return x; else if k < x->key then return search(x->left, k); else return search(x->right, k);

탐색을 구현하는 방법 순환적 방법 반복적 방법

순환적인 방법 //순환적인 탐색 함수 TreeNode *search(TreeNode *node, int key) { if ( node == NULL ) return NULL; if ( key == node->key ) return node; else if ( key < node->key ) return search(node->left, key); else return search(node->right, key); }

반복적인 방법 // 반복적인 탐색 함수 TreeNode *search(TreeNode *node, int key) { while(node != NULL){ if( key == node->key ) return node; else if( key < node->key ) node = node->left; else node = node->right; } return NULL; // 탐색에 실패했을 경우 NULL 반환

이진탐색트리에서의 삽입연산 이진 탐색 트리에 원소를 삽입하기 위해서는 먼저 탐색을 수행하는 것이 필요 탐색에 실패한 위치가 바로 새로운 노드를 삽입하는 위치

이진탐색트리에서의 삽입연산 insert_node(T,z) p←NULL; t←root; while t≠NULL do p←t; if z->key < p->key then t←p->left; else t←p->right; if p = NULL then root←z; // 트리가 비어있음 else if z->key < p->key then p->left←z else p->right←z

이진탐색트리에서의 삽입연산 // key를 이진 탐색 트리에 삽입한다. // key가 이미 존재하면 삽입되지 않는다. void insert_node(TreeNode **root, int key) { TreeNode *p, *t; // p는 부모노드, t는 현재노드 TreeNode *n; // n은 새로운 노드 t = *root; p = NULL; // 탐색을 먼저 수행 while (t != NULL){ if( key == t->key ) return; p = t; if( key < t->key ) t = t->left; else t = t->right; }

이진탐색트리에서의 삽입연산 // key가 트리 안에 없으므로 삽입 가능 n = (TreeNode *) malloc(sizeof(TreeNode)); if( n == NULL ) return; // 데이터 복사 n->key = key; n->left = n->right = NULL; // 부모 노드와 링크 연결 if( p != NULL ) if( key < p->key ) p->left = n; else p->right = n; else *root = n; // empty tree인 경우 }

이진탐색트리에서의 삭제연산 3가지의 경우 1. 삭제하려는 노드가 단말 노드일 경우 2. 삭제하려는 노드가 하나의 자식 노드만을 가지고 있는 경우 3. 삭제하려는 노드가 두개의 자식 노드를 가지고 있는 경우 CASE 1: 삭제하려는 노드가 단말 노드일 경우: 단말 노드의 부모 노드를 찾아서 연결을 끊으면 된다.  

이진탐색트리에서의 삭제연산 CASE 2:삭제하려는 노드가 하나의 자식 노드만을 갖고 있는 경우 삭제되는 노드가 왼쪽이나 오른쪽 서브 트리중 하나만 갖고 있음. 그 노드는 삭제하고 서브 트리는 부모 노드 위치로 올려준다.

이진탐색트리에서의 삭제연산 CASE 3:삭제하려는 노드가 두개의 자식 노드를 갖고 있는 경우 삭제노드의 successor 노드는 왼쪽 자식이 없음 successor를 삭제노드 위치로 이동시키고 successor의 오른쪽 자식(만약 존재한다면)은 successor 위치로 이동. 30

삭제 연산 구현 코드 교재 참조

이진탐색트리의 성능분석 이진 탐색 트리에서의 탐색, 삽입, 삭제 연산의 시간 복잡도는 트리의 높이를 h라고 했을때 h에 비례한다. h값은 트리의 형태와 노드의 개수 n에 따라 달라짐 h = log2n

이진탐색트리의 성능분석 최선의 경우 최악의 경우 h = n-1 h = log2n 이진 트리가 균형적으로 생성되어 있는 경우 한쪽으로 치우친 경사이진트리의 경우 h = n-1 순차탐색과 시간복잡도가 같다. h = log2n h = n-1