IT CookBook, 자바로 배우는 쉬운 자료구조 9 트리 IT CookBook, 자바로 배우는 쉬운 자료구조
이 장에서 다룰 내용 트리 1 이진 트리 2 이진 트리의 구현 3 이진 트리의 순회 4 이진 탐색 트리 5 힙 6
하나의 줄기에서 가지로 뻗어나가면서 확장되는 구조 하나의 그루터기에서 뿌리로 뻗어나가면서 확장되는 구조 트리 트리(tree) 원소들 간에 1:多 관계를 가지는 비선형 자료구조 원소들 간에 계층관계를 가지는 계층형 자료구조 상위 원소에서 하위 원소로 내려가면서 확장되는 트리(나무)모양의 구조 하나의 줄기에서 가지로 뻗어나가면서 확장되는 구조 하나의 그루터기에서 뿌리로 뻗어나가면서 확장되는 구조
트리 트리 자료구조의 예 – 가계도 가계도의 자료 : 가족 구성원 자료를 연결하는 선 : 부모-자식 관계 표현
트리 철수의 자식 – 성호, 영주, 진호 성호, 영주, 진호의 부모 – 철수 같은 부모의 자식들끼리는 형제관계 성호, 영주, 진호는 형제관계 조상 – 현재 위치에서 연결된 선을 따라 올라가면서 만나는 사람들 수영의 조상 : 승완, 성호, 철수 자손 - 현재 위치에서 연결된 선을 따라 내려가면서 만나는 사람들 성호의 자손 : 승우, 승완, 수영, 수철 선을 따라 내려가면서 다음 세대로 확장 가족 구성원 누구든지 자기의 가족을 데리고 분가하여 독립된 가계를 이 룰 수 있다.
트리 트리 A
트리 노드 – 트리의 원소 루트 노드 – 트리의 시작 노드 간선 – 노드를 연결하는 선. 부모 노드와 자식 노드를 연결 트리 A의 노드 - A,B,C,D,E,F,G,H,I,J,K,L 루트 노드 – 트리의 시작 노드 트리 A의 루트노드 - A 간선 – 노드를 연결하는 선. 부모 노드와 자식 노드를 연결 형제 노드 – 같은 부모 노드의 자식 노드들 B,C,D는 형제 노드 조상 노드 – 간선을 따라 루트 노드까지 이르는 경로에 있는 모든 노드들 K의 조상 노드 : F, B, A 서브 트리 – 부노 노드와 연결된 간선을 끊었을 때 생성되는 트리 각 노드는 자식 노드의 개수 만큼 서브 트리를 가진다. 자손 노드 – 서브 트리에 있는 하위 레벨의 노드들 B의 자손 노드 – E,F,K,L
트리 차수 높이 노드의 차수 : 노드에 연결된 자식 노드의 수. 트리의 차수 : 트리에 있는 노드의 차수 중에서 가장 큰 값 A의 차수=3, B의 차수=2, C의 차수=1 트리의 차수 : 트리에 있는 노드의 차수 중에서 가장 큰 값 트리 A의 차수=3 단말 노드(리프 노드) : 차수가 0인 노드. 자식 노드가 없는 노드 높이 노드의 높이 : 루트에서 노드에 이르는 간선의 수. 노드의 레벨 B의 높이=1, F의 높이=2 트리의 높이 : 트리에 있는 노드의 높이 중에서 가장 큰 값. 최대 레벨 트리 A의 높이=3
트리 포리스트 : 서브트리의 집합 트리A에서 노드 A를 제거하면, A의 자식 노드 B, C, D에 대한 서브 트리가 생기고, 이들의 집합은 포리스트가 된다. A 루트 트리1 트리2 트리3 B C D E F G H I J K L
이진트리 이진트리 트리의 노드 구조를 일정하게 정의하여 트리의 구현과 연산이 쉽도록 정의한 트리 트리의 노드 구조를 일정하게 정의하여 트리의 구현과 연산이 쉽도록 정의한 트리 이진 트리의 모든 노드는 왼쪽 자식 노드와 오른쪽 자식 노드 만을 가진다. 부모 노드와 자식 노드 수와의 관계 ☞ 1:2 공백 노드도 자식 노드로 취급한다. 0 ≤ 노드의 차수 ≤ 2
이진트리 이진트리는 순환적 구성 노드의 왼쪽 자식 노드를 루트로 하는 왼쪽 서브트리도 이진 트리 노드의 오른쪽 자식 노드를 루트로 하는 오른쪽 서브 트리도 이진 트리 루트 A A A의 왼쪽 서브트리 A의 오른쪽 서브트리 루트 루트 B C B의 왼쪽 서브트리 B의 오른쪽 서브트리 C의 왼쪽 서브트리 C의 오른쪽 서브트리 D E F G H I J K L
이진트리 추상 자료형 이진트리 ADT BinaryTree 데이터 : 공백이거나 루트 노드, 왼쪽 서브 트리, 오른쪽 서브 트리로 구성된 노드들의 유한 집합 연산 : bt, bt1, bt2∈BinaryTree; item∈Element; createBT() ∷= create an empty binary tree; // 공백 이진 트리를 생성하는 연산 isEmpty(bt) ∷= if (bt is empty) then return true else return false; // 이진 트리가 공백인지 아닌지를 확인하는 연산 makeBT(bt1, item, bt2) ∷= return {item을 루트로 하고 bt1을 왼쪽 서브 트리, bt2를 오른쪽 서브 트리로 하는 이진 트리} // 두개의 이진 서브 트리를 연결하여 하나의 이진 트리를 만드는 연산 leftSubtree(bt) ∷= if (isEmpty(bt)) then return null else return left subtree of bt; // 이진 트리의 왼쪽 서브 트리를 구하는 연산 rightSubtree(bt) ∷= if (isEmpty(bt)) then return null else return right subtree of bt; // 이진 트리의 오른쪽 서브 트리를 구하는 연산 data(bt) ∷= if (isEmpty(bt)) then return null else return the item in the root node of bt; // 이진 트리에서 루트 노드의 데이터(item)를 구하는 연산 End BinaryTree [ADT 9-1]
이진트리 이진트리의 특성 정의1) n개의 노드를 가진 이진 트리는 항상 (n-1)개의 간선을 가진다. 정의2) 높이가 h인 이진 트리가 가질 수 있는 노드의 최소 개수는 (h+1)개 가 되며, 최대 개수는 (2h+1-1)개가 된다. 이진 트리의 높이가 h가 되려면 한 레벨에 최소한 한 개의 노드가 있어야 하므로 높이가 h인 이진 트리의 최소 노드의 개수는 (h+1)개 하나의 노드는 최대 2개의 자식노드를 가질 수 있으므로 레벨 i에서의 노드의 최대 개수는 2i개 이므로 높이가 h인 이진 트리 전체의 노드 개수는 ∑2i = 2h+1-1 개
이진트리 높이가 3이면서 최소의 노드를 갖는 이진트리와 최대의 노드를 갖는 이진 트리
이진트리 이진 트리의 종류 포화 이진 트리 모든 레벨에 노드가 포화상태로 차 있는 이진 트리 높이가 h일 때, 최대의 노드 개수인 (2h+1-1)개의 노드를 가진 이진 트리 루트를 1번으로 하여 2h+1-1까지 정해진 위치에 대한 노드 번호를 가진다.
이진트리 완전 이진 트리 높이가 h이고 노드 수가 n개일 때 (단, h+1 ≤ n < 2h+1-1 ), A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 13 14 15
이진트리 편향 이진 트리 높이 h에 대한 최소 개수의 노드를 가지면서 한쪽 방향의 자식 노드만을 가진 이진 트리 왼쪽 편향 이진 트리 모든 노드가 왼쪽 자식 노드만을 가진 편향 이진 트리 오른쪽 편향 이진 트리 모든 노드가 오른쪽 자식 노드만을 가진 편향 이진 트리
이진트리의 구현 순차 자료구조를 이용한 이진트리의 구현 1차원 배열의 순차 자료구조 사용 높이가 h인 포화 이진 트리의 노드번호를 배열의 인덱스로 사용 인덱스 0번 : 실제로 사용하지 않고 비워둔다. 인덱스 1번 : 루트 저장
이진트리의 구현 완전 이진 트리의 1차원 배열 표현
이진트리의 구현 왼쪽 편향 이진 트리의 1차원 배열 표현
이진트리의 구현 이진 트리의 1차원 배열에서의 인덱스 관계 B A C D H I E F G J K L 1 2 [0] A B C D E F G H I J K L 1 [1] B 2 [2] A 부모노드의 인덱스 = 2 [3] 3 [4] C [5] 왼쪽 자식노드의 인덱스 = 10 오른쪽 자식노드의 인덱스 = 11 [6] 4 5 6 7 D H I E F G [7] [8] 10 [10] J 11 [11] K 8 9 12 [9] L [12]
이진트리의 구현 이진 트리의 순차 자료구조 표현의 단점 편향 이진 트리의 경우에 사용하지 않는 배열 원소에 대한 메모리 공간 낭비 발생 트리의 원소 삽입/삭제에 대한 배열의 크기 변경 어려움
이진트리의 구현 연결 자료구조를 이용한 이진트리의 구현 단순 연결 리스트를 사용하여 구현 이진 트리의 모든 노드는 2개의 자식 노드를 가지므로 일정한 구조의 노 드를 사용할 수 있다. 이진 트리의 노드 구조에 대한 C 구조체 정의 class TreeNode{ Object data; TreeNode left; TreeNode right; }
이진트리의 구현 완전 이진 트리의 단순 연결 리스트 표현
이진트리의 구현 왼쪽 편향 이진 트리의 단순 연결 리스트 표현
이진트리의 순회 이진 트리의 순회(traversal) 계층적 구조로 저장되어있는 트리의 모든 노드를 방문하여 데이터를 처리하는 연산 순회를 위해 수행할 수 있는 작업 정의 ⑴ 현재 노드를 방문하여 데이터를 읽는 작업 D ⑵ 현재 노드의 왼쪽 서브트리로 이동하는 작업 L ⑶ 현재 노드의 오른쪽 서브트리로 이동하는 작업 R 현재 노드 노드의 데이터 읽기 : 작업 D 왼쪽 서브 트리로 이동하기 : 작업 L 오른쪽 서브 트리로 이동하기 : 작업 R
이진트리의 순회 이진 트리가 순환적으로 정의되어 구성되어있으므로, 순회작업도 서브트리에 대해서 순환적으로 반복하여 완성한다. 왼쪽 서브트리에 대한 순회를 오른쪽 서브트리 보다 먼저 수행한다. 순회의 종류 전위 순회 중위 순회 후위 순회
이진트리의 순회 전위 순회(preorder traversal) 수행 방법 전위 순회 알고리즘 ① 현재 노드 n을 방문하여 처리한다. : D ② 현재 노드 n의 왼쪽 서브트리로 이동한다. : L ③ 현재 노드 n의 오른쪽 서브트리로 이동한다. : R 전위 순회 알고리즘 preorder(T) if (T≠null) then { visit T.data; preorder(T.left); preorder(T.right); } end preorder() [알고리즘 9-1]
이진트리의 순회 전위 순회의 예
이진트리의 순회 전위 순회 과정 >> A-B-D-H-E-I-J-C-F-G-K ① 루트 A에서 시작하여 현재 노드 A의 데이터를 읽고, 왼쪽 서브트리 B로 이동한다. ② 현재 노드 B의 데이터를 읽고, 왼쪽 서브트리 D로 이동한다. ③ 현재 노드 D의 데이터를 읽는다. ④ 현재 노드 D의 왼쪽 단말노드 H의 데이터를 읽고, 오른쪽 노드인 공백 노드를 읽는 것으로 노 드 D에 대한 순회가 끝난다. 이제 현재 노드 D의 이전 경로인 노드 B의 오른쪽 서브트리 E로 이동한다. ⑤ 현재 노드 E의 데이터를 읽는다. ⑥ 현재 노드 E의 왼쪽 단말노드 I의 데이터를 읽는다. ⑦ 현재 노드 E의 오른쪽 단말노드 J의 데이터를 읽는 것으로 노드 E에 대한 순회가 끝나고, 이것 으로 노드 E의 이전 경로인 노드 B의 오른쪽 서브트리의 순회가 끝난다. 다시 노드 B의 이전 경로인 노드 A로 돌아가서 노드 A의 오른쪽 서브트리 C로 이동한다. ⑧ 현재 노드 C의 데이터를 읽는다. ⑨ 현재 노드 C의 왼쪽 단말노드 F의 데이터를 읽고, 오른쪽 서브트리 G로 이동한다. ⑩ 현재 노드 G의 데이터를 읽는다. ⑪ 공백노드인 왼쪽 단말노드를 읽고, 현재 노드 G의 오른쪽 단말노드 K의 데이터를 읽는다.
이진트리의 순회 수식 이진 트리에 대한 전위 순회 수식을 이진 트리로 구성한 수식 이진 트리를 전위 순회하면, 수식에 대 한 전위 표기식을 구할 수 있다. 이진 트리의 전위 순회 경로 예 (A*B-C/D) -*AB/CD
이진트리의 순회 중위 순회(inorder traversal) 수행 방법 중위 순회 알고리즘 ① 현재 노드 n의 왼쪽 서브트리로 이동한다. : L ② 현재 노드 n을 방문하여 처리한다. : D ③ 현재 노드 n의 오른쪽 서브트리로 이동한다. : R 중위 순회 알고리즘 inorder(T) if (T≠null) then { inorder(T.left); visit T.data; inorder(T.right); } end inorder() [알고리즘 9-2]
이진트리의 순회 중위 순회의 예
이진트리의 순회 중위 순회 과정 >> H-D-B-I-E-J-A-F-C-G-K ① 루트 A에서 시작하여 노드 A의 왼쪽 서브트리 B로 이동한다. 현재 노드 B의 왼쪽 서브트리 D 로 이동한다. 현재 노드 D의 왼쪽 단말노드 H의 데이터를 읽는다. ② 현재 노드 D의 데이터를 읽고, 오른쪽 단말노드인 공백노드를 읽고 이전 경로인 노드 B로 돌 아간다. ③ 현재 노드 B의 왼쪽 서브트리 순회가 끝났으므로, 현재 노드 B의 데이터를 읽고, 오른쪽 서브 트리 E로 이동한다. ④ 현재 노드 E의 왼쪽 단말노드 I의 데이터를 읽는다. ⑤ 현재 노드 E의 데이터를 읽는다. ⑥ 현재 노드 E의 오른쪽 단말노드 J의 데이터를 읽고, 이전 경로인 노드 B로 이동한다. ⑦ 노드 B는 이미 방문했으므로 다시 이전 경로인 노드 A로 이동한다. 이로써 현재 노드 A의 왼 쪽 서브트리에 대한 순회가 끝났으므로 현재 노드 A의 데이터를 읽고, 오른쪽 서브트리 C로 이동한다. ⑧ 현재 노드 C의 왼쪽 단말노드 F의 데이터를 읽는다. ⑨ 현재 노드 C의 데이터를 읽고, 오른쪽 서브트리 G로 이동한다. ⑩ 현재 노드 G의 왼쪽 단말노드인 공백노드를 읽고, 현재 노드 G의 데이터를 읽는다. ⑪ 현재 노드 G의 오른쪽 단말노드 K의 데이터를 읽는다.
이진트리의 순회 수식 이진 트리에 대한 중위 순회 수식 이진 트리를 중위 순회하면, 수식에 대한 중위 표기식을 구할 수 있 다. 이진 트리의 중위 순회 경로 예 (A*B-C/D) A*B-C/D
이진트리의 순회 후위 순회(postorder traversal) 수행 방법 후위 순회 알고리즘 ① 현재 노드 n의 왼쪽 서브트리로 이동한다. : L ② 현재 노드 n의 오른쪽 서브트리로 이동한다. : R ③ 현재 노드 n을 방문하여 처리한다. : D 후위 순회 알고리즘 postorder(T) if (T≠null) then { postorder(T.left); postorder(T.right); visit T.data; } end postorder() [알고리즘 9-3]
이진트리의 순회 후위 순회의 경로 : H-D-I-J-E-B-F-K-G-C-A
이진트리의 순회 후위 순회 과정 >> H-D-I-J-E-B-F-K-G-C-A ①루트 A에서 시작하여 노드 A의 왼쪽 서브트리 B로 이동한다. 현재 노드 B에서 왼쪽 서브트리 D로 이동한다. 현재 노드 D의 왼쪽 단말노드 H의 데이터를 읽는다. ② 현재 노드 D의 오른쪽 단말노드인 공백노드를 읽는다. 현재 노드 D의 데이터를 읽고, 이전 경로 인 노드 B로 이동한다. ③ 현재 노드 B의 왼쪽 서브트리에 대한 순회가 끝났으므로 현재 노드 B의 오른쪽 서브트리 E로 이 동한다. 현재 노드 E의 왼쪽 단말노드 I의 데이터를 읽는다. ④ 현재 노드 E의 오른쪽 단말노드 J의 데이터를 읽는다. ⑤ 이제 현재 노드 E의 데이터를 읽고, 이전 경로인 노드 B로 이동한다. ⑥ 현재 노드 B의 오른쪽 서브트리에 대한 순회가 끝났으므로, 현재 노드 B의 데이터를 읽고, 이전 경로인 노드 A로 이동한다. ⑦ 현재 노드 A의 왼쪽 서브트리에 대한 순회가 끝났으므로, 오른쪽 서브트리 C로 이동한다. 현재 노 드 C의 왼쪽 단말노드 F의 데이터를 읽는다. ⑧ 현재 노드 C의 오른쪽 서브트리 G로 이동한다. 현재 노드 G의 왼쪽 단말노드인 공백노드를 읽고, 오른쪽 단말노드 K의 데이터를 읽는다. ⑨ 이제 현재 노드 G의 데이터를 읽고, 이전 경로인 노드 C로 이동한다. ⑩ 현재 노드 C의 오른쪽 서브트리에 대한 순회가 끝났으므로, 현재 노드 C의 데이터를 읽고, 이전 경로인 노드 A로 이동한다. ⑪ 루트노드 A에 대한 오른쪽 서브트리에 대한 순회가 끝났으므로, 현재 노드 A의 데이터를 읽는다.
이진트리의 순회 수식 이진 트리에 대한 후위 순회 수식에 대한 후위 표기식을 구할 수 있음 이진 트리의 후위 순회 경로 예 (A*B-C/D) AB*CD/-
이진트리의 순회 이진 트리의 순회 프로그램 01 class TreeNode{ 02 Object data; 03 TreeNode left; 04 TreeNode right; 05 } 06 07 class LinkedTree{ 08 private TreeNode root; 09 10 public TreeNode makeBT(TreeNode bt1, Object data, TreeNode bt2){ 11 TreeNode root = new TreeNode(); 12 root.data = data;13 root.left = bt1; 14 root.right = bt2; 15 return root; 16 } 17 public void preorder(TreeNode root){ 18 if(root != null){ [예제 9-1] >> 계속
이진트리의 순회 19 System.out.printf("%c", root.data); 20 preorder(root.left); 21 preorder(root.right); 22 } 23 } 24 public void inorder(TreeNode root){ 25 if(root != null){ 26 inorder(root.left); 27 System.out.printf("%c", root.data); 28 inorder(root.right); 29 } 30 } 31 public void postorder(TreeNode root){ 32 if(root != null){ 33 postorder(root.left); 34 postorder(root.right); 35 System.out.printf("%c", root.data); 36 } [예제 9-1] >> 계속
이진트리의 순회 37 } 38 } 39 40 class Ex9_1{ 41 public static void main(String args[]){ 42 LinkedTree T = new LinkedTree(); 43 44 TreeNode n7 = T.makeBT(null, 'D', null); 45 TreeNode n6 = T.makeBT(null, 'C', null); 46 TreeNode n5 = T.makeBT(null, 'B', null); 47 TreeNode n4 = T.makeBT(null, 'A', null); 48 TreeNode n3 = T.makeBT(n6, '/', n7); 49 TreeNode n2 = T.makeBT(n4, '*', n5); 50 TreeNode n1 = T.makeBT(n2, '-', n3); 51 52 System.out.printf("\n Preorder : "); 53 T.preorder(n1); 54 [예제 9-1] >> 계속
이진트리의 순회 실행결과 55 System.out.printf("\n Inorder : "); 56 T.inorder(n1); 57 58 System.out.printf("\n Postorder : "); 59 T.postorder(n1); 60 } 61 } [예제 9-1]
이진 탐색 트리 이진 탐색 트리(binary search tree) 이진 트리에 탐색을 위한 조건을 추가하여 정의한 자료구조 이진 탐색 트리의 정의 ⑴ 모든 원소는 서로 다른 유일한 키를 갖는다. ⑵ 왼쪽 서브트리에 있는 원소의 키들은 그 루트의 키보다 작다. ⑶ 오른쪽 서브트리에 있는 원소의 키들은 그 루트의 키보다 크다. ⑷ 왼쪽 서브트리와 오른쪽 서브트리도 이진 탐색 트리이다.
이진 탐색 트리 이진 탐색 트리의 탐색 연산 루트에서 시작한다. 탐색할 키값 x를 루트 노드의 키값과 비교한다. ☞ 원하는 원소를 찾았으므로 탐색연산 성공 (키 값 x < 루트노드의 키 값)인 경우 : ☞ 루트노드의 왼쪽 서브트리에 대해서 탐색연산 수행 (키 값 x > 루트노드의 키 값)인 경우 : ☞ 루트노드의 오른쪽 서브트리에 대해서 탐색연산 수행 서브트리에 대해서 순환적으로 탐색 연산을 반복한다.
이진 탐색 트리 탐색 연산 알고리즘 searchBST(bsT, x) p ← bsT; if (p=null) then return null; if (x = p.key) then return p; if (x < p.key) then return searchBST(p.left, x); else return searchBST(p.right, x); end searchBST() [알고리즘 9-4]
이진 탐색 트리 탐색 연산 예) 원소 11 탐색하기 ① 찾는 키 값 11을 루트노드의 키 값 8과 비교 (찾는 키 값 11 > 노드의 키 값 8) 이므로 오른쪽 서브트리를 탐색 ② (찾는 키 값 11 > 노드의 키 값 10) 이므로, 다시 오른쪽 서브트리를 탐색 ③ (찾는 키 값 11 < 노드의 키 값 14) 이므로, 왼쪽 서브트리를 탐색 ④ (찾는 키 값 11 = 노드의 키 값 11) 이므로, 탐색 성공! (연산 종료)
이진 탐색 트리 이진 탐색 트리의 삽입 연산 1) 먼저 탐색 연산을 수행 2) 탐색 실패한 위치에 원소를 삽입한다. 삽입할 원소와 같은 원소가 트리에 있으면 삽입할 수 없으므로, 같은 원 소가 트리에 있는지 탐색하여 확인한다. 탐색에서 탐색 실패가 결정되는 위치가 삽입 원소의 자리가 된다. 2) 탐색 실패한 위치에 원소를 삽입한다.
이진 탐색 트리 이진 탐색 트리에서의 삽입 연산 알고리즘 삽입할 자리 탐색 삽입할 노드 만들기 탐색한 자리에 노드연결 insertBST(bsT, x) p ← bsT; while (p≠null) do { if (x = p.key) then return; q ← p; if (x < p.key) then p ← p.left; else p ← p.right; } new ← getNode(); new.key ← x; new.left ← null; new.right ← null; if (bsT = null) then bsT←new; else if (x < q.key) then q.left ← new; else q.right ← new; return; end insertBST() [알고리즘 9-4] 삽입할 자리 탐색 삽입할 노드 만들기 탐색한 자리에 노드연결
이진 탐색 트리 이진 탐색 트리에서의 삽입 연산 예) 원소 4 삽입하기 ① 찾는 키 값 4를 루트노드의 키 값 8과 비교하여, (찾는 키 값 4 < 노드의 키 값 8) 이므로, 왼쪽 서브트리를 탐색한다. ② (찾는 키 값 4 > 노드의 키 값 3) 이므로, 오른쪽 서브트리를 탐색한다. ③ (찾는 키 값 4 < 노드의 키 값 5) 이므로, 왼쪽 서브트리를 탐색해야하는데 왼쪽 자식노드가 없으므로 탐색 실패! 이때, 탐색 실패가 결정된 위치 즉, 왼쪽 자식노드의 위치가 삽입할 자리가 된다. ④ 탐색작업으로 찾은 자리 즉, 노드 5의 왼쪽 자식노드 자리에 노드 4를 삽입한다.
이진 탐색 트리 단순 연결 리스트로 표현한 이진트리에서의 원소 4 삽입하기 q q 탐색실패! q
이진 탐색 트리 이진 탐색 트리의 삭제 연산 1) 먼저 탐색 연산을 수행 2) 탐색하여 찾은 노드를 삭제한다. 삭제할 노드의 위치를 알아야하므로 트리를 탐색한다. 2) 탐색하여 찾은 노드를 삭제한다. 삭제 노드의 경우 삭제할 노드가 단말노드인 경우 : 차수가 0인 경우 삭제할 노드가 하나의 자식노드를 가진 경우 : 차수가 1인 경우 삭제할 노드가 두개의 자식노드를 가진 경우 : 차수가 2인 경우 노드의 삭제 후에도 이진 탐색 트리를 유지해야 하므로 삭제 노드의 경 우에 대한 후속 처리(이진 탐색 트리의 재구성 작업)가 필요하다.
이진 탐색 트리 단말 노드의 삭제 연산 노드 4를 삭제하는 경우
이진 탐색 트리 노드 4를 삭제하는 경우에 대한 단순 연결 리스트 표현 노드를 삭제하고, 삭제한 노드의 부모 노드의 링크 필드에 null 설정
이진 탐색 트리
이진 탐색 트리 자식 노드가 하나인 노드, 즉 차수가 1인 노드의 삭제 연산 노드를 삭제하면, 자식 노드는 트리에서 연결이 끊어져서 고아가 된다. 후속 처리 : 이진 탐색 트리의 재구성 삭제한 부모노드의 자리를 자식노드에게 물려준다. 예) 노드 10을 삭제하는 경우 탐색시작 1단계: 삭제할 노드 탐색 8 ① 10 > 8 2단계: 탐색한 노드 삭제 3단계: 후속처리 3 10 ② 10 = 10 자식노드 이동 2 5 11 16 14 4
이진 탐색 트리 예) 노드 10을 삭제하는 경우
이진 탐색 트리 노드 10을 삭제하는 경우에 대한 단순 연결 리스트 표현
이진 탐색 트리
이진 탐색 트리 자식 노드가 둘인 노드, 즉 차수가 2인 노드의 삭제 연산 노드를 삭제하면, 자식 노드들은 트리에서 연결이 끊어져 고아가 된다. 후속 처리 : 이진 탐색 트리의 재구성 삭제한 노드의 자리를 자손 노드들 중에서 선택한 후계자에게 물려준다. 후계자 선택 방법 방법1) 왼쪽 서브트리에서 가장 큰 자손노드 선택 왼쪽 서브트리의 오른쪽 링크를 따라 계속 이동하여 오른쪽 링크 필드가 NULL인 노드 즉, 가장 오른쪽에 있는 노드가 후계자가 된다. 방법2) 오른쪽 서브트리에서 가장 작은 자손노드 선택 오른쪽 서브트리에서 왼쪽 링크를 따라 계속 이동하여 왼쪽 링크 필드가 NULL인 노드 즉, 가장 왼쪽에 있는 노드가 후계자가 된다.
이진 탐색 트리 삭제한 노드의 자리를 물려받을 수 있는 후계자 노드
이진 탐색 트리 예) 노드 8을 삭제하는 경우
이진 탐색 트리 노드 5를 후계자로 선택한 경우 ① 후계자 노드 5를 원래자리에서 삭제하여, 삭제노드 8의 자리를 물려준다. ② 후계자노드 5의 원래자리는 자식노드 4에게 물려주어 이진 탐색 트리를 재구성한다. (☞ 자식노드가 하나인 노드 삭제연산의 후속처리 수행!) 1단계: 노드 삭제 2단계: 삭제한 노드의 자리를 후계자에게 물려주기 노드 삭제 3단계: 후계자노드의 원래자리를 자식노드에게 물려주기 8 3 10 2 5 14 4 11 16
이진 탐색 트리 노드 10을 후계자로 선택한 경우 ① 후계자 노드 10을 원래자리에서 삭제하여, 삭제노드 8의 자리를 물려준다. ② 후계자노드 10의 원래자리는 자식노드 14에게 물려주어 이진 탐색 트리를 재구성한다. (☞ 자식노드가 하나인 노드 삭제연산의 후속처리 수행!) 1단계: 노드 삭제 2단계: 삭제한 노드의 자리를 후계자에게 물려주기 노드 삭제 3단계: 후계자노드의 원래자리를 자식노드에게 물려주기 8 3 10 2 5 11 16 14 4
이진 탐색 트리 이진 탐색 트리에서의 삭제 연산 알고리즘 deleteBST(bsT, x) p ← 삭제할 노드; parent ← 삭제할 노드의 부모 노드; if (p = null) then return; if (p.left = null and p.right = null) then { if (parent.left = p) then parent.left ← null; else parent.right ← null; } else if (p.left = null or p.right = null) then { if (p.left ≠ null) then { if (parent.left = p) then parent.left ← p.left; else parent.right ← p.left; else { if (parent.left = p) then parent.left ← p.right; else parent.right ← p.right; [알고리즘 9-6] >> 계속
이진 탐색 트리 } else { q ← maxNode(p.left); p.key ← q.key; deleteBST(p.left, p.key); end deleteBST() [알고리즘 9-6]
이진 탐색 트리 이진 탐색 트리의 연산 프로그램 01 class TreeNode{ 02 char data; 03 TreeNode left; 04 TreeNode right; 05 } 06 07 class BinarySearchTree{ 08 private TreeNode root = new TreeNode(); 09 10 public TreeNode insertKey(TreeNode root, char x){ 11 TreeNode p = root; 12 TreeNode newNode = new TreeNode(); 13 newNode.data = x; 14 newNode.left = null; 15 newNode.right = null; 16 if(p == null) 17 return newNode; [예제 9-2] >> 계속
이진 탐색 트리 18 else if(newNode.data < p.data){ 19 p.left = insertKey(p.left, x); 20 return p; 21 } 22 else if(newNode.data > p.data){ 23 p.right = insertKey(p.right, x); 24 return p; 25 } 26 else return p; 27 } 28 29 public void insertBST(char x){ 30 root = insertKey(root, x); 31 } 32 33 public TreeNode searchBST(char x){ 34 TreeNode p = root; 35 while(p != null){ [예제 9-2] >> 계속
이진 탐색 트리 36 if(x < p.data) p = p.left; 37 else if (x > p.data) p = p.right; 38 else return p; 39 } 40 return p; 41 } 42 43 public void inorder(TreeNode root){ 44 if(root != null){ 45 inorder(root.left); 46 System.out.printf(" %c", root.data); 47 inorder(root.right); 48 } 49 } 50 51 public void printBST(){ 52 inorder(root); 53 System.out.println(); [예제 9-2] >> 계속
이진 탐색 트리 54 } 55 } 56 57 class Ex9_2{ 58 public static void main(String args[]){ 59 BinarySearchTree bsT = new BinarySearchTree(); 60 bsT.insertBST('G'); 61 bsT.insertBST('I'); 62 bsT.insertBST('H'); 63 bsT.insertBST('D'); 64 bsT.insertBST('B'); 65 bsT.insertBST('M'); 66 bsT.insertBST('N'); 67 bsT.insertBST('A'); 68 bsT.insertBST('J'); 69 bsT.insertBST('E'); 70 bsT.insertBST('Q'); 71 [예제 9-2] >> 계속
이진 탐색 트리 72 System.out.printf("\nBinary Tree >>> "); 73 bsT.printBST(); 74 75 System.out.printf("Is There \"A\" ? >>> "); 76 TreeNode p1 = bsT.searchBST('A'); 77 if(p1 != null) 78 System.out.printf("Searching Success! Searched key : %c \n", p1.data); 79 else 80 System.out.printf("Searching fail!! There is no %c \n", p1.data); 81 82 System.out.printf("Is There \"Z\" ? >>> "); 83 TreeNode p2 = bsT.searchBST('Z'); 84 if(p2 != null) 85 System.out.printf("Searching Success! Searched key : %c \n", p2.data); 86 else 87 System.out.printf("Searching fail!! \n"); 88 } 89 } [예제 9-2]
이진트리의 순회 실행결과