ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 프로그래머스 <달리기 경주>
    Algorithms 2026. 2. 1. 11:21

     

    문제 이해

    얀에서는 매년 달리기 경주가 열린다.
    해설진은 선수가 바로 앞의 선수를 추월할 때, 추월한 선수의 이름을 부른다.

    예를 들어, 1등부터 3등까지 선수들이 다음과 같은 순서로 달리고 있다고 하자.

    ["mumu", "soe", "poe"]

    이때 해설진이 "soe"를 불렀다면, 2등이던 "soe" 선수가 1등 "mumu"를 추월했다는 의미가 된다.

     

    결과는 다음과 같이 바뀐다.

    ["soe", "mumu", "poe"]

    선수들의 이름이 현재 등수 순서대로 담긴 배열 players와 해설진이 부른 이름들이 담긴 배열 callings가 주어질 때,
    경주가 끝난 후의 최종 순위를 반환하는 solution 함수를 구현하는 문제이다.

     

     


     

     

    초기 접근

    처음에는 단순하게 생각했다.

    • callings 배열을 하나씩 순회하면서
    • 그 안에서 players 배열을 다시 순회한다
    • 이름이 같으면 해당 선수와 앞 선수의 위치를 swap

    이를 코드로 구현하면 다음과 같다.

    class Solution {
        public String[] solution(String[] players, String[] callings) {
            String temp = "";
            
            for (int i=0; i<callings.length; i++) {
                for (int j=0; j<players.length; j++) {
                    if (players[j].equals(callings[i])) {
                        temp = players[j -1];
                        players[j - 1] = players[j];
                        players[j] = temp;
                    }
                }
            }
            
            return players;
        }
    }

    논리적으로는 문제가 없어 보였고, 그대로 제출했다.

     

     

    문제 발생: 시간 초과

    결과는 20개 테스트케이스 중 6개에서 시간 초과가 발생했다.

    원인을 분석해보면, 현재 코드는 callings 하나마다 players 배열을 처음부터 끝까지 탐색하고 있다.

    • callings 길이: M
    • players 길이: N

    → 전체 시간 복잡도는 O(N × M)

    입력 크기가 커질 경우, 이중 반복문은 충분히 시간 초과를 유발할 수 있다.

    따라서 두 번 순회하지 않는 방식으로 접근을 바꿀 필요가 있었다.

     

     


     

     

    수정한 접근: HashMap 활용

    수정한 핵심 아이디어는 간단하다.

    선수의 현재 등수를 O(1)로 알 수 있다면, 전체 탐색이 필요 없다.

    이를 위해 선수 이름과 현재 등수를 HashMap에 저장한다.

     

    자료구조 정의

    • players[i] : i등에 위치한 선수 이름
    • pos[name] : 해당 선수의 현재 등수(index)

     

    처리 흐름

    1. players 배열을 순회하며
      이름 → 등수 형태로 HashMap에 저장
    2. callings를 순회하며
      • 불린 선수의 현재 등수를 O(1)로 조회
      • 앞 선수와 swap
      • swap된 두 선수의 등수를 HashMap에서도 함께 갱신

    이렇게 하면 모든 호출을 상수 시간에 처리할 수 있다.

    → 최종 시간 복잡도: O(N + M)

     

     

    최종 코드

    import java.util.*;
    
    class Solution {
        public String[] solution(String[] players, String[] callings) {
            String temp = "";
            
            Map<String, Integer> pos = new HashMap<>();
            
            // 이름 -> 현재 등수(index) 저장
            for (int i = 0; i < players.length; i++) {
                pos.put(players[i], i);
            }
            
            for (String callingName : callings) {
                int i = pos.get(callingName);
                
                String front = players[i - 1];
                players[i - 1] = players[i];
                players[i] = front;
                
                pos.put(callingName, i - 1);
                pos.put(front, i);
            }
            
            return players;
        }
    }

    이중 반복문을 제거하면서 로직은 거의 유지한 채 성능을 크게 개선할 수 있었다.

     

     


     

     

    느낀점

    • 비교 대상이 많아질수록 전체 배열을 반복해서 순회하는 방식은 위험하다
    • 이중 for문, 삼중 for문은 입력 크기가 커지는 순간 성능 문제가 발생한다
    • 찾는 값이 무엇인가? → 이걸 바로 찾을 수 있는 자료구조는? 이 질문을 먼저 떠올리는 습관이 중요하다
    • Map(HashMap)을 활용하면 반복 횟수를 줄이면서도 로직을 단순하게 유지할 수 있다

     

     

     

     

Designed by Tistory.