DFS/BFS

송현준·2022년 11월 22일
0

1. 자료구조 기초

탐색(Search)이란

  • 많은 양의 데이터 중에서 원하는 데이터를 찾는 과정

자료구조(Data Structure)란

  • 데이터를 표현하고 관리하고 처리하기 위한 구조
  • 그중 스택과 큐는 자료구조의 기초 개념으로 다음의 두 핵심적인 함수로 구성
    • 삽입(Push) : 데이터를 삽입한다.
    • 삭제(pop) : 데이터를 삭제한다.
  • 오버플로(Overflow) : 특정한 자료구조가 수용할 수 있는 데이터의 크기를 이미 가득 찬 상태에서 삽입 연산을 수행할 때 발생
  • 언더플로(Underflow) : 특정한 자료구조에 데이터가 전혀 들어 있지 않은 상태에서 삭제 연산을 수행할 때 발생

스택

  • 선입후출(First In Last Out) 구조 또는 후입선출(Last In First Out) 구조

  • 스택 예제

    stack = []
    
    # 삽입(5) - 삽입(2) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
    stack.append(5)
    stack.append(2)
    stack.append(3)
    stack.append(7)
    stack.pop()
    stack.append(1)
    stack.append(4)
    stack.pop()
    
    print(stack) # 최하단원소부터 출력
    print(stack[::-1] # 최상단 원소부터 출력

    [5, 2, 3, 1]
    [1, 3, 2, 5]

  • 선입선출(First In First Out) 구조

  • 큐 예제

    from collections import deque
    
    # 큐(Queue) 구현을 위해 deque 라이브러리 사용
    queue = deque()
    
    # 삽입(5) - 삽입(2) - 삽입(3) - 삽입(7) - 삭제() - 삽입(1) - 삽입(4) - 삭제()
    queue.append(5)
    queue.append(2)
    queue.append(3)
    queue.append(7)
    queue.popleft()
    queue.append(1)
    queue.append(3)
    queue.popleft()
    
    print(queue)    # 먼저 들어온 순서대로 출력
    queue.reverse() # 다음 출력을 위해 역순으로 바꾸기
    print(queue)    # 나중에들어온 원소부터 출력

    deque([3, 7, 1, 4])
    deque([4, 1, 7, 3])

재귀 함수

  • 자기 자신을 다시 호출하는 함수

  • 재귀 함수 예제

    def recursive_function() :
    	print('재귀 함수를 호출합니다.')
        recursive_function()
    
    recursive_function()
       
    • 이 코드를 실행하면 '재귀 함수를 호출합니다.'라는 문자열을 무한히 출력
      recursive_function()이 자기 자신을 계속해서 추가로 불러오기 때문
      어느 정도 출력 하다가 다음과 같은 오류 메시지를 출력
    • recursionerror maximum recursion depth exceeded while pickling an object
      이 오류 메시지는 재귀(Recursion)의 최대 깊이를 초과했다는 내용이다.
      보통 파이썬 인터프리터는 호출 횟수제한이 있는데 이 한계를 벗어났기 때문, 따라서 무한대로 재귀 호출을 진핼할 수는 없다.

재귀 함수의 종료 조건

  • 재귀 함수를 문제 풀이에서 사용할 때는 재귀 함수가 언제 끝날지, 종료 조건을 꼭 명시해야 한다.

  • 재귀 함수 종료 예제

    def recursive_function(i) :
    	# 100번째 출력했을 때 종료되도록 종료 조건 명시
        if i == 100 :
        	return
        print(i, '번째 재귀 함수에서', 'i + 1, '번째 재귀 함수를 호출합니다.
        recursive_function(i + 1)
        print(i, '번째 재귀 함수를 종료합니다.')
        
     recursive_function(1)
  • 컴퓨터 내부에서 재귀 함수의 수행은 스택 자료구조를 이용
    함수를 계속 호출했을 때 가장 마지막에 호출한 함수가 먼저 수행을 끝내야 그 앞의 함수 호출이 종료되기 때문이다.
    컴퓨터의 구조 측면에서 보자면 연속해서 호출되는 함수는 메인 메모리의 스택 공간에 적재되므로 재귀 함수는 스택 자료구조와 같다는 말은 틀린 말이 아니다.

  • 재귀함수는 내부적으로 스택 자료구조와 동일하다
    따라서 스택 자료구조를 활용해야 하는 상당수 알고리즘은 재귀 함수를 이용해서 간편하게 구현될 수 있다. DFS가 대표적인 예이다.

  • 재귀 함수를 이용하는 대표적 예제 팩토리얼(Factorial)
    2가지 방식으로 구현한 팩토리얼 예제

# 반복적으로 구현한 n!
def factorial_iterative(n) :
	result = 1
    # 1부터 n까지의 수를 차례대로 곱하기
    for i in range(1, n + 1) :
    	result *= i
    return result
    
# 재귀적으로 구현한 n!
def factorial_recursive(n) :
	if <= 1 :  # n이 1 이하인 경우 1을 반환
    	return 1
    # n! = n * (n -1) !를 그대로 코드로 작성하기
    return n * factorial_recursive(n - 1)
    
# 각각의 방식으로 구현한 n! 출력(n = 5)
print('반복적으로 구현 : ', factorial_iterative(5))
print('재귀적으로 구현 : ', factorial_recursive(5))

반복적으로 구현 : 120
재귀적으로 구현 : 120

  • 재귀 함수를 사용했을 때 얻을 수 있는 장점
    • 재귀 함수의 코드가 더 간결하다.
      이유는 재귀 함수가 수학의 점화식(재귀식)을 그대로 소스코드로 옮겼기 때문

출처 : 이것이 코딩테스트다 with 파이썬

profile
노력중

0개의 댓글