본문 바로가기
Python/자료구조

[python] 이진트리 / 이진 탐색트리(+백준 5639)

by 덤더리덤떰 2023. 11. 21.

1. 이진트리

  • 모든 노드의 차수가 <=2 인 트리
  • 트리 안의 모든 서브트리는 이진트리
  • 모든 노드는 왼쪽 자식 노드와 오른쪽 자식 노드 존재 ( 공백노드도 노드 )

           

1-1. 이진트리의 넓이 우선 순회 (BFS ; Breadth First Traversal)

class ArrayQueue:

    def __init__(self):
        self.data = []

    def size(self):
        return len(self.data)

    def isEmpty(self):
        return self.size() == 0

    def enqueue(self, item):
        self.data.append(item)

    def dequeue(self):
        return self.data.pop(0)

    def peek(self):
        return self.data[0]


class Node:

    def __init__(self, item):
        self.data = item
        self.left = None
        self.right = None


class BinaryTree:

    def __init__(self, r):
        self.root = r


    def bft(self):
        if(self.root==None):
            return []
        else:
            traverse=[]
            queue = ArrayQueue()
            queue.enqueue(self.root)
            while(not queue.isEmpty()):
                now= queue.dequeue()
                traverse.append(now.data)
                if(now.left):
                    queue.enqueue(now.left)
                if(now.right):
                    queue.enqueue(now.right)
                    
        return traverse

 

 

 

2. 이진 탐색 트리

출처 : &lt;어서와! 자료구조와 알고리즘은 처음이지? 강의 자료&gt;
출처 : 어서와! 자료구조와 알고리즘은 처음이지? 강의 자료

 

모든 노드에 대해서, 

           왼쪽 서브트리에 있는 데이터는 모두 현재 노드의 값보다 작고

           오른쪽 서브트리에 있는 데이터는 모두 현재 노드의 값보다 큰 성질을 만족하는 이진트리

(모든 원소는 서로 다른 유일한 키를 갖는다)

 

 

 

 

 

 

2-1. BST의 ADT

  • insert(key,data) : 트리에 주어진 데이터 원소 추가
  • remove(key) : 특정 원소를 트리로부터 삭제
  • lookup(key) : 특정 원소 검색
  • inorder() : 키의 순서대로 데이터 원소 나열
  • min(), max() : 최소 키, 최대 키 가지는 원소 각각 탐색

2-1-1. BST 구현

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

    def inorder(self):
        traversal=[]
        if self.left:
            traversal+=self.left.inorder()

        traversal.append(self)
        if(self.right):
            traversal+=self.right.inorder()

        return traversal
    
    def min(self):
        if(self.left):
            return self.left.min()
        else:
            return self
        
    def max(self):
        if(self.right):
            return self.right.max()
        else:
            return self
        
    def lookup(self,key,parent=None):
        if(key<self.key):
            if(self.left):
                return self.left.lookup(key,self)
            else:
                return None,None
        elif(key>self.key):
            if(self.right):
                return self.right.lookup(key,self)
            else:
                return None,None
            
        else:
            return self,parent
        
    def insert(self,key,data):
        if(key<self.key):
            if(self.left):
                self.left.insert(key,data)

            else:
                self.left = Node(key,data)

        elif(key>self.key):
            if(self.right):
                self.right.insert(key,data)
            else:
                self.right = Node(key,data)


        else:
            raise KeyError
        
    def countChildren(self):
        count=0
        if(self.left):
            count+=1
        if(self.right):
            count+=1

        return count


class BinSearchTree:
    def __init__(self):
        self.root = None


    def inorder(self):
        if(self.root):
            return self.root.inorder()
        
        else:
            return []


    def min(self):
        if(self.root):
            return self.root.min()
        
        else:
            return None
        

    def max(self):
        if(self.root):
            return self.root.max()
        
        else:
            return None
        
    def lookup(self,key):
        if(self.root):
            return self.root.lookup(key)
        
        else:
            return None,None
        
    def insert(self,key,data):
        if(self.root):
            self.root.insert(key,data)

        else:
            self.root=Node(key,data)

    
    def remove(self,key):
        node,parent = self.lookup(key)
        if(node):
            
            nChildren = node.countChildren()
            if(nChildren==0):
                if(parent):
                    if(parent.left ==node):
                        parent.left = None
                    elif(parent.right ==node):
                        parent.right = None
                else:
                    self.root = None
            elif(nChildren==1):
                if(node.left):
                    child= node.left
                elif(node.right):
                    child = node.right
                if(parent):
                    if(parent.left==node):
                        parent.left = child

                    elif(parent.right ==node):
                        parent.right = child
                else:
                    self.root = child
            else:
                succ_parent=node
                succ= node.left
                while(succ.right):
                    succ_parent = succ
                    succ= succ.right

                node.key = succ.key
                node.data = succ.data
                #처음부터 왼쪽 자식이 없는 경우
                if(succ_parent.left == succ):
                    succ_parent.left = succ.left
                else:
                    succ_parent.right = succ.left

            return True
        else:
            return False

 

import sys
input = sys.stdin.readline

class Node():
    def __init__(self,item):
        self.data =item
        self.left = None
        self.right = None
    
    def traverse(self):
        if(self.left):
            self.left.traverse()
        print(self.data, end=" ")
        if(self.right):
            self.right.traverse()
        
        

    def insert(self, item):
        if(item < self.data):
            if(self.left):
                self.left.insert(item)
            else:
                self.left = Node(item)
        elif(item>self.data):
            if(self.right):
                self.right.insert(item)
            else:
                self.right=Node(item)
        else:
            raise IndexError

    def search(self,item,parent=None):
        if(item<self.data):
            if(self.left):
                return self.left.search(item,self)
            else:
                print('Not Exist')
                return None,None
        elif(item>self.data):
            if(self.right):
                return self.right.search(item,self)
            else:
                print('Not Exist')
                return None,None
        else:
            print('exist')
            return self,parent

    def delete(self, item):
        delNode, parent = self.search(item)
        if(delNode):
            if(parent==None):
                self=None
            else:
                if(delNode.left ==None and delNode.right==None):
                    if(parent.left==delNode):
                        parent.left = None
                    elif(parent.right==delNode):
                        parent.right = None
                elif(delNode.left!=None and delNode.right!=None):
                    succ_parent = delNode
                    succ= delNode.right
                    while(succ.left):
                        succ_parent = succ
                        succ=succ.left
                    delNode.data= succ.data
                    if(succ_parent.right==succ):
                        succ_parent.right = succ.right
                    else:
                        succ_parent.left = succ.right
                else:
                    if(delNode.left):
                        child = delNode.left
                    elif(delNode.right):
                        child = delNode.right
                    if(parent==None):
                        self.root = child
                    else:
                        if(parent.left==delNode):
                            parent.left = child
                        else:
                            parent.right = child
                    
        else:
            print('Node is not exist')
        
        


class BinaryTree():
    def __init__(self):
        self.root = None
    def PrintTree(self):
        if(self.root):
            self.root.traverse()
        else:
            print('Empty Tree')

    def insert(self, item):
        if(self.root):
            self.root.insert(item)
        else:
            self.root=Node(item)

    def search(self,item):
        if(self.root):
            return self.root.search(item=item)
        else:
            print('Empty Tree')


    def delete(self, item):
        if(self.root):
            self.root.delete(item)
        
        else:
            print('Empty tree')

def main():
    BST= BinaryTree()
    while(True):
        print("------------------------------------")
        print("\t\t1 : 트리 출력")
        print("\t\t2 : 문자 삽입")
        print("\t\t3 : 문자 삭제")
        print("\t\t4 : 문자 검색")
        print('\t\t5 : 종료')
        ans = int(input())
        if(ans==1):
            BST.PrintTree()
            print()
        elif(ans==2):
            item = input().rstrip()
            BST.insert(item)
        elif(ans==3):
            item = input().rstrip()
            BST.delete(item)
        elif(ans==4):
            item = input().rstrip()
            BST.search(item)
         

        else:
            break




if __name__=="__main__":
    main()

2-2. BST의 시간복잡도

: 삽입,삭제,탐색의 시간 복잡도는 트리의 높이를 h라고 가정했을 때 h에 비례

: 이진 트리가 균형적으로 생성되어 있는 경우 h=log(n) => O(logN)

 

2-3. 이진 탐색 트리가 효율적이지 못한 경우

: 편향트리인 경우

: 선형탐색과 동일한 시간복잡도를 갖게됨 (=O(N))

                                 ↓

높이의 균형을 유지함으로써 O(logN)의 탐색 복잡도 보장하는 AVL트리/Red-Black Tree 존재

(추후에 다룰 예정)

 

2-4. 백준 5639 (이진 탐색 트리 문제)

https://www.acmicpc.net/problem/5639

 

5639번: 이진 검색 트리

트리를 전위 순회한 결과가 주어진다. 노드에 들어있는 키의 값은 106보다 작은 양의 정수이다. 모든 값은 한 줄에 하나씩 주어지며, 노드의 수는 10,000개 이하이다. 같은 키를 가지는 노드는 없다

www.acmicpc.net

2-4-1. 구현

import sys
sys.setrecursionlimit(10**5)

#input은 EOF를 받을 때 EOFError
#sys.stdin.readline은 EOF를 받을 때 빈문자열 리턴
#위 코드로 입력 받으면 입력 후 엔터가 줄 바꿈 기호로 입력됨
input = sys.stdin.readline

class Node():
    def __init__(self,item):
        self.data = item
        self.left = None
        self.right = None
    
    def postorder(self):
        if(self.left):
            self.left.postorder()

        if(self.right):
            self.right.postorder()

        print(self.data)

    def insert(self,item):
        if(item<self.data):
            if(self.left):
                self.left.insert(item)
            else:
                self.left = Node(item)

        elif(item>self.data):
            if(self.right):
                self.right.insert(item)
            else:
                self.right = Node(item)
            
        else:
            raise IndexError




class BinaryTree():
    def __init__(self):
        self.root = None
    
    def postorder(self):
        if(self.root):
            self.root.postorder()
        else:
            pass
    
    def insert(self,item):
        if(self.root):
            self.root.insert(item)
        else:
            newNode = Node(item)
            self.root = newNode


def main():
    BST = BinaryTree()
    while(True):
        try:
            item = int(input())
            BST.insert(item)
        except:
            break

    BST.postorder()

if __name__=="__main__":
    main()

 

2-4-2. EOFError

: "End of File Error"의 약자로 파일을 읽을 때 파일의 끝을 만나면 발생하는 에러

( 더이상 읽을 내용이 없어서 발생하는 에러 ) 

 

  • input()은 EOFError 발생시킴
  • sys.stdin.readline()은 빈 문자열 반환

=> 엔터 들어올 때까지 입력받게 하고자 한다면, 아래와 같이 코드 작성

#input()
while True:
	try:
    	data= input()
    except EOFError:
    	break

#sys.stdin.readline()
import sys
input = sys.stdin.readline

while True:
	temp =input()
    if temp == "":
    	break
        
        
#참고로 문자열을 int()로 형변환 해주면 개행문자는 사라지고 정수형태만 남는다

 

cf> [파이썬 에러] ValueError: invalid literal for int() with base 10

-> 구현 부분에서 try-exception 처리 안하면 발생하는 에러 ( 빈 문자열 int로 변환하는 것이기에)

-> 파이썬에서 데이터 형 변환 시 발생하는 에러

-> input으로 받은 값이 실수 문자형일 경우 int() 함수가 받을 값이 아니기에 float형으로 변환 후 int()함수 사용해야함

int() 정수 형태의 문자열 (str int)
실수 값 (float   int)
float() 실수/정수 형태의 문자열/정수 (str/int float)
str() 정수/실수 값 (int/float str)

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

[python] LinkedList-bubble sort  (0) 2024.03.27
[python] 우선순위 큐  (2) 2023.11.20
[python] dummy node 활용한 Linked List  (0) 2023.11.14