690 likes | 1.2k Views
6. 인덱스 구조 (2-1). 이원 탐색 트리 /AVL 트리 /m- 원 탐색 트리 / B- 트리 /B*- 트리 / 트라이. 인덱스 (index). 특징 화일의 레코드들에 대한 효율적 접근을 위한 조직 < 키 값 , 레코드 주소 ( 포인터 )> 쌍으로 구성 종류 키 값의 유형에 따른 인덱스 기본 인덱스 (primary index) : 키 값이 기본 키인 인덱스 보조 인덱스 (secondary index) : 기본 인덱스 이외의 인덱스 화일 조직에 따른 인덱스
E N D
6. 인덱스 구조(2-1) 이원 탐색 트리/AVL 트리/m-원 탐색 트리/ B-트리/B*-트리/트라이
인덱스(index) • 특징 • 화일의 레코드들에 대한 효율적 접근을 위한 조직 • <키 값, 레코드 주소(포인터)>쌍으로 구성 • 종류 • 키 값의 유형에 따른 인덱스 • 기본 인덱스 (primary index) : 키 값이 기본 키인 인덱스 • 보조 인덱스 (secondary index) : 기본 인덱스 이외의 인덱스 • 화일 조직에 따른 인덱스 • 집중 인덱스 (clustered index) : 레코드들의 물리적 순서와 인덱스 엔트리 순서가 동일한 인덱스 • 비집중 인덱스(unclustered index) : 집중 형태가 아닌 인덱스 • 데이타 범위에 따른 인덱스 • 밀집 인덱스(dense index) : 데이타 레코드 하나에 하나의 인덱스 엔트리가 만들어지는 인덱스. 역 인덱스(inverted index) • 희소 인덱스(sparse index) : 데이타 레코드 그룹 또는 데이타 블록에 하나의 엔트리가 만들어지는 인덱스
이원 탐색 트리 (binary search tree) • 인덱스를 조직하는 한가지 방법 • 이진 트리(binary tree) • 유한한 수의 노드를 가진 트리 • 공백(empty)이거나 루트와 두 개의 분리된 이진 트리 즉, 왼쪽 서브트리(left subtree)와 오른쪽 서브트리(right subtree)로 구성 • 이원 탐색 트리 • 이진 트리 : left – root - right 순서로 탐색 • 각 노드Ni : 레코드 키 Ki와 레코드 포인터를 포함 • 공백이 아닌 이원 탐색 트리의 성질 • 모든 노드는 상이한 키 값들을 갖는다. • 루트 노드Ni의 왼쪽 서브트리(Left(Ni))에 있는 모든 노드의 키 값들은 루트 노드의 키 값보다 작다. • 루트 노드Ni의 오른쪽 서브트리(Right(Ni))에 있는 모든 노드의 키 값들은 루트 노드의 키 값보다 크다. • 왼쪽 서브트리와 오른쪽 서브트리 역시이원 탐색 트리이다.
28 15 30 12 32 11 42 70 14 30 5 37 10 44 (a) (b) (c) 이원 탐색 트리 • 이진 트리와 이원 탐색 트리 • 그림 (a): 이원 탐색 트리가 아닌 이진 트리 • 그림 (b), (c): 이원 탐색 트리
▶ 이원 탐색 트리에서의 검색 • 루트 노드가Ni인 이원 탐색 트리에서 키 값이 K인 노드를 검색하는 방법 • 트리가 공백 : 검색 실패 • K=Ki : 노드Ni가 원하는 노드 • K<Ki : Ni의 왼쪽 서브트리를 검색 Ni ← Root(Left(Ni))로 하여 다시 검색 시작 • K>Ki : Ni의 오른쪽 서브트리를 검색 Ni ← Root(Right(Ni))로하여 다시 검색 시작
▶ 이원 탐색 트리에서의 검색 연결 리스트로 표현된 이원 탐색 트리의 검색 • 노드는key, left, right 필드로 구성 left key right searchBST(B, s_key) // B는 이원 탐색 트리, s_key는 검색 키 값 p ← B; if (p = null) then // 공백 이진 트리로 검색 실패 return null; if (p.key = s_key) then // s_key 검색 성공 return p; if (p.key < s_key) then // 오른쪽 서브트리 검색 returnsearchBST(p.right, s_key); elsereturnsearchBST(p.left, s_key); // 왼쪽 서브트리 검색 end searchBST()
15 15 15 11 70 11 70 11 70 5 13 5 13 50 5 (a) (b) 13 (c) 50 이원 탐색 트리 키값 을 삽입 키값 을 삽입 ▶ 이원 탐색 트리에서의 삽입 • 루트 노드가Ni인 이원 탐색 트리에 키 값이 K인 노드삽입 • 트리가 공백 : K를 루트 노드로 삽입 • K=Ki : 트리에 똑같은 키 값이 이미 존재(삽입 거부) • K<Ki : Ni의 왼쪽 서브트리로 이동하여 계속 탐색 • K>Ki: Ni의 오른쪽 서브트리로 이동하여 계속 탐색 • K가 있어야 할 위치(탐색이 실패로 끝나는 위치)에 키 값이 K인 노드를 삽입 • 삽입 예 : 키 값 13, 50 을 삽입
▶ 이원 탐색 트리에서의 삽입 알고리즘 insertBST(B, new_key) // B는 이원 탐색 트리, new_key는 삽입할 키 값 p ← B; while (p ≠ null) do { // 삽입하려는 키 값을 가진 노드가 이미 있는지 검사 if (new_key = p.key) then return; // 이미 삽입 키값이 존재하므로 실패 q ← p; // q는 p의 부모 노드를 지시 if (new_key < p.key) then p ← p.left; else p ← p.right; } newNode ← getNode(); // 삽입할 노드를 만듦 newNode.key ← new_key; // 새로 만든노드의 키 값을 new_key로 세팅 newNode.right ← null; // 아직우측 하위 노드가 없으므로 null newNode.left ← null; // 아직 좌측하위 노드가 없으므로 null if (B = null) then B ← newNode; // 트리가 공백인 경우 elseif (new_key < q.key) then // q는 탐색이 실패로 종료하게 된 원소 q.left ← newNode; elseq.right ← newNode; return; end insertBST()
▶ 이원 탐색 트리에서의 삭제 • 노드 삭제 후 계속 이원 탐색 트리 성질 유지 • 삭제할 노드 자식 수에 따라 삭제 연산이 다름 1. 자식이 없는 리프노드의 삭제 • 단순히 그 노드를 삭제 2. 자식이 하나인 노드의 삭제 • 삭제되는 노드 자리에 그 자식 노드를 위치 3. 자식이 둘인 노드의 삭제 • 삭제되는 노드 자리에 왼쪽 서브트리에서 제일 큰 키 값이나 또는 오른쪽 서브트리에서 제일 작은 키 값으로 대체 • 해당 서브트리에서 대체 노드를 삭제 • 삭제는 포인터 값 조정으로 구조 변경 • 삭제 표시(논리적 삭제)로 대체 가능 • 물리적으로 즉각 삭제하지 않는 경우 • 검색, 삽입 시 삭제된 노드의 키 값을 참고로 이용 • 정상적 키 값이 아니므로 별도로 취급
50 50 50 50 30 55 30 55 30 55 30 55 20 52 60 52 60 25 20 40 52 60 20 52 60 25 25 25 (a) (b) 삭제 전 삭제 후 (a) (b) 삭제 전 삭제 후 이원 탐색 트리에서 자식이 하나인 노드(20)의 삭제 이원 탐색 트리에서리프노드(40)의 삭제 50 30 52 25 55 30 55 30 55 52 60 25 60 25 52 60 (a) (b) (c) 삭제 전 왼쪽 서브트리의 오른쪽 서브트리의 최대 키 값(30)으로 대체 최소 키 값(52)으로 대체 ▶ 이원 탐색 트리에서의 삭제 • 삭제 예 : 키 값 40, 20, 50 삭제 또는 이원 탐색 트리에서 자식이 둘인 노드(50)의 삭제 10
▶ 이원 탐색 트리에서의 삭제 알고리즘 deleteBST(B, d_key) p ← node to be deleted; // 삭제할 키 d_key를 가진 노드 parent ← parent node of p; // 삭제할 노드의 부모 노드 if (p = null) then return; // 삭제할 원소가 없음 case { p.left=null andp.right=null : // 삭제할 노드가리프노드인 경우 if (parent.left = p) thenparent.left ← null; elseparent.right ← null; p.left=null orp.right = null : // 삭제할 노드의 차수가 1인 경우 if (p.left ≠ null) then { if (parent.left = p) thenparent.left ← p.left; elseparent.right ← p.left; } else { if (parent.left = p) thenparent.left ← p.right; elseparent.right ← p.right; } p.left ≠ null andp.right ≠ null : // 삭제할 노드의 차수가 2인 경우 q←maxNode(p.left); // 왼쪽 서브트리에서 최대 키 값을 탐색 p.key ← q.key; deleteBST(p.left, p.key); } end deleteBST()
60 25 55 30 52 52 30 55 25 60 ▶ 편향 이원 탐색 트리 • 편향 이원 탐색 트리(skewed binary search tree) • 리프노드의탐색 시간이 최악 • N개 노드 이원 탐색 트리 최악의 탐색 시간 : N번 탐색
▶ 이원 탐색 트리의 성능 • 성능 • 이원 탐색 트리의 성능은 트리의 형태와 노드에 대한 접근 빈도에 의존 • 우수한 성능을 위해서는 • 자주 접근되는 노드는 루트에 가장 가깝게 유지 • 이원 탐색 트리를균형 트리(balanced tree)로 유지 • 모든 노드에 대해 양쪽 서브트리의노드 수가 가능한 같게 만들어 트리의최대 경로 길이를 최소화 • 이원 탐색 트리의 단점 • 삽입, 삭제 후 효율적 접근을 위한 균형 유지 부담 • 작은 분기율(branching factor : 2)에 따른 긴 탐색 경로와 긴 검색 시간 • 분기율이2라면각 노드는 많아야 두 개의 서브트리를 가짐 • N개의 노드를 갖는 트리의 최소 높이 :
AVL 트리 • 1962년 러시아 수학자 Adelson-Velskii와 Landis가 고안한 높이 균형 이진 트리 (height-balanced binary tree) • 이진 트리의 높이: 루트에서 가장 긴 경로를 가진 리프까지의 길이 • AVL 높이 균형 이진 트리 • 삽입, 삭제, 검색 시간 : O(logN) 차수(m, Order)에 비례 • 트리 일부를 재균형으로전체가 균형을 계속 유지(차수 최소화) • AVL 트리의 정의 • 왼쪽 서브트리의 높이와 오른쪽 서브트리의 높이 차이가 1 이하 • ½h(Left(Ni)) – h(Right(Ni))½ ≤ 1, Ni∈ T 서브트리 높이 차이에 대한 절대값이 1이하 • 공백 서브트리의 높이 : -1
▶ AVL 트리 • 균형 인수(balance factor, BF) • 왼쪽 서브트리 높이에서 오른쪽 서브트리 높이를 뺀 수BF = h(Left(T)) – h(Right(T)) • 균형 인수가 • ±1 이하이면 AVL 성질(AVL property) 만족 • ±2 이상이면 AVL 성질 상실 • AVL 트리의 모든 노드는 균형 인수가 ±1 이하임.
8 8 8 8 8 5 10 12 9 12 4 4 4 3 6 6 10 9 10 16 16 7 10 16 2 3 6 6 3 2 6 4 4 7 15 14 18 17 11 5 5 9 6 12 13 ▶ AVL 트리와 non-AVL 트리 (a) (b) (c) AVL 트리 (a) (b) (c) non-AVL 트리
▶ AVL 트리에서의 검색과 삽입(1) • 검색 • 이원 탐색 트리의 검색 방법과 동일 • 시간 복잡도 : O(logN) • 삽입 • 삽입되는 노드에서부터 루트까지의 경로에 있는 조상 노드들의 균형인수(bf)에 영향을 줄 수 있음. • 노드 삽입으로 인해 non-AVL 트리가 되면, 삽입된 노드와 가장 가까우면서 불균형이 된 조상 노드의균형 인수가 ±1 이하로 트리 구조를 조정
1 2 12 12 1 1 1 2 16 16 8 8 0 0 0 0 0 1 14 14 4 10 4 10 1 0 0 0 2 2 6 6 0 1 (b) 원소 1의 삽입으로 non-AVL 트리로 바뀜 (a) AVL 트리 원소 1의 삽입으로 노드 8의 AVL 성질 상실 ▶ AVL 트리에서의 검색과 삽입(2) • 노드 삽입으로 균형 인수가 ± 2로 된 노드X가 출현 • 다음 4가지 경우 중 하나로 인해 발생(노드x는 균형 인수가 ±2) LL : x의 왼쪽 자식(L)의 왼쪽 서브트리 (L) 에 삽입 RR : x의 오른쪽 자식(R)의 오른쪽 서브트리 (R) 에 삽입 LR : x의 왼쪽 자식 (L) 의 오른쪽 서브트리 (R) 에 삽입 RL : x의 오른쪽 자식(R)의 왼쪽 서브트리 (L) 에 삽입
2 0 A B LL 1 0 ⇒ B A T T 3 1 T T T T 1 2 2 3 ▶ AVL 트리에서의 검색과 삽입(3) • 회전(rotation)불균형이 발생할 때 트리 구조를 변경하여 균형을 잡아주는 것 • 단순 회전(single rotation) • 한 번의 회전만 필요한 균형: LL, RR • 탐색 순서를 유지 하면서 부모와 자식 원소의 위치를 교환 • 이중 회전(double rotation) • 두 번의 회전이 필요한 균형: LR, RL i) LL 회전
- 2 0 A B - 1 0 RR B A ⇒ T T 1 3 T T T T 2 3 1 2 2 0 A C - 1 LR(a) 0 0 B B A ⇒ C ▶ AVL 트리에서의 검색과 삽입(4) ii) RR 회전 0 0 iii) LR(a) 회전
2 0 A C - 1 0 - 1 LR(b) B B A ⇒ 1 C T 4 T T T T T 4 1 1 2 3 T T 2 3 2 0 A C - 1 1 0 B B A LR(c) - 1 ⇒ C T 4 T T T T T 4 1 1 2 3 T T 2 3 v) LR(c) 회전 ▶ AVL 트리에서의 검색과 삽입(5) iv) LR(b) 회전
- 2 0 A C RL(a) - 1 0 0 ⇒ B A B 0 C - 2 0 A C 1 1 0 RL(b) B A B ⇒ - 1 C T 1 T T T T T 4 1 2 3 4 T T 2 3 ▶ AVL 트리에서의 검색과 삽입(6) vi) RL(a) 회전 vii) RL(b) 회전
T T 2 3 T T 2 3 ▶ AVL 트리에서의 검색과 삽입(7) - 2 0 A C 1 0 - 1 RL(c) B A B ⇒ 1 C T 1 T T T 4 1 4 viii) RL(c) 회전 AVL 트리 회전
-1 -2 0 0 8 8 9 RR 8 -1 0 0 0 9 9 8 10 0 10 1 2 1 9 9 9 1 0 2 0 0 0 LL 8 10 8 10 2 10 0 1 0 0 2 2 8 1 0 1 ▶ AVL 트리에서의 검색과 삽입(8) • 키 리스트 (8, 9, 10, 2, 1, 5, 3, 6, 4, 7, 11, 12)를 차례대로 삽입하면서 AVL 트리를 구축하는 예 (a) 키 8 삽입 (b) 키 9 삽입 (c) 키 10 삽입 (d) 키 2 삽입 (e) 키 1 삽입
▶노드 삽입에 따른 균형 인수(BF)조정 • 노드 삽입으로 인한 균형 인수(BF)의 변화 • 새로 노드를 삽입할 때 BF에 영향을 받는 노드는 오직 이 새로운 노드에서부터 루트까지의 경로상에 있는 노드들이다. • 새로운 노드가왼쪽 서브트리로 삽입되면 그 부모 노드의 BF를 하나 증가(+1)시키고,오른쪽 서브트리로 삽입되면 BF를 하나 감소(-1)시킨다. • BF가 0이 되면 BF 조정은 종료되고 아니면 다시 그의 부모 노드의BF를 변경한다. 즉자기가 부모 노드의 왼쪽 서브트리이면BF를 하나 증가(+1)시키고, 오른쪽 서브트리이면BF를 하나 감소(-1)시키면서 루트까지 경로를 따라가면서 BF 조정 작업을 계속한다.
2 0 1 9 8 -1 0 0 -1 -1 -1 LR 2 10 2 9 0 0 0 0 0 1 0 1 1 1 8 5 5 10 10 0 0 5 3 (f) 키 5 삽입 1 2 1 8 8 8 -2 -1 0 -1 -1 -1 2 9 3 9 RL 2 9 0 1 1 0 0 0 0 0 0 1 2 5 5 10 10 0 1 0 5 -1 0 10 0 0 1 0 3 4 6 6 0 3 6 4 ▶ AVL 트리에서의 검색과 삽입(9) 8 2 9 1 (g) 키 3 삽입 (h) 키 6 삽입 (i) 키 4 삽입
2 0 8 5 -1 0 -1 1 9 8 3 3 1 -1 1 -1 0 0 -1 LR 2 5 2 4 6 10 9 0 0 0 -1 0 0 1 1 4 10 6 7 0 7 -1 0 5 5 -1 0 1 1 8 8 3 3 1 1 -1 -1 0 0 -2 0 RR 2 4 2 4 6 6 9 10 0 0 -1 0 0 0 0 1 1 10 11 7 7 9 0 11 ▶ AVL 트리에서의 검색과 삽입(10) (j) 원소 7 삽입 (k) 원소 11 삽입
▶ AVL 트리에서의 검색과 삽입(11) -1 5 -1 1 8 3 -1 0 1 -1 2 4 6 10 0 0 -1 0 1 11 7 9 0 12 (l) 키 12 삽입
▶ AVL 트리에서의 삽입 알고리즘(1) /* AVL 트리 삽입 알고리즘 */ insertAVL(new_key) if (root = new_key) then { //공백 트리인 경우 y ← getTreeNode(); y.key ← newKey; root ←y; root.bf ← 0; //bf는 균형 인수 root.left ← null; root.right ← null; return true; } f ← null; a ← p ← root; q ← null; found ← false; // phase 1: new_key의 삽입 위치(q) 조사 while (p ≠ null and found = false) do if (p.bf ≠ 0) then {a ← p; f ← q;} if (new_key < p.key) then {q ← p; p ← p.left;} //왼쪽 서브트리로 이동 else if (new_key > p.key) then {q ← p; p ← p.right;} //오른쪽 서브트리로 이동 else {y ← p; found ← true;} } // while // phase 2: new_key를 삽입하고 균형화
▶ AVL 트리에서의 삽입 알고리즘(2) // phase 2: new_key를 삽입하고 균형화 if (found = false) then { // new_key는 트리에 없음. // new_key를 q의 적절한 자식으로 삽입 y ← getTreeNode(); y.key ← newKey; y.left ← null; y.right ← null; y.bf ← 0; if (new_key < q.key) thenq.left ← y; //q의 왼쪽 자식으로 삽입 elseq.right ← y; //q의 오른쪽 자식으로 삽입 // a에서 q까지의 경로에 있는 노드의 균형 인수를 조정. // a의 정의에 따라 이 경로상에 있는 모든 노드들은 그 // 균형 인수가 0이어야 되기 때문에 이들의 균형 인수는 // ±1로 변경된다. 따라서 // d=1은 new_key가 a의 왼쪽 서브트리로 삽입되었다는 // 것을 의미하고 // d=-1은 a의 오른쪽 서브트리로 삽입되었다는 것을 //의미한다.
▶ AVL 트리에서의 삽입 알고리즘(3) if (new_key > a.key) then {p ←a.right; b ← p; d ← -1;} else {p ←a.left; b ← p; d ← 1;} while (p ≠ y) do { if (new_key > p.key) then {p.bf← -1; p ← p.right;} //오른쪽 서브트리의 높이가 1 증가 else {p.bf← 1; p ← p.left;} //왼쪽 서브트리의 높이가 1 증가 } //while (p ≠ y) unbalanced ← true; // 트리가 불균형인지를 검사 if (a.bf = 0 ora.bf+d = 0) then // 트리가 아직 균형 {a.bf ← a.bf+d; unbalanced ← false; } if (unbalanced = true) then // 트리가 불균형. 회전 유형을 결정 if (d = 1) then { //왼쪽 불균형 if (b.bf = 1) then { // LL 회전 타입 a.left ← b.right; b.right ←a; a.bf ← 0; b.bf ← 0;} } else { // LR 회전 타입 c ←b.right; b.right ← c.left; a.left ← c.right; c.left ←b; c.right ← a; switch (c.bf) { case 1 : a.bf ← -1; b.bf ← 0; break; //LR(b) case -1 : b.bf ← 1; a.bf ← 0; break; //LR(c) case 0 : b.bf ← 0; a.bf ← 0; break; //LR(a) } c.bf ← 0; b ← c; // b는 새로운 루트 } // else LR 회전 타입
▶ AVL 트리에서의 삽입 알고리즘(4) if (unbalanced = true) then // 트리가 불균형. 회전 유형을 결정 if (d = 1) then { //왼쪽 불균형 if (b.bf = 1) then { // LL 회전 타입 ************ } else { // LR 회전 타입 ************ } // else LR 회전 타입 } // 왼쪽 불균형 else { // 오른쪽 불균형. 왼쪽 불균형의 대칭 코드 ……………..// RR 회전 타입 ……………..// RL 회전 타입 } if (f = null) then root ← b; // b를 루트로하는서브트리가 //균형을 맞추고 새로운 서브트리가 됨 else if (a = f.left) thenf.right ← b; } // if(unbalanced = true) return true; } // if (found = false return false; end insertAVL()
▶ AVL 트리의 높이 • 높이 균형 이진 트리의 높이 • N개의 노드를 가진 높이 균형 이진 트리는 완전 균형 이진 트리보다45% 이상 높아지지 않음 • log(N+1) ≤ h ≤ 1.4404log(N+2)-0.328 • 높이 균형 이진 트리 완전 균형 이진 트리 • O(1.4 log N) 대 O(log N) • 높이 균형 이진 트리의 탐색 시간이 더 길다 • 완전 균형 트리보다 전체 재균형 작업 발생횟수가 줄어듬 • 수 백만 개로 구성된 AVL트리의노드 탐색은 많은 횟수의 디스크 접근을 요구m-원 탐색 트리 고려
m-원 탐색 트리(m-way search tree) • 이원 탐색 트리보다 높은 분기율: m개 서브트리 • 장점 : 트리의 높이가 감소(특정 노드의 탐색 시간 감소) • 단점 : 삽입, 삭제 시 트리의 균형 유지를 위한 연산이 복잡 • m-원 탐색 트리의 성질 ① 노드 구조는 <n, P0, K1, P1, K2, P2, … , Pn-1, Kn, Pn> : n은노드 내부 표시 [n: 키 값 수(1≤n<m), Pi: 서브트리에 대한 포인터, Ki: 키 값] ② 한 노드에 있는 키 값들은 오름차순: Ki < Ki+1 (1 ≤ i ≤ n-1) ③ Pi (0 ≤ i ≤ n-1)가 지시하는 서브트리 모든 노드들의 키 값 < Ki+1 ④ Pn이 지시하는 서브트리의 모든 노드들의키값> Kn ⑤ Pi가 지시하는 서브트리는 m-원 서브트리
a 100 140 b c d 30 60 110 120 170 200 e f g h i j 180 ^ ^ 40 50 70 90 150 160 220 ▶ 3-원 탐색 트리의 예 ^ ^ 10 20
m-원 탐색 트리 • M-원탐색 트리에서 밀집인덱스 구조 • 서브트리만 가리키는 포인트 외에도, 파일에 있는 모든 레코드 각각에 대한 별도 포인터를 가짐. • 노드구조: <n, P0, <K1, A1>, P1, <K2, A2>, … , Pn-1, <Kn, An>, Pn> • M-원 탐색트리키 값 Ki: (Ki, Ai)쌍을 의미 Ai는 키 값 Ki를 포함하고 있는 데이타 레코드의 주소 • 별도 파일 구조를 고려 B-트리 등장 학번(Ki) 성명 주민번호 학년 지도교수 … 레코드
▶ m-원 탐색 트리의 검색 searchMT(key) // m-원 탐색 트리의 검색 알고리즘 // key : 검색 키 값 // x : 노드(<n, P0, K1, P1, K2, P2, … , Pn-1, Kn, Pn>) // root : 루트 노드 // n : 노드에 있는 키의 개수 x ← root; while( x != null )do { i ← 1; n ← x.n; while( i <= n && key > x.Ki ) i ← i+1; if ( i <= x.n && key = x.Ki) thenreturn Ai; // 키 값 key를 가진 레코드의 주소를 반환 if (i > n) then x ← x.Pn else x ← x.Pi-1 } //while return null; // key와 일치하는 값이 트리에 없는 경우 endsearchMT()
▶ m-원 탐색 트리의 분석 • m-원 탐색 트리의 탐색시간: 탐색 경로 길이(높이)에 비례 • 각 레벨에서는 한 개의 노드만 탐색 • 분기율(m)을 최대로 하면 트리의 높이가 낮아짐 • 한 노드에m-1개 키 값을 저장하는 m-원탐색트리 • 높이 h : n = (mh-1)개 키 값 저장 • (예) 4-원 탐색 트리: 높이가 3이면 n=(43-1)=63개의 키 값을 저장 • n개의 키를 가진 m-원 탐색트리 • 최소 높이 h = logm(N+1) • 최대 탐색 시간 : O(logm(N+1)) (예) m=2이면, 이진 트리 탐색시간과 동일
B-트리 • 1972년 Bayer & McCreight가 제안 • 균형 m-원 탐색 트리 • 가장 많이 사용되는 인덱스 방법 • 효율적인 균형 알고리즘을 제공 • 차수(order)가m인 B-tree의 특성 ① B-트리는 공백이거나 높이가 1 이상인 m-원 탐색 트리 ② 루트와 리프를 제외한 내부 노드 - 최소 m/2 에서최대 m개의 서브트리를 가짐 - 적어도 m/2 - 1개의 키 값(노드의 반 이상이 채워짐) ③ 루트 : 리프가 아니면 적어도 두 개의 서브트리를 가짐 ④ 모든 리프는 같은 레벨
▶ m차 B-트리 노드 구조 • 노드 구조 < n, p0, <K1, A1>, P1, <K2, A2>, P2, … , Pn-1, <Kn, An>, Pn> ① n : 키 값의 수(1≤n<m) P0, … , Pn : 서브트리에 대한 포인터 각 키 값 Ki는 그 키 값을 가진 레코드에 대한 포인터Ai를 포함 ② 각 노드의 키 값들은 항상 오름차순 (1≤i ≤n-1 Ki < Ki+1)유지 ③ Pi가 지시하는 서브트리의 키 값들은 모두 Ki+1 보다 작다. ④ Pn이 지시하는 서브트리의 키 값들은 모두 Kn보다 크다. ⑤ Pi(0≤i≤n)가 지시하는 서브트리들은 모두 m-원 서브 탐색 트리이다. • B-트리의 장점 • 삽입, 삭제 뒤에도 트리의 균형 상태를 유지 • 저장장치의 효율성 • 각 노드의 반 이상은 항상 키 값으로 채워짐
a ^ 69 b c 19 43 128 138 d e f g h i ^ ^ ^ ^ ^ 16 26 40 60 100 132 145 p j k l m n o q r s t u v 7 15 18 20 30 36 42 50 58 62 65 70 110 120 130 136 140 150 ▶ 3차 B-트리 구조
▶ B-트리에서의 연산 • 검색 : m-원 탐색 트리의 직접 검색과 같은 과정 • 직접 탐색은키 값에 따라 서브트리로 분기 • 노드에서 검색은 순차 검색 • B-트리 전체의 순차 검색은 트리의 중위 순회(inorder traversal)로 수행(left-root-right) • 삽입 : 새로운 키 값은 항상 리프노드에 삽입 • 노드에 공간이 있는 경우 : 검색순서에 맞게 단순 삽입 • 노드에 공간이 없는 경우 : overflow로 split 발생 • 해당 노드를 두 개의 노드로 분할 • 해당 노드에 새로운 키 값 삽입했다고 가정 • 중간( m/2번째)키 값을 기준으로 왼쪽 작은 키 값들은 그대로 두고 오른쪽 큰 키 값들은 새로운 노드에 저장 • 중간 키 값은 분할된 두 노드가 왼쪽 서브트리, 오른쪽 서브트리가 되도록 부모 노드에 삽입 • 이 때, 다시 overflow가 발생하면 위와 같은 분할(split) 작업을 반복
1 o n 58 22 42 50 41 20 b b 1 n 20 42 f f · · · · · 60 60 58 ^ p p o o’ 50 59 ▶ B-트리에서의 삽입 • 앞의 B-트리에 새로운 키 값 22, 41, 59, 57, 54, 44, 75, 124, 122, 123 삽입 (a) 노드 l에 키 22의 삽입 (b) 리프 노드 n에 키 42의 삽입 (c) 노드 o에 키 59의 삽입
o o 57 57 50 50 f f f’ · · · · · · · 60 58 54 60 ^ ^ o o’’ o o o’’ o’ p p o o’ 50 57 50 ▶ B-트리에서의 삽입 (d) 노드 o에 키 57의 삽입 (d) 키 54의 삽입으로 노드 o의 분할 (54는 부모 노드 f로 이동) (e) 노드f에 키 54의 삽입 (58은 부모 노드 b에 삽입)
a a · · · · · 69 69 43 ^ c b b b’ c b b b’ · · · · · · · 43 19 19 58 ^ ^ d e f f’ f d e ▶ B-트리에서의 삽입 (g) 노드 b에 키 58의 삽입 (43은 부모 노드 a에 삽입) (h) 노드 a에 키 43의 삽입 나머지 키 값인 33, 75, 124, 122를 차례로 삽입 : 문제가 발생하지 않음 마지막 키 값인 123을 삽입 : B-트리는 한 레벨 증가됨
o a 69 · · a a’ 128 43 · · · · b b’ c c’ 120 138 33 19 58 · · · · · · · · · d e e’ f f’ g g’ h i 100 123 132 145 16 26 40 54 60 · · · · · · · · · · · · · · · · · · j k l m m’ n o o’’ o’ p q r r’ r’’ s t u v 124 130 136 140 150 18 30 36 50 57 59 110 122 7 15 20 22 41 42 62 65 70 75 ▶ 한 레벨 증가된 B-트리
· · 69 · · 69 19 138 43 43 138 ▶ 3차 B-트리 생성 과정 • 키 값 43, 69, 138, 19 순으로 삽입하여 생성 43 (a) 크기가 2인 공백 루트 노드 (b) 키 값 43의 삽입(노드 1개의 3차 B-트리) 69 43 (c) 키 값 69의 삽입(노드 1개의 3차 B-트리) (d) 키 값 138의 삽입(노드3개의 3차 B-트리) (e) 키 값 19의 삽입(노드 3개의 3차 B-트리)
▶ B-트리에서의 삽입 알고리즘 B-트리 삽입 알고리즘 // In-key : 삽입할 키 // Finished : 삽입 완료를 나타내는 플래그 // Found : 레코드가 발견되었음을 나타내는 플래그 // P : 노드에 대한 포인터 // Bignode : 오버플로노드를 위한 변수 // N : 키 카운터 // 노드의 주소를 스택에 저장하면서 In-key가 삽입될 위치를 탐색 Found = false; read root; do { N = number of keys in current node; if (n-key == key in current node) found = true; else if (In-key < key1) P = Po; else if (In-key > keyN) P = PN; else P = Pi-1; /* for some i where keyi-1 < In-key < keyi */ if (P != null) { push onto stack address of current node; read node pointed to by P; } } while (!Found && P is not null);
B-트리 if (Found) report In-key already in tree; else { // In-key를 B-트리에 삽입 P = null; Finished = false; do { if (current node is not full) { put In-key in current node; // 노드 안에서 키 순서를 유지하도록 키를 정렬 Finished = true; } else { copy current node to Bignode; insert In-key and P into Bignode; In-key = center key of Bignode; current node = 1st half of Bignode; get space for new node, assign address to P; new node = 2nd half of Bignode; if (stack not empty) { pop top of stack; read node pointed to; } else { // 트리의 레벨이 하나 증가 get space for new node; new node = pointer to old root, In-key and P; Finished = true; } } } while (!Finished); }
▶ B-트리에서의 삭제 • 삭제할 키 값이 리프노드에 있는 경우에는 그대로 삭제 • 삭제될 키 값이 내부 노드에 있는 경우 • 이 키 값의 후행 키 값과 교환 후 리프노드에서 삭제 • B-트리 특성상 이 후행 키 값은 항상 리프노드에 있음 • 리프노드에서의 삭제 연산이 더 간단 • 후행 키 값 대신 선행 키 값을 사용할 수 있음 • 삭제 결과로 노드의 키 값 수가 B-트리의 최소 키 값 수(m/2 - 1)보다 작게 되면 underflow가 일어나 재분배나 합병을 수행