[코딩테스트] DFS, BFS 자료 구조 기초

민갱·2023년 9월 21일
0

코딩테스트

목록 보기
5/16

탐색(Search)란, 많은 양의 데이터 중에서 원하는 데이터를 찾는 과정을 의미한다.
프로그래밍에서는 그래프, 트리 등의 자료구조 안에서 탐색을 하는 문제를 자주 다룬다. 대표적인 탐색 알고리즘으로 DFS, BFS를 꼽을 수 있는데, 이 두 알고리즘의 원리를 제대로 이해해야 코딩 테스트의 탐색 문제 유형을 풀 수 있다.

DFS와 BFS를 제대로 이해하려면 기본 자료구조인 스택과 큐에 대한 이해가 전제 되어야하므로, 사전 학습으로 스택과 큐, 재귀 함수를 간단히 정리해보자.

자료구조(Data Structure)란, 데이터를 표현하고 관리하고 처리하기 위한 구조를 의미한다.
그중 스택과 큐는 자료구조의 기초 개념으로 두 핵심 함수로 구성된다.

  • 삽입(Push) : 데이터를 삽입한다.
  • 삭제(Pop) : 데이터를 삭제한다.

그 외에도, 삽입,삭제 오버플로, 언더 플로를 고민해야한다.

  • 오버플로 : 특정한 자료구조가 수용할 수 있는 데이터의 크기를 이미 가득 찬 상태에서 삽입 연산을 수행할 때 발생한다. 즉, 저장 공간을 벗어나 데이터가 넘쳐흐를 때 발생한다.
  • 언더플로 : 자료구조에 데이터가 전혀 들어 있지 않은 상태에서 삭제 연산을 수행하면 데이터가 전혀 없는 상태 이므로 언더플로가 발생한다.

스택

스택은 박스 쌓기에 비유할 수 있다. 이러한 구조를
선입후출(First In Last Out), 후입선출(Last In First Out) 구조라 한다.

파이썬에서는 스택을 이용할 때에는 별도의 라이브러리를 사용할 필요가 없다. 기본 리스트에서 append() 와 pop() 메서드를 이용하면, 스택 자료구조와 동일하게 동작한다.

  • append() : 리스트 가장 뒤쪽에 데이터를 추가한다.
  • pop() : 리스트 가장 뒤쪽에서 데이터를 꺼낸다.
stack =[]
stack.append(5)
stack.append(2)
stack.append(3)
stack.append(7)
stack.pop()
stack.append(1)

print(stack)
print(stack[::-1]) # 뒤집어서 출력

출력 ==>
[5,2,3,1]
[1,3,2,5]

큐는 대기줄에 비유할 수 있다. 이러한 구조를
선입선출(First In First Out) 구조라고 한다. 공정한 자료구조라고 비유된다.
큐는 collecions 모듈 안에 deque 라이브러리를 사용한다.
pop() 대신 popLeft()를 사용한다.

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(4)
queue.popLeft()

print(queue)
quere.reverse()
print(queue)

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

파이썬으로 큐를 구현할 때는 collections 모듈에서 제공하는 deque 자료구조를 활용하자.
deque는 스택과 큐의 장점을 모두 채택한 것인데, 데이터를 넣고 배는 속도가 리스트 자료형에 비해 효율적이며 queue 라이브러리를 이용하는 것보다 더 간단하다.
list(queue)를 하면 리스트 자료형이 반환된다.

재귀함수

DFS와 BFS를 구현하려면 재귀 함수를 이해해야한다.
재귀함수란, 자기 자신을 다시 호출하는 함수를 의미한다.

재귀함수의 종료 조건

재귀 함수를 문제 풀이에서 사용할 때에는 재귀 함수가 언제 끝날지, 종료 조건을 꼭! 명시해야한다.
자칫 종료 조건을 명시하지 않으면 함수가 무한히 호출될 수 있다.

예를 들어 다음은 재귀함수를 10번 호출하도록 작성한 코드이다. 재귀 함수 초반에 등장하는 if문이 종료 조건 역할을 한다.


def recursive_func(i):
	if i == 10:
	    return
        
    print(i, '번째 재귀 함수에서', i+1, '번째 재귀 함수를 호출한다.')
    recursive_func(i+1)
    print(i, '번째 재귀함수를 종료합니다.')
    
출력 ====>
1 번째 재귀 함수에서 2 번째 재귀 함수를 호출한다.
2 번째 재귀 함수에서 3 번째 재귀 함수를 호출한다.
3 번째 재귀 함수에서 4 번째 재귀 함수를 호출한다.
4 번째 재귀 함수에서 5 번째 재귀 함수를 호출한다.
5 번째 재귀 함수에서 6 번째 재귀 함수를 호출한다.
6 번째 재귀 함수에서 7 번째 재귀 함수를 호출한다.
7 번째 재귀 함수에서 8 번째 재귀 함수를 호출한다.
8 번째 재귀 함수에서 9 번째 재귀 함수를 호출한다.
9 번째 재귀 함수에서 10 번째 재귀 함수를 호출한다.
9 번째 재귀함수를 종료합니다.
8 번째 재귀함수를 종료합니다.
7 번째 재귀함수를 종료합니다.
6 번째 재귀함수를 종료합니다.
5 번째 재귀함수를 종료합니다.
4 번째 재귀함수를 종료합니다.
3 번째 재귀함수를 종료합니다.
2 번째 재귀함수를 종료합니다.
1 번째 재귀함수를 종료합니다.

컴퓨터 내부에서 재귀 함수의 수행은 수택 자료구조를 이용한다.
함수를 계속 호출했을 때 가장 마지막에 호출한 함수가 먼저 수행을 끝내야 그 앞의 함수 호출이 종료 되기 때문이다.
스택 자료구조를 활용해야 하는 상당수 알고리즘은 재귀 함수를 이용해서 간편하게 구현될 수 있다.
DFS가 대표적 예이다.

재귀 함수를 이용하는 대표적 예제로는 팩토리얼이 있다.


def fact_iterative(n):
	result = 1
    for i in range(1,n+1):
    	result *= i
	return result
    
def fact_recursive(n):
	if n <= 1:
    	return 1
	return n * fact_recursive(n-1)

print('반복문 구현 : ',fact_iterative(5))
print('재귀 구현 : ',fact_recursive(5))

이것이 취업을 위한 코딩테스트다 (저서 : 나동빈)

profile
가보자고

0개의 댓글