문제 설명
ANIMAL_OUTS 테이블은 동물 보호소에서 입양 보낸 동물의 정보를 담은 테이블입니다. ANIMAL_OUTS 테이블 구조는 다음과 같으며, ANIMAL_ID, ANIMAL_TYPE, DATETIME, NAME, SEX_UPON_OUTCOME는 각각 동물의 아이디, 생물 종, 입양일, 이름, 성별 및 중성화 여부를 나타냅니다.

보호소에서는 몇 시에 입양이 가장 활발하게 일어나는지 알아보려 합니다. 0시부터 23시까지, 각 시간대별로 입양이 몇 건이나 발생했는지 조회하는 SQL문을 작성해주세요. 이때 결과는 시간대 순으로 정렬해야 합니다.
예시
SQL문을 실행하면 다음과 같이 나와야 합니다.

WITH RECURSIVE hours AS (
SELECT 0 AS hour
UNION ALL
SELECT hour + 1 FROM hours WHERE hour < 23
)
SELECT
h.hour AS HOUR,
COUNT(a.ANIMAL_ID) AS COUNT
FROM hours h
LEFT JOIN ANIMAL_OUTS a
ON h.hour = HOUR(a.DATETIME)
GROUP BY h.hour
ORDER BY h.hour;
문제 설명
지나다니는 길을 'O', 장애물을 'X'로 나타낸 직사각형 격자 모양의 공원에서 로봇 강아지가 산책을 하려합니다. 산책은 로봇 강아지에 미리 입력된 명령에 따라 진행하며, 명령은 다음과 같은 형식으로 주어집니다.
["방향 거리", "방향 거리" … ]
예를 들어 "E 5"는 로봇 강아지가 현재 위치에서 동쪽으로 5칸 이동했다는 의미입니다. 로봇 강아지는 명령을 수행하기 전에 다음 두 가지를 먼저 확인합니다.
주어진 방향으로 이동할 때 공원을 벗어나는지 확인합니다.
주어진 방향으로 이동 중 장애물을 만나는지 확인합니다.
위 두 가지중 어느 하나라도 해당된다면, 로봇 강아지는 해당 명령을 무시하고 다음 명령을 수행합니다.
공원의 가로 길이가 W, 세로 길이가 H라고 할 때, 공원의 좌측 상단의 좌표는 (0, 0), 우측 하단의 좌표는 (H - 1, W - 1) 입니다.

공원을 나타내는 문자열 배열 park, 로봇 강아지가 수행할 명령이 담긴 문자열 배열 routes가 매개변수로 주어질 때, 로봇 강아지가 모든 명령을 수행 후 놓인 위치를 [세로 방향 좌표, 가로 방향 좌표] 순으로 배열에 담아 return 하도록 solution 함수를 완성해주세요.
제한사항

입출력 예

입출력 예 #1
입력된 명령대로 동쪽으로 2칸, 남쪽으로 2칸, 서쪽으로 1칸 이동하면 [0,0] -> [0,2] -> [2,2] -> [2,1]이 됩니다.
입출력 예 #2
입력된 명령대로라면 동쪽으로 2칸, 남쪽으로 2칸, 서쪽으로 1칸 이동해야하지만 남쪽으로 2칸 이동할 때 장애물이 있는 칸을 지나기 때문에 해당 명령을 제외한 명령들만 따릅니다. 결과적으로는 [0,0] -> [0,2] -> [0,1]이 됩니다.
입출력 예 #3
처음 입력된 명령은 공원을 나가게 되고 두 번째로 입력된 명령 또한 장애물을 지나가게 되므로 두 입력은 제외한 세 번째 명령만 따르므로 결과는 다음과 같습니다. [0,1] -> [0,0]
- 입력 데이터
공원(park):
경로(routes):
- 동작 방식
- 출력
┌────────────────────────────┐
│ 시작(Start) │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ park, routes 입력 받기 │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ 공원의 행 수와 열 수 계산 │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ park 배열에서 'S' 위치 탐색 │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ 현재 위치를 시작 위치로 설정 │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ routes 배열을 순회하며 │
│ 각 명령어 처리(아래 참조) │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ 모든 명령어 처리 후 최종 위치 │
│ 반환 (리턴) │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ 종료 │
└────────────────────────────┘
[각 명령어 처리 세부 플로우]
┌────────────────────────────┐
│ 명령어 "방향 거리" 파싱하기 │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ 해당 방향에 대한 │
│ 이동 벡터(Δ행, Δ열) 구하기 │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ 임시 위치를 현재 위치로 설정 │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ 1부터 거리만큼 반복하면서 │
│ 임시 위치 갱신하기 │
└────────────────────────────┘
│
▼
┌────────────────────────────┐
│ 임시 위치가 범위 내인지, │
│ 장애물 없는지 확인 │
└────────────────────────────┘
│
┌───────┴───────┐
│ │
[문제 있음] [문제 없음]
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 명령어 무시 │ │ 명령어 완료 후 │
│ (현재 위치 유지)│ │ 현재 위치 갱신 │
└─────────────────┘ └─────────────────┘
function solution(park, routes):
// 1. 공원의 크기 계산
rows ← park.length
cols ← park[0].length
// 2. 시작 위치 'S' 탐색
for r from 0 to rows - 1:
for c from 0 to cols - 1:
if park[r].charAt(c) == 'S':
startRow ← r
startCol ← c
// 3. 현재 위치 초기화
currentRow ← startRow
currentCol ← startCol
// 4. 방향 벡터 매핑 설정
directionMap ← {
'N': (-1, 0),
'S': (1, 0),
'W': (0, -1),
'E': (0, 1)
}
// 5. routes 배열의 각 명령어 처리
for each route in routes:
// 5-1. 명령어 파싱: 방향과 거리 구하기
split route by space into [dir, distStr]
distance ← integer value of distStr
// 5-2. 해당 방향의 이동 벡터 가져오기
deltaRow, deltaCol ← directionMap[dir]
// 5-3. 임시 위치 변수 초기화
newRow ← currentRow
newCol ← currentCol
validCommand ← true
// 5-4. 주어진 거리만큼 한 칸씩 이동하면서 검증
for i from 1 to distance:
newRow ← newRow + deltaRow
newCol ← newCol + deltaCol
// 범위 검사 및 장애물 확인
if newRow < 0 or newRow ≥ rows or newCol < 0 or newCol ≥ cols or park[newRow].charAt(newCol) == 'X':
validCommand ← false
break out of the loop
// 5-5. 명령어가 유효한 경우에만 현재 위치 업데이트
if validCommand is true:
currentRow ← newRow
currentCol ← newCol
// 6. 최종 위치 반환
return [currentRow, currentCol]
public int[] solution(String[] park, String[] routes) {
int rows = park.length;
int cols = park[0].length();
int startRow = 0, startCol = 0;
// 시작 위치 'S' 찾기
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (park[r].charAt(c) == 'S') {
startRow = r;
startCol = c;
break;
}
}
}
// 방향 벡터 설정
Map<Character, int[]> directions = new HashMap<>();
directions.put('N', new int[]{-1, 0});
directions.put('S', new int[]{1, 0});
directions.put('W', new int[]{0, -1});
directions.put('E', new int[]{0, 1});
// 현재 위치 초기화
int curRow = startRow;
int curCol = startCol;
// 명령어 처리
for (String route : routes) {
String[] parts = route.split(" ");
char dir = parts[0].charAt(0);
int dist = Integer.parseInt(parts[1]);
int[] delta = directions.get(dir);
boolean canMove = true;
int newRow = curRow;
int newCol = curCol;
// 주어진 거리만큼 이동할 수 있는지 확인
for (int i = 1; i <= dist; i++) {
newRow += delta[0];
newCol += delta[1];
if (newRow < 0 || newRow >= rows || newCol < 0 || newCol >= cols
|| park[newRow].charAt(newCol) == 'X') {
canMove = false;
break;
}
}
if (canMove) {
curRow = newRow;
curCol = newCol;
}
}
return new int[]{curRow, curCol};
}
import java.util.*;
class Solution {
public int[] solution(String[] park, String[] routes) {
int[] answer = {0, 0};
int garo = park[0].length();
int sero = park.length;
int[] map = new int[garo * sero];
for (int i = 0; i < map.length; i++) {
if (park[i / garo].charAt(i % garo) == 'S') {
answer[0] = i / garo;
answer[1] = i % garo;
}
char tempChar = park[i / garo].charAt(i % garo);
map[i] = tempChar == 'S' ? 1 : (tempChar == 'X' ? -1 : 0);
}
char[] direct = new char[routes.length];
int[] distance = new int[routes.length];
for (int i = 0; i < routes.length; i++) {
String[] temp = routes[i].split(" ");
direct[i] = temp[0].charAt(0);
distance[i] = Integer.parseInt(temp[1]);
}
for (int i = 0; i < direct.length; i++) {
int tempInt = distance[i];
int now = answer[0] * garo + answer[1];
int move = 0;
switch (direct[i]) {
case 'N':
if (answer[0] - tempInt < 0) break;
for (int j = 1; j <= tempInt; j++) {
if (map[now - garo * j] < 0) {
move = 0;
break;
}
move--;
}
answer[0] += move;
break;
case 'S':
if (answer[0] + tempInt >= sero) break;
for (int j = 1; j <= tempInt; j++) {
if (map[now + garo * j] < 0) {
move = 0;
break;
}
move++;
}
answer[0] += move;
break;
case 'W':
if (answer[1] - tempInt < 0) break;
for (int j = 1; j <= tempInt; j++) {
if (map[now - j] < 0) {
move = 0;
break;
}
move--;
}
answer[1] += move;
break;
case 'E':
if (answer[1] + tempInt >= garo) break;
for (int j = 1; j <= tempInt; j++) {
if (map[now + j] < 0) {
move = 0;
break;
}
move++;
}
answer[1] += move;
break;
}
}
return answer;
}
}
map으로 표현하여 i / garo와 i % garo를 통해 2차원 좌표로 변환.'S', 'X', 'O'에 대해 각각 1, -1, 0으로 매핑하여 장애물 여부를 숫자로 판단.park[r].charAt(c)로 접근.'N', 'S', 'W', 'E')마다 별도의 switch-case 분기를 사용하여 처리.now - garo * j, now + j 등) 때문에 디버깅이나 유지보수가 어려워질 수 있음.Map<Character, int[]>를 활용하여 각 방향에 대한 (행, 열) 변화량을 저장.Map)를 사용함으로써 약간의 초기 설정 코드가 추가되지만, 전체 코드 길이와 가독성 측면에서는 유리함.| 비교 항목 | 원본 코드 | 개선 코드 |
|---|---|---|
| 가독성 | 1차원 배열과 switch-case 사용으로 복잡 | 2차원 좌표 및 방향 벡터로 직관적 |
| 유지보수성 | 중복 코드가 많아 수정 어려움 | 공통 로직을 사용하여 유지보수 용이 |
| 성능 | 문제 크기 내에서는 성능 차이 없음 | 문제 크기 내에서 성능 문제 없음 |
| 코드 중복 | 각 방향별로 동일한 로직 반복 | 방향 벡터 활용으로 중복 제거 |