lst 라는 이차원 행렬에서 3개를 조합으로 뽑고 싶을 때

중복되는 경우 없이 뽑고 싶다면 

row, col을 기억하면서 같은 row일 때는 해당 col보다 작은 값들은 보지 않도록 하면 된다.

(같은 row에서 해당 col보다 작은 값들은 이미 본 것이므로)

lst = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
check = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
n, m = 3, 3


def combination(count, r, c):
    global ret  # 조합 갯수 세기
    if count == 3:
        print(selected)
        ret += 1
        return
    for i in range(r, n):
        if i == r:  # 해당 row일 때는 col보다 작은 값은 보지 않기
            c2 = c
        else:
            c2 = 0
        for j in range(c2, m):
            if check[i][j] == 0:
                check[i][j] = 1
                selected.append(lst[i][j])
                combination(count+1, i, j)
                check[i][j] = 0
                selected.pop()
ret = 0
selected = []

combination(0, 0, 0)
print(ret)

 

'알고리즘 > 백트래킹' 카테고리의 다른 글

14888. 연산자 끼워넣기  (0) 2019.11.28
2580. 스도쿠  (0) 2019.11.27
9663. N-Queen  (0) 2019.11.26
15650. N과 M (2)  (0) 2019.11.22
15649. N과 M(1)  (0) 2019.11.22

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

 

 

DFS로도 풀 수 있지만, 플로이드-워셜 알고리즘으로 풀리는 문제.

플로이드 워셜 알고리즘이란 

정점 i 에서 정점 j 로 가는 경로가 있는지 확인하고 싶을 때,

 

i 에서 j 로 가는 직통 길이 있니? matrix[i][j] == 1? 

k ( k = 모든 정점 ) 를 지나쳐 가는 길이 있니? matrix[i][k] == 1 and matrix[k][j] == 1?

 

를 확인하는 알고리즘. k에는 0부터 마지막 정점까지 모든 정점을 하나씩 대입한다.

플로이드 워셜 알고리즘에서는 최단 거리를 발견할 때마다 업데이트하지만

여기서는 최단 거리가 아니라 가는 경로 유무 1, 0으로 만 확인하면 된다.

 

 

'알고리즘 > BFS와 DFS' 카테고리의 다른 글

11724번. 연결 요소의 개수  (0) 2019.12.24
14502번. 연구소  (0) 2019.12.20
2206번. 벽 부수고 이동하기  (0) 2019.12.18
7576번. 토마토  (0) 2019.12.17
1012번. 유기농 배추  (0) 2019.12.13

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

 

 

1. visited를 따로 사용할 필요가 없다. 거리를 나타내는 dist 배열이 0 이상이면 방문한 적이 있다는 뜻이기 때문.

2. dist에 방문 여부를 표시하되, 지금까지 벽을 부순 적 있는 경우와 없는 경우를 따로 체크해야 한다.

벽을 부수지 않고 오다가, 어떤 벽을 만났는데 다른 경로가 한번 부숴서 지나갔다고 거기서 막히면 안 된다. 벽은 각 '경로마다' 한 번씩 부술 수 있기 때문이다. 벽이 아닌 길을 만났을 때도 여기까지 '벽을 부수면서 온 경로'만 방문했다면 이번 경로는 '벽을 부수지 않고 온 유일한 경로'이다.

3. 모든 길을 모두 방문하지 않고 중간에 목적지를 만났으면 리턴하는 것이 좋다. 벽을 부수면서 도착했건, 부수지 않으면서 도착했건 일단 도착했으면 제일 먼저 도착한 것이므로 바로 리턴하면 된다. 또다른 경우를 상정하지 않는다.

 

아, 벽이 0이나 1이 아니라 '0'이나 '1'임에 유의하자.

import sys
import collections


def bfs(start):
    queue = collections.deque()
    queue.append(start)
    dist[start[0]][start[1]][0] = 1

    while queue:
        r, c, wallBreak = queue.popleft()  # wallBreak=지금껏 벽을 부순 적이 있는가?
        if r == n-1 and c == m-1:
            return dist[r][c][wallBreak]  # BFS는 처음 목적지에 도착하면 그것이 최단경로
        for nR, nC in [[r+1, c], [r-1, c], [r, c+1], [r, c-1]]:
            if 0 <= nR < n and 0 <= nC < m:
                if dist[nR][nC][wallBreak] > 0:  # 이전에 같은 상태에서 방문한 적이 있으면 방문하지 않음
                    continue
                if board[nR][nC] == '0':  # 벽이 뚫려있으면
                    dist[nR][nC][wallBreak] = dist[r][c][wallBreak] + 1  # 단순하게 부모 노드+1
                    queue.append([nR, nC, wallBreak])
                elif board[nR][nC] == '1' and wallBreak == 0:  # 벽이 막혀 있고, 이전에 뚫고 온 적이 없으면
                    dist[nR][nC][1] = dist[r][c][wallBreak] + 1
                    queue.append([nR, nC, 1])
    return -1


n, m = map(int, sys.stdin.readline().split())
board = []
for _ in range(n):
    board.append(sys.stdin.readline().rstrip())
dist = [[[0, 0] for _ in range(m)] for _ in range(n)] # [non-break distance, break distance]

print(bfs([0, 0, 0]))  # 큐의 초기값. row, col, break_or_not

 

'알고리즘 > BFS와 DFS' 카테고리의 다른 글

14502번. 연구소  (0) 2019.12.20
11403번. 경로 찾기  (0) 2019.12.19
7576번. 토마토  (0) 2019.12.17
1012번. 유기농 배추  (0) 2019.12.13
2667번. 단지번호붙이기  (0) 2019.12.13

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

 

 

BFS로 푸는 문제이다.

처음 접근방법은 처음부터 익은 토마토 리스트를 하나 만든 후에, 그 중 하나가 주변을 익힐 수 있을 만큼 모두 익히게 하고,

그 다음 처음부터 익은 토마토가 또 주변을 익힐 수 있게 하되, 더 이른 날짜에 익을 수 있으면 날짜를 교체하도록 했다.

그렇게 하니 보기 좋게 시간초과가 생겼고, 날짜를 그렇게 변경해나가는 것은 BFS가 아니라는 질문 글을 발견했다.

도착한 순간 최단 경로가 확정되어야 한다는 것이다. 즉, 재방문 하는 일이 없어야 한다.

잘못 생각했던 점은 한 토마토의 주변을 일단 모두 익게 하려고 했다는 점이다.

익은 각각의 토마토들의 상하좌우를 각각 한번씩만 익힌 후 그것을 모두 큐에 넣고, 날짜를 하나 증가시킨 후 또다시 각각의 상하좌우를 큐에 넣고...하는 식으로 하면 된다.

소스코드 상으로는 처음부터 익은 토마토를 큐의 초기값으로 잡으면 된다.

 

채점한 코드 중에 중요한 함수만 가지고 왔다.

def start_ripe_tomato():
    queue = collections.deque()
    queue.extend(initiate_tomato())  # 처음에 1인 토마토 리스트

    while queue:
        r, c = queue.popleft()
        for nR, nC in [[r + 1, c], [r - 1, c], [r, c + 1], [r, c - 1]]:  # 상하좌우
            if 0 <= nR < n and 0 <= nC < m:
                if box[nR][nC] == 0:  # 아직 안 익었다면
                    queue.append([nR, nC])  # 큐에 추가. 큐에는 방금 익은 리스트가 들어감
                    box[nR][nC] = 1  #  익음 체크
                    days[nR][nC] = days[r][c] + 1

2) 큐를 사용하기 위해서 collections의 덱을 사용한다.

3) initiate_tomato()는 처음에 주어진 입력에서 1인 토마토들을 모두 리스트에 저장해 반환하는 함수이다. queue에는 방금 익은 토마토들이 들어갈 리스트인데, 초기값으로는 처음부터 익어 있었던 (1이었던) 토마토들을 방금 익은 것이라고 가정하고 넣는다.

5) 방금 익은 토마토(queue)들이 없을 때까지 진행한다. 방금 익은 토마토가 없다면 더이상 진행이 불가능할 것이기 때문이다.

6~8) 각각의 토마토들의 상하좌우를 가져와서 out of index 체크를 한다.

9) 토마토가 아직 익지 않은 상태라면, 그것을 익히고 (11번줄), 방금 익은 토마토 리스트에 추가한다. (10번줄)

12) days는 각각의 토마토가 익을 때까지 걸리는 일수를 저장한 이차원 리스트인데, 부모 토마토가 익은 날짜+1이 된다.

 

다시 와서 생각해보면 BFS에서 크게 벗어나지 않은 문제인데, 미로찾기처럼 생각해서 헤맨 문제같다.

 

 

 

'알고리즘 > BFS와 DFS' 카테고리의 다른 글

11403번. 경로 찾기  (0) 2019.12.19
2206번. 벽 부수고 이동하기  (0) 2019.12.18
1012번. 유기농 배추  (0) 2019.12.13
2667번. 단지번호붙이기  (0) 2019.12.13
2606번. 바이러스  (0) 2019.12.13

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

 

 

 

BFS와 DFS를 이해하고

2667번을 풀어봤다면 오히려 더 쉬운 문제이지만,

m와 n, x와 y, 가로와 세로, 행과 열에 있어서 헷갈렸던 문제...

import sys


def DFS(row, col):
    stack = []
    stack.append([row, col])

    while stack:
        r, c = stack.pop()
        if visited[r][c] == 0:
            visited[r][c] = 1
            for nR, nC in [[r+1, c],[r-1,c],[r,c+1],[r,c-1]]:
                if 0 <= nR < n and 0 <= nC < m:
                    if farm[nR][nC] == 1:
                        stack.append([nR, nC])


T = int(sys.stdin.readline())   # 테스트케이스 갯수

for _ in range(T):
    m, n, k = map(int, sys.stdin.readline().split())   # 가로, 세로, 배추갯수
    farm = [[0 for _ in range(m)] for _ in range(n)]
    visited = [[0 for _ in range(m)] for _ in range(n)]  # 배추벌레가 방문한 곳
    worm = 0  # 배추벌레의 갯수 카운트

    for _ in range(k):
        col, row = map(int, sys.stdin.readline().split())
        farm[row][col] = 1  # 배추가 위치하는 곳을 1로 표시

    #print(farm)
    for row in range(n):
        for col in range(m):
            if farm[row][col] == 1 and visited[row][col] == 0:
                DFS(row, col)
                worm += 1

    print(worm)

'알고리즘 > BFS와 DFS' 카테고리의 다른 글

2206번. 벽 부수고 이동하기  (0) 2019.12.18
7576번. 토마토  (0) 2019.12.17
2667번. 단지번호붙이기  (0) 2019.12.13
2606번. 바이러스  (0) 2019.12.13
1260번. DFS와 BFS  (0) 2019.12.13

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

 

 

문제 설명에 나와있는 것을 보면....

2차원 행렬을 그래프로 표현해 BFS나 DFS로 순회한다? 설명이 뭔가 난해했다.

2차원 행렬을 그래프로 표현해? 2차원 행렬은 인접 노드들을 표현하기 위한 거였는데...

여기에 낚이지 말자. BFS나 DFS에서 썼던 2차원 행렬은 대각선으로 접었을 때 똑같은 데칼코마니 행렬이고,

여기서 말하는 2차원 행렬은 그냥 집이 있음/없음을 나타내는 불규칙한 행렬이다. 이걸 BFS나 DFS로 순회해보자는 거다.

 

자, 그럼 인접한 노드들은 어떻게 알 수 있을까?

인덱스로 알 수 있다. 어떤 한 집의 주소가 houses[row][col] 이라면, 인접한 집은 위/아래/왼쪽/오른쪽이므로

이웃집들은 [row+1, col], [row-1, col], [row, col-1], [row, col+1] 이다. 인접 노드들은 이것들로 잡으면 된다.

 

하지만 이 이웃집들이 항상 존재하는 것은 아니다.

집이 없을수도 있고 (houses[row][col] == 0),

땅조차 없을 수도 있다. (row나 col이 mapSize를 벗어날 때)

 

이러한 조건들을 맞춰주면서 BFS나 DFS를 짜면 된다.

 

 

 

데이터 입력부

import sys

mapSize = int(sys.stdin.readline())
houses = [sys.stdin.readline()[:-1] for _ in range(mapSize)]  # 입력
visited = [[0 for _ in range(mapSize)] for _ in range(mapSize)]  # 이미 카운트됨 표시
ret = []
for row in range(mapSize):
    for col in range(mapSize):
        if houses[row][col] == '1' and visited[row][col] == 0:
            newTown = BFS(row, col)  # 리스트에 값이 추가될 때마다 새로운 단지 추가
            ret.append(newTown)      # newTown에 저장된 숫자는 각 단지에 포함된 가구 수

mapSize는 한 변의 길이이다. houses는 입력받은 1010111...따위를 저장한 리스트고,

visited는 이미 카운트된 집을 표시하는 리스트이다. DFS나 BFS에서 사용하는 이름을 그냥 따왔다. 어떤 조사원 한 명이 집집마다 방문하면서 단지 수를 센다고 생각하면 이해하기 편하겠다.

7번 줄부터 이중 for문을 돌면서 houses가 1인 곳을 찾는데, mapSize가 최대 25이므로 0부터 제일 끝까지 살펴봤자 25*25=625밖에 안된다. 맘편하게 돌자.

참고로 제출할 때는 4번 줄에 [:-1]을 없애야 한다. 백준 데이터 입력은 스트링 맨 끝에 \n 없이 들어오기 때문이다.

테스트 케이스를 복붙할 때는 \n이 있어서 코딩할 때는 저렇게 해야지 어쩔 수 없다.

 

 

 

DFS (스택)

def DFS(row, col):
    stack = []  # houses가 1인 이웃집들이 추가될 것임
    stack.append([row, col])  
    connected = 0  # 가구 수

    while stack:
        [r, c] = stack.pop()
        if visited[r][c] == 0:
            connected += 1
            visited[r][c] = 1
            for [nR, nC] in [[r+1, c], [r-1, c], [r, c+1], [r, c-1]]:  # 인접
                if 0 <= nR < mapSize and 0 <= nC < mapSize:  # 범위 체크
                    if houses[nR][nC] == '1':
                        stack.append([nR, nC])
    return connected
    

print(len(ret))
ret.sort()
for i in ret:
    print(i)

houses가 1인 이웃집들만 스택에 추가를 해 주면서, 아직 방문하지 않았다면 connected에 하나 추가하는 식으로 가구 수를 세어 나가면 된다.

 

 

 

BFS

import collections

def BFS(row, col):
    queue = collections.deque()  # houses가 1인 이웃집들이 추가될 것임
    queue.append([row, col])
    connected = 0  # 가구 수

    while queue:
        [r, c] = queue.popleft()  # 큐에서 꺼낸 row, col -> r, c
        if visited[r][c] == 0:
            connected += 1
            visited[r][c] = 1
            for [nR, nC] in [[r+1, c], [r-1, c], [r, c+1], [r, c-1]]:  # 인접.이웃.
                if 0 <= nR < mapSize and 0 <= nC < mapSize:  # 범위 체크
                    if houses[nR][nC] == '1':
                        queue.append([nR, nC])
    return connected
    
print(len(ret))
ret.sort()
for i in ret:
    print(i)

BFS도 똑같으니 설명 생략.

 

 

 

'알고리즘 > BFS와 DFS' 카테고리의 다른 글

2206번. 벽 부수고 이동하기  (0) 2019.12.18
7576번. 토마토  (0) 2019.12.17
1012번. 유기농 배추  (0) 2019.12.13
2606번. 바이러스  (0) 2019.12.13
1260번. DFS와 BFS  (0) 2019.12.13

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

 

 

 

BFS 혹은 DFS로 풀 수 있는 문제인데, 전에 포스팅했던 문제보다는 조건이 덜 까다롭다.

인접 노드들을 일정한 순서대로 방문할 필요가 없으므로 인접 노드들을 저장할 때 set을 사용했다.

 

import sys
import collections


def BFS(start):
    infected = []
    queue = collections.deque()
    queue.append(start)

    while queue:
        node = queue.popleft()
        if node not in infected:
            infected.append(node)
            if node in network:
                queue.extend(network[node])
    return len(infected)-1


nodeCount = int(sys.stdin.readline())
edgeCount = int(sys.stdin.readline())
network = {}

for _ in range(edgeCount):
    start, end = map(int, sys.stdin.readline().split())
    if start not in network:
        network[start] = {end}  # set. 집합
    elif start in network:
        network[start].add(end)
    if end not in network:
        network[end] = {start}
    elif end in network:
        network[end].add(start)

#print(network)

print(BFS(1))

'알고리즘 > BFS와 DFS' 카테고리의 다른 글

2206번. 벽 부수고 이동하기  (0) 2019.12.18
7576번. 토마토  (0) 2019.12.17
1012번. 유기농 배추  (0) 2019.12.13
2667번. 단지번호붙이기  (0) 2019.12.13
1260번. DFS와 BFS  (0) 2019.12.13

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

 

 

딕셔너리를 이용한 DFS와 BFS 구현하기 문제이다.

인접 행렬(이중 리스트)이 아닌 딕셔너리를 사용한 이유는, 정점은 많은데 간선이 적은 경우 시간 및 메모리 낭비를 피하기 위함이다. 예를 들어 정점은 1000개인데 간선은 1개일 때, 인접 행렬의 0, 1로 이를 표현한다고 생각해보자. 0들로 채워진 수많은 값들은 버려질 것이다. 그리고 탐색할 때도 오래 걸릴 것이다.

따라서 파이썬을 통해 DFS 및 BFS를 구현할 때는 {시작정점:도착정점}으로 이루어진 딕셔너리를 사용하는 것이 좋다고 판단했다.

 

단순한 것 같지만 인터넷에서 BFS, DFS 구현 코드를 배워다 그대로 쓰면 틀리는 문제이다.

문제에 있는 조건 중 신경써야 할 것은 

자식이 여러 개라면 작은 것부터 방문해야 한다는 것이고,

또한 간선이 없는 그래프일 수도 있다 (ex: 입력이 1 0 1 인 경우) 는 것이다.

 

(참고..쓰다보니 단어를 혼합해서 썼는데 정점=노드, 인접=자식 의 의미로 썼다.)

먼저 데이터 삽입부부터 보자.

n, m, v = map(int, sys.stdin.readline().split())  # n=정점 갯수, m=간선 갯수, v=시작점
graph = {}  # 인접 노드들을 표시한 딕셔너리
for _ in range(m):
    start, end = map(int, sys.stdin.readline().split())
    if start in graph:
        if end not in graph[start]:
            graph[start].append(end)
    elif start not in graph:
        graph[start] = [end]
    if end in graph:
        if start not in graph[end]:
            graph[end].append(start)
    elif end not in graph:
        graph[end] = [start]

for vertex in graph.keys():
    graph[vertex].sort()

1) 문제에 있는 알파벳을 그대로 사용했다.

2) graph는 자식 노드(인접 노드)들을 표시한 딕셔너리이다.

3) 간선 갯수만큼 for문을 돌린다. 노드 갯수만큼이 아니다. 입력으로 주어지는 1 3, 2 3 등은 간선을 표시한 거다.

4) start와 end는 시작 정점, 도착 정점을 의미한다. 사실 시작과 도착의 의미는 없지만 1 3에서 앞에 있는 숫자가 시작, 뒤에 있는 숫자가 도착이라고 임의로 정한거다. 

5) 먼저 start라는 정점이 graph에 이미 key로 존재하는지 확인하고, 있다면 거기에 end를 추가한다. 

6) graph[start]에 이미 end라는 정점이 있는지 살피는 이유는 두 정점 사이에 여러 간선이 있을 수 있기 때문이다. 입력이 2 5, 5 2 와 같이 들어왔을 때 굳이 자식들이 중복되서 들어가지 않게 하기 위함이다.

8) 만약 graph에 start라는 key가 없다면, 그 key를 추가하고 end를 리스트화해서 value로 넣는다.

10~14) end라는 정점에 대해서 같은 작업을 반복해준다. 무방향 그래프이므로, 1 3이 주어진다면 1의 자식은 3이며 3의 자식은 1이기 때문이다.

16~17) 입력이 오름차순으로 순차적으로 주어진다는 보장이 없으므로 정렬해준다.

 

설명은 엄청 긴데, 리스트를 사용해서 중복을 제거하며 인접 노드들을 (양방향으로) 추가해줬다고 생각하면 된다.

graph는 {1: [2, 3], 2: [1], 3: [1, 4], 4: [3]} 이런 모양이다.

인접 노드를 추가할 때 중복을 피하는 작업을 하면서까지 굳이 set을 안 쓰고 리스트를 쓴 이유는 

나중에 인접한 노드가 여러 개일 경우 크기가 작은 것부터 방문해야 하는데, set은 순서가 없는 자료형이기 때문이다.

 

아래로는 진짜 구현부다.

 

 

 

재귀를 이용한 DFS

def DFS_recursion(start, visited):
    visited.append(start)
    if start in graph:
        for child in graph[start]:
            if child not in visited:
                DFS_recursion(child, visited)
    return visited
    
ret = []
Print(DFS_recursion(v, ret))

좀더 이해하기 쉬운 재귀를 이용한 방식이다. 먼저 문제에서 정해준 v라는 시작 정점으로 호출한다.

2) 해당 정점을 방문한 리스트에 추가한다.

3) 해당 정점이 인접 노드가 있다면! (무인도처럼 떨어져 있는 정점이 있을 수 있다. 이런 경우에 해당 정점은 graph에 등장하지 않는다. 이 if문이 없다면 런타임 에러가 날 수 있다)

4) 그 인접 노드들을 child로 하나하나 가지고 와서 방문하지 않았을 경우, 그 child로 다시 이 함수를 시작한다.

이 함수는 자식 노드들을 방문하는 함수이므로, 재귀 함수를 호출할 때마다 자식 노드->자식의 자식 노드->자식의 자식의 자식 노드 순으로 방문하게 된다.

하지만 이와 같이 재귀로 DFS를 구현할 경우 이해하기에는 직관적이지만 속도가 느려질 수 있고 최대 재귀 깊이에 따라서 에러가 날 수 있다.

 

 

 

Stack를 이용한 DFS

def DFS_stack(start):
    visited = []
    stack = []
    stack.append(start)

    while stack:
        node = stack.pop()
        if node not in visited:
            visited.append(node)
            if node in graph:
                stack.extend(sorted(graph[node], reverse=True))
    return visited
    
 Print(DFS_stack(v))

 스택을 이용한 방식은 stack이라는 리스트를 따로 만들어줘야 한다. 스택을 이용한 방식에서는 이 함수를 또다시 호출하는 일은 없을 것이므로 visited를 함수 밑에서 생성해도 된다.

4) 먼저 시작 정점을 stack에 추가한다.

6) stack이 비어 있지 않을 때까지 진행한다.

7~8) stack에서 노드를 하나 꺼내서, 아직 방문하지 않았다면 방문한 리스트에 추가하고

9) 그 노드가 인접 딕셔너리에 key로 존재한다면, 즉 인접 노드들을 가지고 있다면! (인접 노드가 0일 수 있음)

10) stack 리스트에 그 인접 노드들을 모두 추가해주되, 역순으로 추가한다. 그 이유는 스택은 마지막에 들어온 것이 먼저 나가는데, 현재 노드가 1이고 앞으로 추가할 노드들이 2, 3, 4라고 한다면 4, 3, 2 순으로 추가해줘야 2부터 방문할 것이기 때문이다.

이게 왜 DFS이냐 하면, 스택에 추가되서 방문되는 순서를 생각해보자. 시작 노드가 1이고 그 자식 노드가 2, 3, 4라고 한다면 스택에 4, 3, 2 순으로 들어올 것이다. 그 다음 반복문에서 2를 pop 하고 방문한 뒤 2의 자식들을 또 stack에 추가하고, 그것들을 방문하고...생각해보면 DFS이다.

 

 

 

리스트를 사용한 BFS

def BFS_list(start):
    visited = []
    queue = []
    queue.append(start)

    while queue:
        node = queue.pop(0)
        if node not in visited:
            visited.append(node)
            if node in graph:
                queue.extend(graph[node])  # node의 자식 노드 추가
    return visited
    
Print(BFS_list(v))

리스트보다는 collections의 deque를 사용하는 것이 빠르지만, 이해를 위해서 일단 리스트를 사용했다.

BFS는 DFS 스택 버전과 거의 비슷하다. 달라지는 것은 7번줄의 pop(0)이다.

앞에서부터 노드를 빼서 방문하겠다는 것이다. 

처음에는 시작 노드의 자식 노드들을 방문했다가, 그 자식들의 자식들을 맨 뒤에 추가하고, 앞에서부터 순차적으로 방문하니 BFS가 되는 것이다.

 

 

 

Collections의 Deque를 사용한 BFS

import collections

def BFS_queue(start):
    visited = []
    queue = collections.deque()
    queue.append(start)

    while queue:
        node = queue.popleft()
        if node not in visited:
            visited.append(node)
            if node in graph:
                queue.extend(graph[node])
    return visited
    
Print(BFS_queue(v))

pop(0) 는 O(N)

deque의 popleft() 는 O(1)이다. 큐를 파이썬에서 사용하려면 이것을 써야한다고 한다.

 

 

 

 

코드 전문

import sys
import collections


def DFS_recursion(start, visited):
    visited.append(start)
    if start in graph:
        for child in graph[start]:
            if child not in visited:
                DFS_recursion(child, visited)
    return visited


def DFS_stack(start):
    visited = []
    stack = []
    stack.append(start)

    while stack:
        node = stack.pop()
        if node not in visited:
            visited.append(node)
            if node in graph:
                stack.extend(sorted(graph[node], reverse=True))
    return visited


def BFS_list(start):
    visited = []
    queue = []
    queue.append(start)

    while queue:
        node = queue.pop(0)
        if node not in visited:
            visited.append(node)
            if node in graph:
                queue.extend(graph[node])  # node의 자식 노드 추가
    return visited


def BFS_queue(start):
    visited = []
    queue = collections.deque()
    queue.append(start)

    while queue:
        node = queue.popleft()
        if node not in visited:
            visited.append(node)
            if node in graph:
                queue.extend(graph[node])
    return visited


def Print(lst):
    for i in lst:
        print(i, end=' ')
    print("")


n, m, v = map(int, sys.stdin.readline().split())  # n=정점 갯수, m=간선 갯수, v=시작점
graph = {}  # 인접 노드들을 표시한 딕셔너리
for i in range(m):
    start, end = map(int, sys.stdin.readline().split())
    if start in graph:
        if end not in graph[start]:
            graph[start].append(end)
    elif start not in graph:
        graph[start] = [end]
    if end in graph:
        if start not in graph[end]:
            graph[end].append(start)
    elif end not in graph:
        graph[end] = [start]

for vertex in graph.keys():
    graph[vertex].sort()

print(graph)
ret = []
#Print(DFS_recursion(v, ret))
Print(DFS_stack(v))
#Print(BFS_list(v))
Print(BFS_queue(v))

'알고리즘 > BFS와 DFS' 카테고리의 다른 글

2206번. 벽 부수고 이동하기  (0) 2019.12.18
7576번. 토마토  (0) 2019.12.17
1012번. 유기농 배추  (0) 2019.12.13
2667번. 단지번호붙이기  (0) 2019.12.13
2606번. 바이러스  (0) 2019.12.13

+ Recent posts