CS/자료구조_개념

[자료구조] 트리(Tree)

Jedy_Kim 2021. 5. 30. 22:08
728x90

이미지 출처 : https://python.plainenglish.io/data-structures-tree-29c825760095

- Node와 Branch를 이용해서 사이클을 이루지 않도록 구성한 데이터 구조

- 이진트리 : 노드의 최대 Branch가 2인 트리

- 이진탐색트리(Binary Search Tree, BST) : 왼쪽 노드는 해당 노드보다 작은 값, 오른쪽 노드는 해당 노드보다 큰 값을 가진다.

 

#코드

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
class Node:
    def __init__ (self, value):
        self.value = value
        self.left  = None
        self.right = None
 
class NodeMgmt:
    def __init__(self, head):
        self.head = head
    
    def insert(self, value):
        self.current_node = self.head
        while True:
            if value < self.current_node.value: # 찾고자하는 value값이 작은 경우 : 왼쪽 
                if self.current_node.left != None# 왼쪽에 자식이 있을 경우
                    self.current_node = self.current_node.left # 왼쪽으로 계속 이동한다.
                else# 자식이 없을 경우
                    self.current_node.left = Node(value) #노드생성
                    break
            else# 찾고자하는 value값이 큰 경우 : 오른쪽
                if self.current_node.right != None# 오른쪽에 자식이 있을 경우
                    self.current_node = self.current_node.right
                else# 자식이 없을 경우
                    self.current_node.right = Node(value) #노드생성
                    break
 
    def search(self, value):
        self.current_node = self.head
        while self.current_node: # self.head에 값이 있을 경우
            if self.current_node.value == value: # 찾고자하는 값과 일치할 경우
                return value
            elif self.current_node.value > value: # 왼쪽 탐색
                self.current_node = self.current_node.left
            else# 오른쪽 탐색
                self.current_node = self.current_node.right
        return False
 
    '''
    설계
    1) Leaf Node삭제 : 해당 노드를 삭제하고 해당 노드의 부모노드가 가르키는 값을 None으로 초기화
    2) Child Node가 하나인 Node삭제 : 해당 노드를 삭제하고 해당 노드의 브랜치가 삭제 노드의 자식 노드를 가르킨다.
    3) Child Node가 두개인 Node삭제 : 
       - 삭제할 노드의 오른쪽 자식 중, 가장 작은 값을 삭제할 노드의 부모 노드가 가르키도록 한다.
                                            or 
       - 삭제할 노드의 왼쪽 자식 중, 가장 큰 값을 삭제할 노드의 부모 노드가 가크리도록 한다.
 
    전략
    1. 삭제할 노드의 오른쪽 자식 선택
    2. 오른쪽 자식의 가장 왼쪽에 있는 노드를 선택
    3. 해당 노드를 삭제할 노드의 부모 노드의 왼쪽 브랜치가 가르키게함.
    4. 해당 노드의 왼쪽 브랜치가 삭제할 노드의 왼쪽 자식노드를 가르키게함.
    5. 해당 노드의 오른쪽 브랜치가 삭제할 노드의 오른쪽 자식노드를 가르키게함.
    6. 만약 해당 노드의 오른쪽 자식노드가 있을 경우, 
    해당 노드의 부모노드의 왼쪽 브랜치가 해당노드 오른쪽 자식노드를 가르키게함.    
    '''
    def delete(self, value):
        searched = False # 삭제할 노드가 없는 경우도 처리해야함.
        # 삭제할 노드 탐색
        self.current_node = self.head
        self.parent_node  = self.head
        while self.current_node: 
            if self.current_node.value == value:
                searched = True
                break
            elif value < self.current_node.value:
                self.parent_node = self.current_node
                self.current_node = self.current_node.left
            else:
                self.parent_node = self.current_node
                self.current_node = self.current_node.right
        if searched == False:  
            return False # 삭제하고자 하는 값이 없는 경우 종료한다.
        # case 1 : 삭제할 노드가 leaf node인 경우 
        if self.current_node.left == None and self.current_node.right == None:
            if value < self.parent_node.value:
                self.parent_node.left = None
            else:
                self.parent_node.right = None
            del self.current_node
        # case 2 : 삭제할 노드가 child node를 한 개 가지고 있는 경우
        if self.current_node.left != None and self.current_node.right == None# 왼쪽 한 개
            if value < self.parent_node.value:
                self.parent_node.left = self.current_node.left
            else:
                self.parent_node.right = self.current_node.left
        elif self.current_node.left == None and self.current_node.right != None# 오른쪽 한 개
            if value < self.parent_node.value:
                self.parent_node.left = self.current_node.right
            else:
                self.parent_node.right = self.current_node.right
        # case 3 : 삭제할 노드가 자식노드를 두 개 가지고 있는 경우
        if self.current_node.left != None and self.current_node.right != None:
            # case 3-1 : 삭제할 노드가 부모노드의 왼쪽
            if value < self.parent_node.value: 
                self.change_node = self.current_node.right  
                self.change_node_parent = self.current_node.right
                while self.change_node.left:
                    self.change_node_parent = self.change_node
                    self.change_node = self.change_node.left
                # case 3-1-1 : 삭제할 노드가 부모 노드의 왼쪽에 있고, 
                #              삭제할 노드의 오른쪽 자식 중 가장 작은 값을 가진 노드의 오른쪽 자식노드가 있을 때        
                if self.change_node.right != None:
                    self.change_node_parent.left = self.change_node.right
                # case 3-1-2 : 삭제할 노드가 부모노드의 왼쪽에 있고, 
                #              삭제할 노드의 오른쪽 자식 중 가장 작은 값을 가진 노드의 자식이 없을 때
                else:
                    self.change_node_parent.left = None
                ###### 실질적 교체
                self.parent_node.left = self.change_node
                self.change_node.left = self.current_node.left
                self.change_node.right = self.current_node.right
            # case 3-2 : 삭제할 노드가 부모노드의 오른쪽
            else:
                self.change_node = self.current_node.right
                self.change_node_parent = self.current_node.right
                while self.change_node != None:
                    self.change_node_parent = self.change_node
                    self.change_node = self.change_node.left                
                # case 3-2-1 : 삭제할 노드가 부모 노드의 오른쪽에 있고, 
                #              삭제할 노드의 오른쪽 자식 중 가장 작은 값을 가진 노드의 자식 노드가 있을 때
                if self.change_node.right != None:
                    self.change_node_parent.left = self.change_node.right 
                # case 3-2-2 : 삭제할 노드가 부모 노드의 오른쪽에 있고, 
                #              삭제할 노드의 오른쪽 자식 중 가장 작은 값을 가진 노드의 자식 노드가 없을 때
                else:
                    self.change_node_parent.left = None
                ###### 실질적 교체
                self.parent_node.right = self.change_node
                self.change_node.left = self.current_node.left
                self.change_node.right = self.current_node.right
        return True
                
        
 
if __name__ == "__main__":
    head = Node(1)
    bst =NodeMgmt(head)
    bst.insert(2)
    bst.insert(3)
    res = bst.search(2)
    if res != False:        
        print(res)
    else:
        print('찾고자하는 값이 없습니다.')
 
    res = bst.delete(2)
    print(res)
cs

# 시간복잡도 : O(logN)

 

# 전위/중위/후위 순회(복습)

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import sys 
 
def preorder(x):
  # x를 루트로 하는 서브트리를 전위순회 하여 출력하는 함수.
  # 기저조건 : 노드의 자식 노드가 없을 때
  if myTree[x][0== -1 and myTree[x][1== -1:
    print(x, end=' ')
  else:
    # Root -> Left -> Right
    print(x, end=' ')
    preorder(myTree[x][0])
    preorder(myTree[x][1])
 
def inorder(x):
  if myTree[x][0== -1 and myTree[x][1== -1:
    print(x, end=' ')
  else:
    inorder(myTree[x][0])
    print(x, end=' ')
    inorder(myTree[x][1])
    
def postorder(x):
  if myTree[x][0== -1 and myTree[x][1== -1:
    print(x, end=' ')
  else:
    postorder(myTree[x][0])
    postorder(myTree[x][1])
    print(x, end=' ')
  
 
if __name__ == "__main__":
  input = sys.stdin.readline
  
  n = int(input())
  # myTree[i] = 노드 i의 정보를 담고 있다.
  # myTree[i][0] = 노드 i의 왼쪽 노드 번호.
  # myTree[i][1] = 노드 i의 오른쪽 노드 번호.
  
  myTree = [[] for _ in range(n+1)]
  
  for _ in range(n):
    a, b, c = map(int, input().split())
    myTree[a].append(b) 
    myTree[a].append(c)
  
  
  preorder(1)
  print()
  inorder(1)
  print()
  postorder(1)
cs
입력
5
1 2 3
2 4 5
3 -1 -1
4 -1 -1
5 -1 -1

출력
1 2 4 5 3 
4 2 5 1 3 
4 5 2 3 1 

 

# 문제

https://ttolkist.tistory.com/155

 

트리 순회 결과 출력하기

문제 루트가 0인 이진트리가 주어질 때, 이를 전위순회, 중위순회, 후위순회한 결과를 각각 출력하는 프로그램을 작성하시오. 입력 첫 번째 줄에 트리의 노드 개수 n이 주어진다. ( 1 ≤ n ≤ 100 )

ttolkist.tistory.com

 

반응형