문제2 - 행렬 테두리 회전하기 (Python)

 

프로그래머스

SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr


행렬의 특정 영역 테두리를 시계 방향으로 회전시키고, 그 과정에서 이동한 숫자들 중 최솟값을 찾는 문제입니다. collections.deque 자료구조를 활용한 효율적인 회전과 실시간 최솟값 추적을 적용했습니다.


1.  나의 풀이 (Modular Approach)

기능별로 함수를 분리하고, deque를 도입하여 회전 로직의 가독성과 성능을 높였습니다. 한 줄이 길어질 수 있는 연산은 코드 블록으로 나누어 작성했습니다.

 
from collections import deque

def rotate(board, point):
    # 0-인덱스 보정
    row1, col1 = point[0] - 1, point[1] - 1
    row2, col2 = point[2] - 1, point[3] - 1
    
    # 변의 길이 계산
    horizontal_len = abs(col2 - col1 + 1)
    vertical_len = abs(row2 - row1 + 1)

    # 최솟값 초기화 및 추출을 위한 변수 설정
    min_val = board[row1][col1]
    r, c = row1, col1 - 1
    deq = deque()

    # 1. 테두리 값 추출 및 실시간 최솟값 갱신
    for _ in range(horizontal_len):
        c += 1
        deq.append(board[r][c])
        if board[r][c] < min_val: 
            min_val = board[r][c]
            
    for _ in range(vertical_len - 1):
        r += 1
        deq.append(board[r][c])
        if board[r][c] < min_val: 
            min_val = board[r][c]
            
    for _ in range(horizontal_len - 1):
        c -= 1
        deq.append(board[r][c])
        if board[r][c] < min_val: 
            min_val = board[r][c]
            
    for _ in range(vertical_len - 2):
        r -= 1
        deq.append(board[r][c])
        if board[r][c] < min_val: 
            min_val = board[r][c]
            
    # 2. 데큐를 이용한 시계 방향 1칸 회전
    deq.rotate(1)

    # 3. 회전된 값을 다시 보드에 삽입
    r, c = row1, col1 - 1
    index = 0
    
    for _ in range(horizontal_len):
        c += 1
        board[r][c] = deq[index]
        index += 1
        
    for _ in range(vertical_len - 1):
        r += 1
        board[r][c] = deq[index]
        index += 1
        
    for _ in range(horizontal_len - 1):
        c -= 1
        board[r][c] = deq[index]
        index += 1
        
    for _ in range(vertical_len - 2):
        r -= 1
        board[r][c] = deq[index]
        index += 1
        
    return min_val


def solution(rows, columns, queries):
    answer = []
    # 보드 초기화
    board = [[0] * columns for _ in range(rows)]
    counter = 1
    for i in range(rows):
        for j in range(columns):
            board[i][j] = counter
            counter += 1
            
    # 쿼리 순차 수행
    for query in queries:
        answer.append(rotate(board, query))

    return answer

2.  오늘 배운 점 및 복기 노트

collections.deque의 rotate() 활용

리스트 슬라이싱을 이용한 회전(arr[-1:] + arr[:-1])보다 deque.rotate(1)을 사용하는 것이 자료구조의 목적에 부합하며 가독성이 훨씬 뛰어납니다. 내부적으로 양방향 연결 리스트 형태를 띠어 양 끝단의 데이터 조작에 최적화되어 있습니다.

실시간 최솟값 추적 최적화

이전에는 추출이 끝난 뒤 min(arr)을 호출했으나, 데이터를 하나씩 추출하여 데큐에 넣는 루프 안에서 즉시 최솟값을 비교 및 갱신하도록 수정했습니다. 이를 통해 별도의 순회 비용을 줄일 수 있습니다.

좌표 보정과 루프 제어

사각형 테두리를 돌 때 모서리 부분이 중복으로 들어가지 않도록 horizontal_len, vertical_len - 1, horizontal_len - 1, vertical_len - 2 순으로 루프 범위를 설계하는 디테일한 인덱스 제어 방식을 익혔습니다.


3.  프로그래머스 코딩 테스트 문제 풀이 전략 교재에서 배울만한 점

리스트 컴프리헨션을 이용한 Pythonic 초기화

중첩 for문 대신 행과 열의 인덱스 규칙을 활용한 리스트 컴프리헨션으로 보드를 초기화하는 방식이 간결하고 성능면에서도 유리합니다.

board = [[(i * columns + j + 1) for j in range(columns)] for i in range(rows)]

인플레이스(In-place) 스왑을 통한 공간 최적화

별도의 리스트나 데큐를 생성하지 않고, temp 변수 하나만 사용하여 보드 내에서 직접 값을 당겨오는 방식의 효율성을 배웠습니다. 공간 복잡도를 $O(1)$로 유지해야 하는 상황에서 필수적이지만, 실수할 가능성이 있으니 전략에 따라 deque를 사용하는 저의 방식도 나쁘지 않겠네요.

시계 방향 회전의 반시계 방향 순회

시계 방향으로 값을 한 칸씩 밀기 위해, 실제 코드 루프는 반대 방향(반시계)으로 돌며 앞의 값을 현재 위치로 가져오는 방식이 데이터 덮어쓰기 문제를 깔끔하게 해결한다는 인사이트를 얻었습니다.

'알고리즘 > 프로그래머스' 카테고리의 다른 글

문제1 - 교점에 별 만들기 (Python)  (0) 2026.04.20