모든 데이터가 internal 노드에 저장되는 이진트리
key 값(또는 entry) 를 저장. key를 이용해서 데이터를 구분
external 노드에는 key값(또는 entry) 를 저장하지 않음
inorder-traveral (중위순회) 가능.
동일한 item 들을 저장하고 있는 BST 는 여러가지 형태가 나올 수 있다.
ex) 1,2,3 이라는 3개의 key값을 저장한 BST는 아래와 같은 다양한 형태가 나옴
형태1 형태2 형태3 형태4
2 1 3 1
1 3 3 2 2
2 1 3
구조 조건
순서조건
=> (모든 key값이 다르다고 가정)
특정 노드의 왼쪽 subtree : 해당 노드보다 작은 key들의 집합
특정 노드의 오른쪽 subtree : 해당 노드보다 큰 key들의 집합
"왼쪽 서브트리는 해당 노드보다 작은 값, 오른쪽 서브트리는 해당 노드보다 큰 값" 이라는 특징을 활용
임의의 key값을 주면, 그 key가 저장된 데이터를 찾을 수 있다.
- 실행시간 : O(h)
- 한 레벨을 내려가는데 비교연산을 1번 진행하므로, O(h) 이다!
- 주의 : O(log n) 이 아니다!!!!!
탐색과정
1) 탐색을 시작할 노드 v (일반적으로 root 노드) 와 탐색할 key인 k를 인자로 받는다.
2) 찾으려는 값 k가 더 작으면 왼쪽 subtree 로 이동, 더 크면 오른쪽 subtree로 이동한다. (by 재귀호출)
=> 만약 k와 해당 노드의 key가 일치하면, 바로 리턴하면 끝!
=> k값을 갖는 노드 or external 노드를 만날떄까지 반복한다.
Algorithm TreeSearch(k, v)
if(v.isExternal()) // external 노드라면 null을 리턴하게 됨
return v
if k < v.key() // k의 값이 더 작은 경우, k는 왼쪽 서브트리에 존재
return TreeSearch(k, v.left())
else if k = v.key() // k값이 일치하면 리턴하면 끝
return v
else if k > v.key() // k의 값이 더 큰 경우, k는 오른쪽 서브트리에 존재
return TreeSearch(k, v.right())
예시 : TreeSearch(4, root)
만약 TreeSearch(5, root) 를 호출한경우는?
put( k ) 메서드로 구현됨
핵심은 삽입할 노드가 있어야 할 자리를 찾는 것이다.
=> TreeSearch() 를 활용
- 원리
=> TreeSearch() 연산에서 탐색에 실패했을 때 external 노드가 리턴되는 것을 활용
1) TreeSearch() 를 호출해서 정렬의 원리에 따라 external 노드에 도달한다.
2) 리턴되는 external 노드 위치에 k값을 가지는 새로운 노드를 삽입한다.
3) 삽입된 노드 왼쪽, 오른쪽 자식에 null 값을 가지는 external 노드를 양쪽에 매달아 줘서, 그 노드가 internal 노드가 되게한다.
- 실행시간 : O(h)
- 1) O(h)
=> TreeSearch() : 반드시 external 노드까지 내려가므로 수행시간은 트리의 높이 만큼이 걸린다.
2) O(1)
=> key값으로 k가지는 새로운 노드 생성하고 트리에 매달고, 그 노드의 양쪽 자식에 null 노드 매다는 과정
예시 - put(5) => key 값 5를 삽입
삽입 전
삽입 후
삭제과정 요약
과정1) 삭제하려는 노드를 트리에서 탐색해서 찾아낸다
과정2)case1) 삭제하려는 노드의 왼쪽or 오른쪽 자식이 external 인 경우
- removeExternal() 로 같이 한 묶음으로 삭제
- 삭제된 노드의 부모와, 삭제안된 나머지 한 자식을 이어준다.
case2) 삭제하려는 노드의 왼쪽, 오른쪽 자식이 모두 external 인 경우
- removeExternal() 로 두 자식중 아무거나 한 묶음으로 같이 삭제
- 삭제된 노드의 부모와, 삭제안된 나머지 한 자식을 이어준다.
case3) 삭제하려는 노드의 두 자식이 모두 internal인 경우
- 삭제하려는 노드 k를 찾기(=탐색)
- successor 찾기 (노드 k의 오른쪽 서브트리에 대해 중위순회를 진행)
- 찾아낸 successor 값을 노드 k에 덮어씌우기
- 기존의 successor 를 자신의 왼쪽 external 와 함께 삭제
삭제연산 종류
1) removeExternal(w) : 자식이 하나만 external 노드인 경우
2) removeExternal(w) : 두 자식 모두 external 인 경우
ex) 4를 삭제
3) 삭제하려는 노드의 두 자식 모두 internal 노드인 경우
실행시간 : O(h)
1) 삭제하려는 노드 찾기(=탐색) (즉, key값 k를 찾기) : O(h)
2) successor 찾기 : O(h) => 오른쪽 서브트리에 대해 external 이 나오기 직전노드를 찾기
3) 찾아낸 sccuessor 를 노드 k에 값을 덮어씌우기 : O(1)
4) 기존의 successor 를 자신의 왼쪽 external 과 함께 삭제 : O(1)
5) 삭제안된 나머지 한 자식을 삭제된 노드의 부모에 매달기 : O(1)
힙처럼 삭제 대상 노드에 다른 노드를 덮어씌우는 방법을 사용
과정1) 순서 조건을 지키기 위해, 대상 노드보다 큰 값중 가장 가까운 값(successor)으로 덮어씌운다.
과정2) removeExternal() 호출
=> successor을 대상 노드에 덮어씌우고 이후 successor와 그의 자식 external 노드(주로 left) 하나를 삭제한다.
예제) 노드 3을 삭제
BST
- 사용공간 : O(n)
- 탐색,삽입,삭제 연산 모두 : O(h)
- worst case : O(n) ( = O(h) ) => 트리가 한쪽으로 쭉 나열된 형태인 경우
- best case : O(log n) ( = O(h) ) => 균형이진탐색트리( 트리가 완전이진트리인 경우 )
n개의 아이템을 저장한 BST의 높이가 h라할때,
최악의 경우 위 첫번쨰 그림처럼, BST가 쭉 나열된 형태일 수 있다.
=> Balanced Binary Search Tree(균형 이진탐색트리)