Data Structure (2) Tree (ํŠธ๋ฆฌ)

๋ฐ˜์‘ํ˜•

๐Ÿ“ŒํŠธ๋ฆฌ

  • node์™€ branch๋ฅผ ์ด์šฉํ•ด์„œ, cycle์„ ์ด๋ฃจ์ง€ ์•Š๋„๋ก ๊ตฌ์„ฑํ•œ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ
  • ํŠธ๋ฆฌ ์ค‘ binary tree(์ด์ง„ ํŠธ๋ฆฌ) ํ˜•ํƒœ์˜ ๊ตฌ์กฐ๋Š” ํƒ์ƒ‰(search) ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ตฌํ˜„์— ๋งŽ์ด ์‚ฌ์šฉ๋จ

In computer science, a tree is a widely used abstract data type that simulates a hierarchical tree structure, with a root value and subtrees of children with a parent node, represented as a set of linked nodes.

A tree data structure can be defined recursively as a collection of nodes (starting at a root node), where each node is a data structure consisting of a value, together with a list of references to nodes (the "children"), with the constraints that no reference is duplicated, and none points to the root.

(์ถœ์ฒ˜: wikipedia)

๐Ÿ“Œ์šฉ์–ด

img

  • node: ํŠธ๋ฆฌ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ธฐ๋ณธ ๋‹จ์œ„(์š”์†Œ)
  • root node: tree์˜ ๊ฐ€์žฅ ์œ„์— ์žˆ๋Š” node
  • level: ์ตœ์ƒ์œ„ ๋…ธ๋“œ๋ฅผ level 0 (๋˜๋Š” 1)ํ•˜์˜€์„ ๋•Œ, ํ•˜์œ„ branch๋กœ ์—ฐ๊ฒฐ๋œ ๋…ธ๋“œ์˜ ๊นŠ์ด
  • parent node: ์–ด๋–ค node์˜ ํ•˜์œ„ level์— ์—ฐ๊ฒฐ๋œ node
  • child node: ์–ด๋–ค ๋…ธ๋“œ์˜ ์ƒ์œ„ level์— ์—ฐ๊ฒฐ๋œ node
  • leaf node: child node๊ฐ€ ์—†๋Š” node
  • sibling (brother node): ๋™์ผํ•œ parent node๋ฅผ ๊ฐ€์ง„ ๋…ธ๋“œ
  • depth: ํŠธ๋ฆฌ์—์„œ node๊ฐ€ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ level
  • width: ํ•œ level์— ์žˆ๋Š” node์˜ ๊ฐœ์ˆ˜
  • breadth: leaf node์˜ ๊ฐœ์ˆ˜

 

๐Ÿ“ŒBinary Tree์™€ Binary Search Tree

  • binary tree: ํ•œ ๋…ธ๋“œ๊ฐ€ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ branch(child node)๊ฐ€ 2์ธ ํŠธ๋ฆฌ
  • binary search tree (BST): binary tree์—์„œ ์™ผ์ชฝ ๋…ธ๋“œ๋Š” ํ•ด๋‹น ๋…ธ๋“œ๋ณด๋‹ค ์ž‘์€ ๊ฐ’, ์˜ค๋ฅธ์ชฝ ๋…ธ๋“œ๋Š” ๋…ธ๋“œ๋Š” ํ•ด๋‹น ๋…ธ๋“œ๋ณด๋‹ค ํฐ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํŠธ๋ฆฌ

 

binary search tree์˜ ์ฃผ์š” ์šฉ๋„ / ์žฅ์ 

  • ์ฃผ์š” ์šฉ๋„๋Š” ๋ฐ์ดํ„ฐ ๊ฒ€์ƒ‰/ํƒ์ƒ‰์œผ๋กœ binary search tree๋ฅผ ์ด์šฉํ•˜๋ฉด ๊ฒ€์ƒ‰ ์†๋„์˜ ์ด์ ์ด ์žˆ๋‹ค.
  • BST vs Array

binary-search-tree-sorted-array-animation

(์ถœ์ฒ˜: https://blog.penjee.com/5-gifs-to-understand-binary-search-tree/#binary-search-tree-insertion-node)

 

๐Ÿ“ŒBST ๊ตฌํ˜„ (linked list ํ™œ์šฉ)

Node class ๋งŒ๋“ค๊ธฐ


  
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None

 

Binary Search Tree์— node ์‚ฝ์ž…

! BST ์กฐ๊ฑด์— ๋งž๋„๋ก ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•ด์•ผ ํ•จ (์ž‘์€ ๊ฐ’์€ ์™ผ์ชฝ์—, ํฐ ๊ฐ’์€ ์˜ค๋ฅธ์ชฝ์—)


  
class NodeMgmt:
def __init__(self, head):
self.head = head # root node
def insert(self, value):
self.current_node = self.head
while True:
if value < self.current_node: #์™ผ์ชฝ์œผ๋กœ ์‚ฝ์ž…
if self.current_node.left != None: #left์— data๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
current_node = self.current_node.left #์žˆ์œผ๋ฉด node ์ด๋™(๋ฐ‘ ๋…ธ๋“œ ํƒ€๊ธฐ)
else:
current_node.left = Node(value) #left์— data ์—†์œผ๋ฉด ๋ฐ”๋กœ ์‚ฝ์ž…
break
else: # value > self.current_node -> ์˜ค๋ฅธ์ชฝ์œผ๋กœ ์‚ฝ์ž…
if self.current_node.right != None: #right์— data๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
current_node = self.current_node.right #์žˆ์œผ๋ฉด ์•„๋ž˜ node ํƒ€๊ธฐ
else:
current_node.right = Node(value)
break

 

์ด์ง„ ํƒ์ƒ‰ ํŠธ๋ฆฌ ํƒ์ƒ‰ (search() ํ•จ์ˆ˜ ๊ตฌํ˜„)

๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š”์ง€ ์—†๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ•จ์ˆ˜ (๋ง๊ทธ๋Œ€๋กœ ๊ฒ€์ƒ‰)


  
def search(self, value):
self.current_node = self.head #์ผ๋‹จ head๋ถ€ํ„ฐ search
while self.current_node:
if self.current_node == value:
return True
elif value < self.current_node:
self.current_node = self.current_node.left
else:
self.current_node = self.current_node.right
return False #์—ฌ๊ธฐ๊นŒ์ง€ ์™”๋‹ค๋Š” ๊ฑด ๋‚ด๊ฐ€ ์ฐพ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค๋Š” ๋œป
{% endhighlight %}
<br>
### <strong><span style = "color:red">์ด์ง„ ํƒ์ƒ‰ ํŠธ๋ฆฌ ์‚ญ์ œ (delete() ํ•จ์ˆ˜ ๊ตฌํ˜„)</span></strong>
#### 0) ์‚ญ์ œํ•  node ํƒ์ƒ‰
{% highlight python %}
def delete(self, value):
searched = False
self.current_node = self.head
self.parent = self.head #๋…ธ๋“œ ์‚ญ์ œ๋ฅผ ์œ„ํ•ด์„œ๋Š” parent node๋„ ์•Œ๊ณ  ์žˆ์œผ์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— parent๋ณ€์ˆ˜๋„ ์ƒ์„ฑํ•ด์ค€๋‹ค.
while self.current_node:
if self.current_node == value:
searched = True
break
elif value < self.current_node:
self.current_node = self.current_node.left
self.parent = self.current_node
else:
self.current_node = self.current_node.right
self.parent = self.current_node
if searched == False:
return False

์ด๋ ‡๊ฒŒ ์‚ญ์ œํ•  node ํƒ์ƒ‰๊นŒ์ง€ ์ง„ํ–‰ํ•˜๋ฉด self.current_node๊ฐ€ ์‚ญ์ œํ•  node๋กœ ํ• ๋‹น๋˜์–ด์žˆ๊ณ , self.parent๋Š” ์‚ญ์ œํ•  node์˜ parent node๋กœ ํ• ๋‹น๋˜์–ด ์žˆ๋Š” ์ƒํƒœ๊ฐ€ ๋œ๋‹ค.

์ดํ›„ ๋ถ€ํ„ฐ๋Š” ์‚ญ์ œํ•˜๋Š” case๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ code๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.

 

Case 1) ์‚ญ์ œํ•  node์˜ child node๊ฐ€ 0๊ฐœ์ผ ๋•Œ (leaf node ์‚ญ์ œ)

์‚ญ์ œํ•  node์˜ parent node๊ฐ€ ์‚ญ์ œํ•  node๋ฅผ ๊ฐ€๋ฆฌํ‚ค์ง€ ์•Š๋„๋ก ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋!
node๋Š” ์‚ญ์ œํ•˜๊ณ  ์—ฐ๊ฒฐ branch๋Š” None์œผ๋กœ ์ดˆ๊ธฐํ™”


  
## case1: ์‚ญ์ œํ•  node์˜ child node๊ฐ€ 0๊ฐœ์ธ ๊ฒฝ์šฐ
if self.current_node.left == None and self.current_node.right == None: #leaf node์ธ์ง€ ํ™•์ธ!
if value < self.parent.value: #value๊ฐ’, current_node ์ž์ฒด๋Š” ์•Œ๊ณ  ์žˆ์ง€๋งŒ, current_node๊ฐ€ parent์˜ ์™ผ์ชฝ์— ์žˆ๋Š”์ง€, ์˜ค๋ฅธ์ชฝ์— ์žˆ๋Š”์ง€๋Š” ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค.
self.parent.left == None
else:
self.parent.left == None

 

Case 2) ์‚ญ์ œํ•  node์˜ child node๊ฐ€ 1๊ฐœ์ผ ๋•Œ

image

2๋ฒˆ case์—์„œ ๋˜ ๊ฒฝ์šฐ๋ฅผ ๋‚˜๋ˆ„์–ด ๋ณด๋ฉด,

1) ์‚ญ์ œํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ "์™ผ์ชฝ"์— child node๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋•Œ

  • ์‚ญ์ œํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ parent node์˜ ์™ผ์ชฝ์— ์žˆ์„ ๋•Œ
  • ์‚ญ์ œํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ parent node์˜ ์˜ค๋ฅธ์ชฝ์— ์žˆ์„ ๋•Œ

2) ์‚ญ์ œํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ "์˜ค๋ฅธ์ชฝ"์— child node๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋•Œ

  • ์‚ญ์ œํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ parent node์˜ ์™ผ์ชฝ์— ์žˆ์„ ๋•Œ
  • ์‚ญ์ œํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ parent node์˜ ์˜ค๋ฅธ์ชฝ์— ์žˆ์„ ๋•Œ

  
## case2: ์‚ญ์ œํ•  node์˜ child node๊ฐ€ 1๊ฐœ์ธ ๊ฒฝ์šฐ
elif self.current_node.left != None and self.current_node.right == None:
if value < self.parent.value:
self.parent.left = self.current_node.left
else:
self.parent.right = self.current_node.left
elif self.current_node.left == None and self.current_node.right != None:
if value < self.parent.value:
self.parent.left = self.current_node.right
else:
self.parent.right = self.current_node.right

 

Case 3) ์‚ญ์ œํ•  node์˜ child node๊ฐ€ 2๊ฐœ์ผ ๋•Œ

child node๊ฐ€ 2๊ฐœ์ธ ๊ฒฝ์šฐ๋Š” ๋งค์šฐ ๋ณต์žกํ•˜๋‹ค...,,ใ… _ใ… 

์šฐ์„ , case3์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ „๋žต์„ 2๊ฐ€์ง€ ์ด๋‹ค. (๋ฐฉํ–ฅ๋งŒ ๋ฐ˜๋Œ€)

  1. ์‚ญ์ œํ•  Node์˜ ์˜ค๋ฅธ์ชฝ child ์ค‘, ๊ฐ€์žฅ ์ž‘์€ ๊ฐ’(๊ฐ€์žฅ ์™ผ์ชฝ ๋…ธ๋“œ)์„ ์‚ญ์ œํ•  Node์˜ Parent Node๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ํ•œ๋‹ค.
  2. ์‚ญ์ œํ•  Node์˜ ์™ผ์ชฝ child ์ค‘, ๊ฐ€์žฅ ํฐ ๊ฐ’(๊ฐ€์žฅ ์˜ค๋ฅธ์ชฝ ๋…ธ๋“œ)์„ ์‚ญ์ œํ•  Node์˜ Parent Node๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ํ•œ๋‹ค.

๋‚˜๋Š” 1๋ฒˆ ์ „๋žต์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ๋‹ค.

์„ธ๋ฒˆ์งธ ๊ฒฝ์šฐ์—์„œ๋„ ๋˜ ๊ฒฝ์šฐ๋ฅผ ๋‚˜๋ˆ„๋ฉด,

Case 3-1) ์‚ญ์ œํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ "์™ผ์ชฝ"์— child node๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋•Œ
  • Case 3-1-1) change_node์˜ ์˜ค๋ฅธ์ชฝ์— child๊ฐ€ ์žˆ์„ ๋•Œ
  • Case 3-1-2) change_node์˜ ์˜ค๋ฅธ์ชฝ์— child๊ฐ€ ์—†์„ ๋•Œ

img

Case 3-2) ์‚ญ์ œํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ "์˜ค๋ฅธ์ชฝ"์— child node๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋•Œ
  • Case 3-2-1) change_node์˜ ์˜ค๋ฅธ์ชฝ์— child๊ฐ€ ์žˆ์„ ๋•Œ
  • Case 3-2-2) change_node์˜ ์˜ค๋ฅธ์ชฝ์— child๊ฐ€ ์—†์„ ๋•Œ

img


  
## case3: ์‚ญ์ œํ•  node์˜ child node๊ฐ€ 2๊ฐœ์ธ ๊ฒฝ์šฐ
elif self.current_node.left != None and self.current_node.right != None: # case3
if value < self.parent.value: #์‚ญ์ œํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ "์™ผ์ชฝ"์— child node๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋•Œ
self.change_node = self.current_node.right
self.change_node_parent = self.current_node.right
while self.change_node.left != None: #์˜ค๋ฅธ์ชฝ child ์ค‘ ๊ฐ€์žฅ ์ž‘์€ ๊ฐ’ ์ฐพ๊ธฐ!(์ „๋žต1)
self.change_node_parent = self.change_node
self.change_node = self.change_node.left #while๋ฌธ์€ change_node๋ฅผ ์ฐพ์•„ ๊ฐ์„ ์œ„ํ•จ
if self.change_node.right != None: #Case 3-1-1
self.change_node_parent.left = self.change_node.right
else: #Case 3-1-2
self.change_node_parent.left = None
self.parent.left = self.change_node
self.change_node.left = self.current_node.left
self.change_node.right = self.current_node.right
else: # ์‚ญ์ œํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ "์˜ค๋ฅธ์ชฝ"์— child node๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์„ ๋•Œ
self.change_node = self.current_node.right
self.change_node_parent = self.current_node.right
while self.change_node.left != None:
self.change_node_parent = self.change_node
self.change_node = self.change_node.left
if self.change_node.right != None: #Case 3-2-1
self.change_node_parent.left = self.change_node.right
else: #Case 3-2-2
self.change_node_parent.left = None
self.parent.right = self.change_node
self.change_node.left = self.current_node.left
self.change_node.right = self.current_node.right
return True

 

test code


  
# test code
# 0~999์ˆซ์ž ์ค‘์—์„œ ์ž„์˜๋กœ 100๊ฐœ์˜ ์ˆซ์ž๋ฅผ ์ถ”์ถœํ•ด์„œ, ์ด์ง„ ํƒ์ƒ‰ ํŠธ๋ฆฌ์— ์ž…๋ ฅ, ๊ฒ€์ƒ‰, ์‚ญ์ œ
import random
bst_nums = set() #์ง‘ํ•ฉ ๋งŒ๋“ค๊ธฐ(์ค‘๋ณต๋˜์ง€ ์•Š๋„๋ก)
while len(bst_nums) != 100:
bst_nums.add(random.randint(0, 999))
#์„ ํƒ๋œ 100๊ฐœ์˜ ์ˆซ์ž๋ฅผ ์ด์ง„ ํƒ์ƒ‰ ํŠธ๋ฆฌ์— ์ž…๋ ฅ, root node๋Š” ์ž„์˜๋กœ 500์„ ๋„˜์Œ
head = Node(500)
binary_tree = NodeMgmt(head)
for num in bst_nums:
binary_tree.insert(num)
#์ž…๋ ฅํ•œ 100๊ฐœ์˜ ์ˆซ์ž search
for num in bst_nums:
if binary_tree.search(num) == False:
print("search failed", num)
#์ž…๋ ฅํ•œ 100๊ฐœ์˜ ์ˆซ์ž ์ค‘ 10๊ฐœ์˜ ์ˆซ์ž๋ฅผ ๋žœ๋ค ์„ ํƒํ•˜์—ฌ ์‚ญ์ œ
delete_nums = set()
bst_nums = list(bst_nums)
while len(delete_nums) != 10:
delete_nums.add(bst_nums[random.randint(0, 99)])
for del_num in delete_nums:
if binary_tree.delete(del_num) == False:
print("delete failed", del_num)

๐Ÿ“Œ์ด์ง„ ํƒ์ƒ‰ ํŠธ๋ฆฌ์˜ ์‹œ๊ฐ„ ๋ณต์žก๋„

  • depth๋ฅผ h๋ผ๊ณ  ํ‘œํ˜„ํ•˜๋ฉด BST์˜ ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” O(h)
  • ํŠธ๋ฆฌ๊ฐ€ n๊ฐœ์˜ node๋ฅผ ๊ฐ€์ง„๋‹ค๋ฉด h = log(n)์— ๊ฐ€๊นŒ์šฐ๋ฏ€๋กœ, ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” O(log n)์œผ๋กœ ํ‘œํ˜„ ๊ฐ€๋Šฅ

(๋น…์˜ค ํ‘œ๊ธฐ๋ฒ•์—์„œ log n์—์„œ log์˜ ๋ฐ‘์€ 10์ด ์•„๋‹ˆ๋ผ, 2๋ฅผ ์˜๋ฏธ)

=> ์ฆ‰, ํ•œ ๋ฒˆ ์‹คํ–‰ํ•  ๋•Œ ๋งˆ๋‹ค, 50%์˜ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋ช…๋ น์„ ์ œ๊ฑฐํ•œ๋‹ค๋Š” ์˜๋ฏธ (ํŠธ๋ฆฌ๊ฐ€ ๋ฐ˜์”ฉ ์ชผ๊ฐœ์ ธ ๊ฐ€๋ฉด์„œ depth๋Š” +1). ์ฆ‰, 50%์˜ ์‹คํ–‰์‹œ๊ฐ„์„ ๋‹จ์ถ•์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธ

ex) ๊ท ํ˜•์ ์ธ tree์—์„œ ๋Œ€๋žต ๋…ธ๋“œ๊ฐ€ 1๊ฐœ๋ฉด depth๋Š”1, ๋…ธ๋“œ๊ฐ€ ~3๊ฐœ๋ฉด depth๋Š” 2, ๋…ธ๋“œ๊ฐ€ ~7๊ฐœ๋ฉด depth๋Š” 3, ๋…ธ๋“œ๊ฐ€ ~15๊ฐœ์ด๋ฉด depth๋Š” 4 ==> ์ฆ‰, ๋…ธ๋“œ๊ฐ€ n๊ฐœ ์ด๋ฉด depth๋Š” log n์— ์ ‘๊ทผ

 

์ด์ง„ ํƒ์ƒ‰ ํŠธ๋ฆฌ์˜ ๋‹จ์ 

: ์ตœ์•…์˜ ๊ฒฝ์šฐ(ํŠธ๋ฆฌ๊ฐ€ ๋น„๊ท ํ˜•์ ์ผ ๊ฒฝ์šฐ) linked list ๋“ฑ๊ณผ ๋™์ผํ•œ ์„ฑ๋Šฅ $(O(n))$์„ ๋ณด์—ฌ์คŒ

(ํ‰๊ท  ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” $O(log n)$์ด์ง€๋งŒ, ์ด๊ฑด ํŠธ๋ฆฌ๊ฐ€ ์–‘์ชฝ์œผ๋กœ ๊ท ํ˜•์ด ์žกํ˜€์žˆ์„ ๋•Œ์˜ ์‹œ๊ฐ„๋ณต์žก๋„)

ex) branch๊ฐ€ ํ•œ์ชฝ์œผ๋กœ๋งŒ ๋ป—์–ด์žˆ์„ ๊ฒฝ์šฐ, node๊ฐ€ n๊ฐœ ์ด๋ฉด, depth๋„ n์ด ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” $O(n)$์ด ๋œ๋‹ค.

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€