data class Node(var key: Int, var left: Node? = null, var right: Node? = null)
현재 node 기준 left or right child를 방문하는 순간 반대 child를 root node로 하는 sub tree는 search 대상에서 제거된다. 따라서 이상적인 binary search tree의 경우 탐색이 에 수행 가능하다.
fun search(root: Node, target: Int): Node?{
var p: Node? = root
while (p!=null){
p = when {
p.key == target -> return p
p.key < target -> p.right
else -> p.left
}
}
return null
}
fun insert(root: Node?, target: Int): Node? {
var p: Node? = root
var parent: Node? = null
while (p!=null){
parent = p
p = when {
p.key == target -> return p
p.key < target -> p.right
else -> p.left
}
}
val newNode = Node(target)
if (parent!=null){
if (parent.key < target) parent.right = newNode
else if (parent.key > target) parent.left = newNode
}
return newNode
}
실제 구현시는, left sub tree or right sub tree의 height가 큰 sub tree를 선택해서 진행하면 된다.
fun delete(root: Node?, target: Int): Node? {
var p: Node? = root
var parent: Node? = null
while (p!=null && p.key != target){
parent = p
p = when {
p.key < target -> p.right
else -> p.left
}
}
if (p == null) {
println("delete 불가능")
}else{
if (p.left==null && p.right==null){
if (parent==null){
return null
}else{
if (parent.left == p) parent.left = null
else parent.right = null
}
}else if (p.left == null || p.right == null){
val child = p.left ?: p.right
if (parent == null){
root?.run {
key = child!!.key
left = child.left
right = child.right
}
}else{
if (parent.left == p) parent.left = child
else parent.right = child
}
}else {
var rightSubMinNode = p.right
var rightSubMinNodeParent = p
while (rightSubMinNode?.left != null){
rightSubMinNodeParent = rightSubMinNode
rightSubMinNode = rightSubMinNode.left
}
p.key = rightSubMinNode!!.key
if (rightSubMinNodeParent?.left == rightSubMinNode){
rightSubMinNodeParent.left = rightSubMinNode.right
}else{
rightSubMinNodeParent?.right = rightSubMinNode.right
}
p = rightSubMinNode
}
p = null
}
return root
}
쉽게 말해서 inorder traversal은 left sub tree → 자기 자신 → right sub tree 순으로 자기 자신을 중간에 처리하는 traversal 방법이다.
fun inorderTraversal(root: Node?){
if (root == null) return
inorderTraversal(root.left)
println(root.key)
inorderTraversal(root.right)
}
최종 출력 결과: 4 → 6 → 8 → 10 → 12 → 14 → 15 → 20
오름차순으로 출력된 것을 확인할 수 있다.
자기 자신을 젤 먼저 처리하고 left sub tree → right sub tree를 처리해서 말 그대로 preorder이다.
fun preorderTraversal(root: Node?){
if (root == null) return
print("${root.key} ")
preorderTraversal(root.left)
preorderTraversal(root.right)
}
최종 출력 결과: 10 → 6 → 4 → 8 → 12 → 15 → 14 → 20
자기 자신을 젤 나중에 처리해서 말 그대로 Postorder이다.
fun preorderTraversal(root: Node?){
if (root == null) return
print("${root.key} ")
preorderTraversal(root.left)
preorderTraversal(root.right)
}
최종 출력 결과: 4 → 8 → 6 → 14 → 20 → 15 → 12 → 10
var i = 0
val arr = IntArray(8)
fun checkBSTInorderTraversal(root: Node?, arr: IntArray){
if (root == null) return
checkBSTInorderTraversal(root.left, arr)
arr[i++] = root.key
checkBSTInorderTraversal(root.right, arr)
}
fun checkBST(arr:IntArray):Boolean{
for (i in 0 until arr.size-1){
if (arr[i]>=arr[i+1]) return false
}
return true
}
위의 방법은 tree의 size 만큼 배열을 할당해야 하므로 메모리 측면에서 낭비를 초래할 수 있다. 아래에 소개할 방법은 배열을 할당하지 않고 하나의 int 변수만 할당하여 확인하는 방법이다
var value: Int = Int.MIN_VALUE
fun checkBSTInorderTraversalNoArr(root: Node?):Boolean{
if (root == null) return true
if (!checkBSTInorderTraversalNoArr(root.left)) return false
if (root.key < value){
return false
}
value = root.key
if (!checkBSTInorderTraversalNoArr(root.right)) return false
return true
}
fun checkBST(root: Node?, lowerBoundExist: Boolean,
upperBoundExist: Boolean, lowerBound: Int, upperBound: Int): Boolean{
if (root == null) return true
if (lowerBoundExist && root.key <= lowerBound) return false
if (upperBoundExist && root.key >= upperBound) return false
return checkBST(root.left, lowerBoundExist, true, lowerBound, root.key)
&& checkBST(root.right, true, upperBoundExist, root.key, upperBound)
}
3가지 방법 모두 tree의 모든 원소를 재귀적으로 탐색 한다. 따라서 에 수행 가능한 알고리즘 이다.