오늘은 백준 1012번 문제 풀이(DFS & BFS), 재귀 함수, 큐(Queue), 프로그래머스 문제 풀이를 진행
배추밭에서 인접한 배추가 하나의 군집을 이루며, 군집 개수를 구하는 문제.
DFS(깊이 우선 탐색)와 BFS(너비 우선 탐색)을 활용하여 해결할 수 있다.
Base Case)와 재귀 조건(Recursive Case) 필요DFS(깊이 우선 탐색)에서 자주 사용Base Case가 없으면 StackOverflowError 발생 가능Queue란 FIFO(First in first out, 선입선출)방식의 자료구조Queue)의 주요 연산| 연산 | 설명 |
|---|---|
| enqueue() | 데이터 삽입 (큐의 뒤쪽, Rear에 추가) |
| dequeue() | 데이터 삭제 (큐의 앞쪽, Front에서 제거) |
| peek() | 가장 앞(front)의 데이터를 확인 (제거 X) |
| isEmpty() | 큐가 비어 있는지 확인 |
Queue)와 우선순위 큐(Priority Queue)| 자료구조 | 큐 (Queue) | 우선순위 큐 (PriorityQueue) |
|---|---|---|
| 동작 방식 | FIFO(선입선출) | 작은 값이 먼저 나옴 |
| 삽입 | offer() | offer() |
| 삭제 | poll() (맨 앞 제거) | poll() (우선순위 높은 값 제거) |
| 사용 예시 | BFS, 작업 예약 | 다익스트라 알고리즘 |
Queue) vs 스택(Stack)| 자료구조 | 큐 (Queue) | 스택 (Stack) |
|---|---|---|
| 동작 방식 | FIFO (선입선출) | LIFO (후입선출) |
| 삽입 | offer() | push() |
| 삭제 | poll() (맨 앞 제거) | pop() (맨 위 제거) |
| 사용 예시 | BFS, 작업 예약 | DFS, 실행 취소 |
✔ 큐는 BFS에서 사용 (가까운 노드부터 탐색)
✔ 스택은 DFS에서 사용 (깊은 곳부터 탐색)
static void DFS(int x, int y){
visited[y][x] = true; // 방문 처리
for(int i = 0; i < 4; i++){ //네 방향으로 탐색 진행
int nx = x + dx[i]; // 이동할 x좌표
int ny = y + dy[i]; // 이동할 y좌표
if(nx >= 0 && ny >= 0 && nx < M && ny < N){ // 범위 내에 있는지 확인
if(field[ny][nx] == 1 && !visited[ny][nx]){ // 배추가 있으며 방문하지 않았으면
DFS(nx, ny); // 재귀 호출
}
}
}
}
✔ 재귀를 이용한 깊이 우선 탐색(DFS)
✔ 방문한 노드는 visited[][]배열로 체크
✔ 범위를 초과하지 않도록 조건문 활용
visited[y][x] = true; // 방문 처리
(x, y)를 방문 완료 상태로 변경visited[][]배열을 사용하여 중복 방문 방지StackOverflowError발생for(int i = 0; i < 4; i++){ // 네 방향으로 탐색
dx[] 와 dy[] 배열을 활용하여 상하좌우 이동i=0 -> 위쪽 이동i=1 -> 아래쪽 이동i=2 -> 왼쪽 이동i=3 -> 오른쪽 이동int nx = x + dx[i]; // 이동할 x 좌표
int ny = y + dy[i]; // 이동할 y 좌표
(x, y)에서 dx[i], dy[i]값을 더하여 이동할 좌표 (nx, ny) 계산if(nx >= 0 && ny >= 0 && nx < M && ny < N){ // 배열 범위 내에 있는지 확인
(nx, ny)가 배열 범위를 벗어나는지 확인if(field[ny][nx] == 1 && !visited[ny][nx]){ // 배추가 있으며 방문하지 않았으면
1)만 이동visited[ny][nx] == true이라면 이동하지 않음DFS(nx, ny); // 재귀 호출
DFS(nx, ny)를 호출하여 재귀적 탐색DFS호출이 종료되고, 이전 호출로 돌아감(백트래킹)M = 5, N = 5
배추 위치:
(1,1) (1,2) (2,2) (3,3) (4,3)
배추밭 배열(field[][])
0 0 1 0 0
0 1 1 1 0
0 0 1 0 0
0 0 0 1 1
0 0 1 1 1
DFS 실행 흐름
(0,2)에서 탐색 시작 → DFS(0, 2) 호출visited[0][2] = true(1, 2) 이동 → DFS(1, 2) 호출(1, 2)에서visited[1][2] = true(1, 1) 이동 → DFS(1, 1) 호출(1, 3) 이동 → DFS(1, 3) 호출(1, 1)에서visited[1][1] = true(1, 2)(1, 3)에서visited[1][3] = true(2, 2) 이동 → DFS(2, 2) 호출(2, 2)에서visited[2][2] = true(1,3)(3, 3)에서 DFS(3, 3) 호출(3, 4), (4, 3), (4, 4) 순서로 이동static void BFS(int x, int y){
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{x, y}); // 시작 위치 추가
visited[y][x] = true;
while (!queue.isEmpty()){
int[] pos = queue.poll();
int cx = pos[0], cy = pos[1];
for (int i = 0; i < 4; i++){
int nx = cx + dx[i];
int ny = cy + dy[i];
if(nx >= 0 && ny >= 0 && nx < M && ny < N){
if(field[ny][nx] == 1 && !visited[ny][nx]){
queue.offer(new int[]{nx, ny});
visited[ny][nx] = true;
}
}
}
}
}
✔ Queue를 사용하여 레벨별로 탐색
✔ DFS보다 재귀 호출이 없어 안정적
✔ 최단 거리 탐색과 유사한 방식
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{x, y}); // 시작 위치 추가
visited[y][x] = true; // 방문 처리
Queue를 선언, BFS 탐색 수행 준비queue.offer(new int[]{x, y})를 통해 탐색을 시작할 좌표 (x, y)를 큐에 추가visited[y][x] = true를 설정해 중복 방문 방지스택 대신 큐를 사용하며, 선입선출 방식으로 탐색 진행while (!queue.isEmpty()){
int[] pos = queue.poll();
int cx = pos[0], cy = pos[1];
queue.poll()를 통해 큐의 맨 앞에 있는 좌표 꺼내기cs, cy : 현재 방문 중인 좌표for (int i = 0; i < 4; i++){
int nx = cx + dx[i];
int ny = cy + dy[i];
dx[], dy[] 배열을 사용하여 네 방향(상하좌우)으로 이동할 좌표(nx, ny)를 계산if(nx >= 0 && ny >= 0 && nx < M && ny < N){
(nx, ny)가 배열 범위를 벗어나는지 확인if(field[ny][nx] == 1 && !visited[ny][nx]){
1)만 이동visited[ny][nx] == true이라면 이동하지 않음queue.offer(new int[]{nx, ny});
visited[ny][nx] = true;
queue.offer()을 사용하여 큐에 추가visited[][] = true로 설정해 중복 방문 방지M = 5, N = 5
배추 위치:
(1,1) (1,2) (2,2) (3,3) (4,3)
배추밭 배열(field[][])
0 0 1 0 0
0 1 1 1 0
0 0 1 0 0
0 0 0 1 1
0 0 1 1 1
BFS 실행 흐름
(0, 2)에서 탐색 시작 → BFS(0,2) 호출
visited[0][2] = true(1, 2), 왼쪽 (1, 1), 오른쪽 (1, 3)을 큐에 추가[(1, 2), (1, 1), (1, 3)](1, 2)에서
visited[1][2] = true(2, 2)를 큐에 추가[(1, 1), (1, 3), (2, 2)](1, 1)에서
visited[1][1] = true[(1, 3), (2, 2)](1, 3)에서
visited[1][3] = true[(2, 2)](2, 2)에서
visited[2][2] = true새로운 배추 (3, 3)에서 BFS(3, 3) 호출
(3, 4), (4, 3), (4, 4)를 큐에 추가class Solution {
public int solution(int num1, int num2) {
int answer = (num1/(double)num2) * 1000;
return answer;
}
}
num1 / (double) num2는 double 이지만 int 타입의 answer에 저장 하려고 함으로 타입 불일치 오류class Solution {
public int solution(int num1, int num2) {
int answer = (int)((num1/(double)num2) * 1000);
return answer;
}
}
double을 바로 int에 저장 할 수 없음으로 형변환 및 정수 변환 진행class Solution {
public int solution(int n) {
int answer;
for(int i = 0; i <= n; i++){
if(i % 2 == 0){
answer += i;
}
}
return answer;
}
}
class Solution {
public int solution(int n) {
int answer = 0;
for(int i = 0; i <= n; i++){
if(i % 2 == 0){
answer += i;
}
}
return answer;
}
}
NullPointerException 방지class Solution {
public double solution(int[] numbers) {
double answer = 0;
int val = numbers.length;
for(int i = 0; i < val; i++){
int a = numbers[i];
answer += a;
}
return answer/val;
}
}
class Solution {
public double solution(int[] numbers) {
double answer = 0;
int val = numbers.length;
for(int num:numbers){
answer += num;
}
return answer/val;
}
}