본문 바로가기

코테준비

알고리즘 응용 - 완전 탐색

순열(nPm) - 서로 다른 n개의 원소 중 m개를 순서를 고려하여 중복 없이 골라낸 것

ex) 6P3

#nPm 구현
#used = num이 이미 사용되었는지 확인하는 리스트
#answer = 현재 조합된 원소들을 저장하는 리스트
answer = [0,0,0] #m개의 정답을 저장
used = [False,False,False,False,False,False] #n개의 원소 사용 여부
m=3
n=6
def permutation(idx):
    global n,m
    if(idx==m):
        print(answer[:m])
        return
    for num in range(n):
        if used[num]:
            continue
        answer[idx] = num
        used[num] = True
        permutation(idx+1)
        used[num] = False
        
permutation(0)

 

조합(nCr) - 서로 다른 n개의 원소 중 r개를 순서 없이 중복을 제외하고 골라낸 것

#nCr 구현
#used = num이 이미 사용되었는지 확인하는 리스트
#answer = 현재 조합된 원소들을 저장하는 리스트
answer = [0,0,0]
used = [False,False,False,False,False,False]
r=3
n=6
def combination(idx,start):
    global n,r
    if idx == r:
        print(answer)
        return
    for i in range(start,n):
        if used[i]:
            continue
        answer[idx] = i
        used[i] = True
        combination(idx+1, i+1)
        used[i] = False
combination(0,0)

중복 조합은 combination(idx+1, i)로 재귀호출을 하면 된다.

 

부분집합

원소 n개를 가지고 만들 수 있는 모든 집합(멱집합)

일반적으로 각 원소마다 r개의 선택지가 있을 때, O(n^r)이라는 매우 큰 수가 된다.

#1~4의 값이 있을 때, 모든 부분집합 만들기
nums = ["1","2","3","4"]
def allCase(idx,answer):
	#기저조건
    if idx == len(nums):
        print(answer)
        return
    #원소를 사용하는 경우
    allCase(idx+1,answer)
    #원소를 사용하지 않는 경우
    allCase(idx+1,answer+nums[idx])
allCase(0,"")
#결과

4
3
34
2
24
23
234
1
14
13
134
12
124
123
1234

 

비트연산

원소의 개수가 약 12개정도 이하일 경우 비트마스킹과 비트연산자를 이용해 최적화할 수 있다.

#nPm 구현
#used = num이 이미 사용되었는지 확인하는 리스트
#answer = 현재 조합된 원소들을 저장하는 비트마스크
answer = [0,0,0] #m개의 정답을 저장
used = 0 #n개의 원소 사용 여부
m=3
n=6
def permutation(idx):
    global n,m,used
    if(idx==m):
        print(answer[:m])
        return
    for num in range(n):
        if (1<<num)&used:
            continue
        answer[idx] = num
        used |= 1<<num
        permutation(idx+1)
        used &= ~(1<<num)
        
permutation(0)

 

 

 

DFS

트리 형태의 자료구조에서 가장 깊은 자식 노드부터 탐색하는 방법

재귀 함수 형태로 구현하는 방법과 stack 자료구조를 활용하는 방법이 있다.

#dfs의 경우 0-1-3-4-2-5 순서로 노드를 방문하면 된다
graph = {0:[1,2],1:[3,4],2:[5],3:[],4:[],5:[]}

def dfs(node):
    print(node)
    for n in graph[node]:
        dfs(n)
        
dfs(0)

stack = [0]
while stack:
    node = stack.pop()
    print(node)
    for n in graph[node][::-1]: #dfs()와 비교를 위해 reverse만 해줌
        stack.append(n)

 

BFS

트리 형태의 자료구조에서 depth 크기 별로 자식 노드들을 탐색하는 방법

queue 자료구조를 활용해 풀 수 있다.

#bfs의 경우 0-1-2-3-4-5 순서로 노드를 방문하면 된다
graph = {0:[1,2],1:[3,4],2:[5],3:[],4:[],5:[]}

queue = [0]
def bfs(queue):
    if not queue:
        return
    node = queue.pop(0)
    print(node)
    for n in graph[node]:
        queue.append(n)
    bfs(queue)
    
bfs(queue)
    
queue=[0]
while queue:
    node = queue.pop(0)
    print(node)
    for n in graph[node]:
        queue.append(n)

 

다만 DFS와 BFS는 문제의 형태에 따라 재귀함수나 자료구조 둘 중 하나를 이용해 표현하기 어려운 경우가 있다. 두 가지 방법을 모두 알아두고 필요에 따라 적용하는 것이 좋다.

'코테준비' 카테고리의 다른 글

그리디 알고리즘  (0) 2024.02.13
자바 - 순열/조합 generator 구현하기  (0) 2024.02.08
연결리스트  (0) 2024.02.05
[Java_코테] 유용한 함수들  (0) 2024.01.15
[Java_코테]입출력  (0) 2024.01.12