본문 바로가기
카테고리 없음

자바 - 프로그래머스 - 카드 뭉치 (feat. 런타임 에러)

by Won's log 2024. 4. 15.

 

TIL 기준
1. 어떤 문제가 있었는지
2. 내가 시도해 본 것들
3. 어떻게 해결했는지
4. 뭘 새롭게 알았는지
=문시해알

 

1. 어떤 문제가 있었는지

프로그래머스에서 카드 뭉치 문제를 풀다가 다음과 같은 문제에 도달했다.

1) 테스트 케이스 실행 시 런타임 에러가 나다.

 

우선 문제와 예시를 보자.

 

사실 카드 뭉치의 경우, 문제가 제법 단순해서 조금 고민하고 바로 코드를 적어갔다.

 

참, 아래 내용은 코드 설계가 있기 때문에 한 번 더 고민하고 문제풀이 시도를 해보길 바란다!

 

🤓 코드 설계

<주의할 점>
// 한 번만 사용 가능
// 꼭 다음 카드만 사용 가능
// 단어 순서 바꿀 수 없음

<체크 포인트>
// for문 돌면서 해당 카드가 카드 더미에 없을 때 다음 더미에도 카드가 있는지 확인해 보기

<설계>

꼭 다음 카드만 사용했기에 기본 배열 타입보다 Queue를 사용해서 동일한 카드를 발견하면. pop()하는 방법을 선택했다.

// Create Queue each of the array
// if equals poll
// if cards1 is not matched, go to cards2

 

🤨 구현한 코드

import java.util.*;

class Solution {
    public String solution (String[] cards1, String[] cards2, String[] goal) {
        Queue<String> queCards1 = new LinkedList<>();
        Queue<String> queCards2 = new LinkedList<>();
        String answer = "Yes";

        queCards1.addAll(Arrays.asList(cards1));
        queCards2.addAll(Arrays.asList(cards2));

        for(int i=0; i<goal.length; i++) {
            if(!goal[i].equals(queCards1.peek())) {
                if(!goal[i].equals(queCards2.peek())) {
                    answer = "No";
                    break;
                }
                queCards2.poll();
            } else {
                queCards1.poll();
            }
        }
        return answer;
    }
}

 

그런데 왠 걸, 런타임 에러가 났다.

 

내 인생에 첫 런타임 에러다. 축배를 들자.

그만큼 알고리즘 문제와 다양한 코드 시도를 안 해보았다는 뜻이니까 부끄러움을 느끼고 참회하며 첫 런타임 에러를 맞이한 기분을 만끽하였다.

만끽도 잠시 왜 런타임 에러가 나왔을까, 찾아보았다.

 

2. 내가 시도해 본 것들 + 3. 어떻게 해결했는지

런타임 에러가 나오는 원인에 대해서 백준 홈페이지에 글을 남겨주신 분이 계셨다.

나의 경우에는 goal 배열의 길이를 확인하지 않고 인덱스에 접근하기 때문이었다. goal 배열의 길이를 기준으로 반복문을 돌려야 했다.

`goal` 배열의 인덱스 `i`가 `cards1` 배열과 `cards2` 배열의 길이보다 클 수 있기 때문에 이를 검사해주어야 했다.

 

😎 수정된 코드

import java.util.*;

class Solution {
    public String solution (String[] cards1, String[] cards2, String[] goal) {
        Queue<String> queCards1 = new LinkedList<>();
        Queue<String> queCards2 = new LinkedList<>();
        String answer = "Yes";

        queCards1.addAll(Arrays.asList(cards1));
        queCards2.addAll(Arrays.asList(cards2));

        for(int i=0; i<goal.length; i++) {
            if(!goal[i].equals(queCards1.peek())) {
                if(!goal[i].equals(queCards2.peek())) {
                    answer = "No";
                    break;
                }
                queCards2.poll();
            } else {
                queCards1.poll();
            }
        }
        return answer;
    }
}


`goal` 배열의 길이를 기준으로 반복문을 돌면서, 해당 인덱스의 값과 `queCards1` 또는 `queCards2`의 맨 앞에 있는 값이 일치하는지 확인하였다. 만약 일치하면 해당 큐에서 값을 제거하고, 일치하지 않으면 "No"를 반환하고 반복문을 종료한다.

마지막으로, 반복문 종료 후에도 카드가 남아있는 경우를 처리하여 "No"를 반환하는 방법으로 코드를 구현하였다.

 

4. 뭘 새롭게 알았는지 - 배열의 길이

goal 배열의 길이를 확인해야 하는 이유는 다음과 같다:

1. 인덱스 범위 초과 방지: 반복문에서 goal 배열의 인덱스를 사용하여 카드를 검사하는데, 이때 goal 배열의 길이를 벗어나는 인덱스에 접근하는 것을 방지하기 위해서이다. 만약 goal 배열의 길이를 초과하여 접근하게 되면 런타임 에러가 발생할 수 있다.

2. 모든 goal을 검사: goal 배열의 길이를 기준으로 반복문을 돌면서 모든 goal을 검사할 수 있다. 각각의 goal에 대해 카드가 있는 큐들 중에서 해당하는 카드를 찾고, 만약 모든 goal에 대해 카드를 찾을 수 없다면 "No"를 반환해야 한다.

이상적으로, goal 배열의 길이를 사용하여 모든 goal을 검사하는 것이 보다 안전하고 효율적인 방법이기에

다음부터는 배열의 길이를 애매모호하게 만들지 말고 명확하게 제시해 주어야겠다.