Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

Finn의 개발블로그

9. 레드 블랙 트리 본문

자료구조

9. 레드 블랙 트리

BeginnerFinn 2018. 11. 25. 18:59

레드블랙트리에 대한 이미지 검색결과

1. 레드 블랙 트리

- 이진 검색 트리에서 계속해서 높은 값을 넣을경우 트리의 균형이 망가진다

- 최악의경우 Big-O(n)의 시간을 가지게된다

- 이러한 일을 방지하기 위해서 rebalance를 해줘야하고 레드 블랙 트리는 rebalance를 해주는 트리 중 하나 이다


2. 레드 블랙 트리 의 조건

- 각 노드는 Red or Black 이라는 색깔을 갖는다

- Root Node의 색은 Black이다

- null node를 포함한 각 leaf node는 black 이다

- 어떠한 노드의 색이 red라면 children의 색은 모두 black이다 = 빨간색 노드는 연속해서 나올 수 없다

- root node에서 어떠한 리프 노드 사이에는 똑같은 black 노드의 수를 가진다


3. 레드 블랙 트리 의 삽입

- 이진 검색 트리의 특성을 유지하면서 노드를 삽입하고 삽인된 노드의 색은 RED로 지정한다.

- Red로 지정하는 이유는 Black-Height변견을 최소화하기 위함이다

- 레드 블랙 트리에서 삽입의 경우는 5 가지

- Rotate

- Right Rotate

- 좌측에 있던 자식노드를 부모 노드로 변경한 뒤, 왼쪽 자식노드의 오른쪽 자식노드를 부모의 왼쪽 자식으로 연결한다.

- Left Rotate

- 우측에 있던 자식 노드를 부모 노드로 변경한 뒤, 오른쪽 자식노드의 왼쪽 자식 노드를 부모의 오른쪽 자식으로 연결한다.


- LL (1)

부모 노드(90) 에서 자식 노드( 80) L, 할아버지 노드(100)에서 부모 노드(90)의 방향 L

- Right rotate 진행


- RR (2)

- LL의 반대

- Left Rotate 실행


- LR (3)

- 부모 노드(110)에서 자식 노드(105)의 방향 L, 할아버지 노드(100)에서 부모 노드(110)로 방향 R 

- Right rotate 실행

- Left rotate 실행

- RL (4)

- LR의 반대 


- Recolor(5)

- 삼촌 노드(부모 노드의 형제)와 부모 노드 둘다 Red라면 삼촌 노드와 부모 노드의 색을 Black으로 만들고 할아버지 노드를 Red로 만든다.(할아버지 노드가 Root Node라면 Black)




3. 레드 블랙 트리 의 삭제

- 레드 블랙 트리의 삭제는 6 가지 경우가 있다

Case(1) 

- 삭제 노드 N 루트 노드 일때 

- 모든 경로에서 하나의 Black Node를 삭제했고 루트 노드는 Black Node라 모든 특성 보존

- Case(2)

- 부모 노드가 Black Node, 형제 노드가 Red, 형제 노드의 자식이 Black 이거나 없을때

- 형제 노드를 Roate를 하고 Case(1) 부터 다시 시작한다


- Case(3)

- 부모 노드가 Black Node, 형제 노드 Black Node, 형제 노드의 자식이 Black 이거나 없을때

- 형제 노드의 색을 Red 변환 하고 Case(1) 부터 다시 시작한다 


- Case(4)

- 부모 노드 Red, 형제 노드 Black, 형제 노드의 자식들이 Red Node가 아닐때

- 형제 노드와 부모 노드의 색을 바꾸고 노드 삭제


- Case(5)

- closer node(형제 노드의 안쪽 노드) Red, out node(형제 노드의 바깥쪽 노드) Black 이거나 없을때

- closer node 를 부모와 형제 노드의 방향에 따라 Rotate 한고 Case6을 진행한다

 

- Case(6)

- 형제 노드 Black, out node가 Red 일때

- 형제 노드를 부모와 형제 노드의 방향에 따라 Rotate 하고 리턴한다




4. 레드 블랙 트리 ADT


- init(self)

- 레드 블랙 트리를 초기화 한다


- insert(self, data)

- 레드 블랙 트리에 데이터를 저장한다.


- rebalance(self, node)

- 삽입된 노드를 레드 블랙 트리의 조건에 따라 rebalance 해준다


- recolor(self, node)

- 삽입된 노드를 레드 블랙 트리의 조건에 따라 recolor를 하고 다시 rebalance를 실행한다.


- find_node(self, data)

-  data 가 트리에 있는지 없는지 확인하고 있으면 data 값을 가진 node를 반환한다.


- _find_in_order_successor(self, node)

- 노드를 삭제 할 때 자식이 있을때 오른쪽 자식의 가장 왼쪽 node를 찾아서 반환한다.

- 오른쪽 자식이 왼쪽 node가 없을때는 오른쪽 자식이 반환된다.


- remove(self, data)

- data를 가진 노드가 있는지 확인한다

- data를 가진 노드가 자식이 있는지 확인하고  _find_in_order_successor()함수를 실행한다.

_find_in_order_successor()의 리턴 node를 data를 가진 노드에 넣어준다.

- 삭제할 node를 _remove() 함수의 인자로 넘겨준다.


- _remove(self, node)

- node가 root node면 남은 자식을 root node로 교체한다.

- node의 색이 Red 면 remove_node() 함수 실행한다.

- node의 색이 Black 면 remove_black_node() 함수 실행한다.


- remove_black_node(self, node)

- case_1() 함수를 실행하고 리턴된 node를 remove_node() 인자로 넘긴다.


- remove_node(self, node)

- node를 삭제한다


- case_1 ~ 6(self, node)

- node가 삭제 되었을때 레드 블랙 트리의 조건에 맞게 트리를 rebalance 한다.


- left_rotate(self, node, recolor=None), right_rotate(self, node, recolor=None)

- node를 left rotate시키고 recolor true 이면 node의 색을 Black 변환하고 자식들을 Red로 변환한다.


 


class Node:
def __init__(self, data):
self.data = data
self.left = self.right = self.parent = None
self.color = True


class RedBlackTree:
null_node = Node(data=None)
null_node.color = False

def __init__(self):
self.root_node = None

def insert(self, data):
if not self.root_node:
self.root_node = Node(data)
self.root_node.color = False
self.root_node.right = self.null_node
self.root_node.left = self.null_node
return True
node = self.root_node
while node:
if data > node.data:
if not node.right.data:
node.right = Node(data)
node.right.parent = node
node = node.right
break
else:
node = node.right
else:
if not node.left.data:
node.left = Node(data)
node.left.parent = node
node = node.left
break
else:
node = node.left
node.right = self.null_node
node.left = self.null_node
self.rebalance(node)
return True

def rebalance(self, node):
if self.root_node == node or not node or node.parent == self.root_node\
or (not node.color or not node.parent.color):
return
parent = node.parent
g_parent = parent.parent
parent_dir = 'L' if g_parent.data > parent.data else 'R'
node_dir = 'L' if parent.data > node.data else 'R'
uncle = g_parent.right if parent_dir == 'L' else g_parent.left
rotate_dir = node_dir + parent_dir
if uncle == self.null_node or not uncle.color:
if rotate_dir == 'LL':
self.right_rotate(parent, recolor=True)
elif rotate_dir == 'RR':
self.left_rotate(parent, recolor=True)
elif rotate_dir == 'LR':
self.right_rotate(node)
self.left_rotate(node, recolor=True)
elif rotate_dir == 'RL':
self.left_rotate(node)
self.right_rotate(node, recolor=True)
else:
self.recolor(g_parent)

def recolor(self, g_parent):
g_parent.left.color = False
g_parent.right.color = False

if g_parent != self.root_node:
g_parent.color = True
return self.rebalance(g_parent)

def find_node(self, data):
def _find_node(node, data):
if not self.root_node or not node.data:
return None

if node.data > data:
return _find_node(node.left, data)
elif node.data < data:
return _find_node(node.right, data)
else:
return node

node = _find_node(self.root_node, data)
return node

def _find_in_order_successor(self, node):
right_node = node.right
left_node = right_node.left
if not left_node.data:
return right_node
while left_node.left.data:
left_node = left_node.left
return left_node


def remove(self, data):
remove_node = self.find_node(data)
if remove_node is None:
return
if remove_node.right.data and remove_node.left.data:
successor = self._find_in_order_successor(remove_node)
remove_node.data = successor.data
remove_node = successor
self._remove(remove_node)

def _remove(self, node):
left_node = node.left
right_node = node.right
not_null_child = right_node if not left_node.data else left_node
if node == self.root_node:
if not_null_child:
self.root_node = not_null_child
self.root_node.parent = None
self.root_node.color = False
else:
self.root_node = None
elif node.color:
self.remove_node(node)
else:
self.remove_black_node(node)

def remove_node(self, node):
if node.data >= node.parent.data:
node.parent.right = self.null_node
else:
node.parent.left = self.null_node

def remove_black_node(self, node):
self.case_1(node)
self.remove_node(node)

def case_1(self, node):
if node == self.root_node:
node.color = False
return
self.case_2(node)

def case_2(self, node):
parent = node.parent
sibling, direction = self.get_sibling(node)
if sibling.color and not parent.color and not sibling.left.color and not sibling.right.color:
if direction == 'R':
self.left_rotate(node)
else:
self.right_rotate(node)
parent.color = True
sibling.color = False
return self.case_1(node)
return self.case_3(node)

def case_3(self, node):
parent = node.parent
sibling, _ = self.get_sibling(node)
if not sibling.color and not parent.color and not sibling.left.color and not sibling.right.color:
sibling.color = True
return self.case_1(parent)
self.case_4(node)

def case_4(self, node):
parent = node.parent
if parent.color:
sibling, direction = self.get_sibling(node)
if not sibling.color and not sibling.left.color and not sibling.right.color:
parent.color, sibling.color = sibling.color, parent.color
return

self.case_5(node)

def case_5(self, node):
sibling, direction = self.get_sibling(node)
closer_node = sibling.right if direction == 'L' else sibling.left
outer_node = sibling.left if direction == 'L' else sibling.right

if closer_node.color and not outer_node.color and not sibling.color:
if direction == 'L':
self.left_rotate(closer_node)
else:
self.right_rotate(closer_node)
closer_node.color = False
sibling.color = True
self.case_6(node)

def case_6(self, node):
sibling, direction = self.get_sibling(node)
outer_node = sibling.left if direction == 'L' else sibling.right

if not sibling.color and outer_node.color:
parent_color = sibling.parent.color
if direction == 'L':
self.right_rotate(sibling)
else:
self.left_rotate(sibling)
sibling.color = parent_color
sibling.right.color = False
sibling.left.color = False
return
raise Exception('어흑 마이갓!')

def get_sibling(self, node):
parent = node.parent
if node.data >= parent.data:
sibling = parent.left
direction = 'L'
else:
sibling = parent.right
direction = 'R'
return sibling, direction

def left_rotate(self, node, recolor=None):
is_root = False
parent_direction = None
parent = node.parent
gparent = node.parent.parent if node.parent.parent else None
if not gparent:
is_root = True
else:
if gparent:
if gparent.data < parent.data:
parent_direction = True
else:
parent_direction = False
old_left = node.left
node.left = parent
parent.parent = node

parent.right = old_left
old_left.parent = parent
node.parent = gparent

if is_root:
self.root_node = node
self.root_node.color = False
else:
if parent_direction is True:
gparent.right = node
elif parent_direction is False:
gparent.left = node

if recolor:
node.color = False
node.right.color = False if node.right == self.null_node else True
node.left.color = False if node.left == self.null_node else True

def right_rotate(self, node, recolor=None):
is_root = False
parent_direction = None
parent = node.parent
gparent = node.parent.parent if node.parent.parent else None
if not gparent:
is_root = True
else:
if gparent:
if gparent.data < parent.data:
parent_direction = True
else:
parent_direction = False
old_right = node.right
node.right = parent
parent.parent = node

parent.left = old_right
old_right.parent = node.parent
node.parent = gparent

if is_root:
self.root_node = node
self.root_node.color = False
else:
if parent_direction is True:
gparent.right = node
elif parent_direction is False:
gparent.left = node

if recolor:
node.color = False
node.right.color = False if node.right == self.null_node else True
node.left.color = False if node.left == self.null_node else True


'자료구조' 카테고리의 다른 글

11. Graph  (0) 2018.12.09
10. hash  (0) 2018.12.04
8. 힙  (0) 2018.11.13
7. 트리  (0) 2018.11.11
6. 큐  (0) 2018.11.07