카테고리 없음

(코테) 같은 숫자는 싫어_프로그래머스 (스택 )

불광동 물주먹 2025. 4. 29. 15:57

 

문제 설명
배열 arr가 주어집니다. 배열 arr의 각 원소는 숫자 0부터 9까지로 이루어져 있습니다. 이때, 배열 arr에서 연속적으로 나타나는 숫자는 하나만 남기고 전부 제거하려고 합니다. 단, 제거된 후 남은 수들을 반환할 때는 배열 arr의 원소들의 순서를 유지해야 합니다. 예를 들면,

arr = [1, 1, 3, 3, 0, 1, 1] 이면 [1, 3, 0, 1] 을 return 합니다.
arr = [4, 4, 4, 3, 3] 이면 [4, 3] 을 return 합니다.
배열 arr에서 연속적으로 나타나는 숫자는 제거하고 남은 수들을 return 하는 solution 함수를 완성해 주세요.

제한사항
배열 arr의 크기 : 1,000,000 이하의 자연수
배열 arr의 원소의 크기 : 0보다 크거나 같고 9보다 작거나 같은 정수

 

입출력 예시

 

arr answer
[1,1,3,3,0,1,1] [1,3,0,1]
[4,4,4,3,3] [4,3]

 

 

 

내가 제출한 답

import java.util.*;

class Solution {
    public List<Integer> solution(int[] arr) {
        LinkedList<Integer> list = new LinkedList<>();

        for (int num : arr) {
            if (list.isEmpty() || !list.getLast().equals(num)) {
                list.add(num);
            }
        }
        
        return list;
    }
}

 

최초 답을 정한 이유.

1. 임시로 만든 리스트 객체 안에 마지막 값을 비교해서 add 하자 (중복 허용이므로 Set 안됨)

2.내가 생각해낸게 마지막 배열 혹은 노드에 값을 꺼내 비교를 하자.

3. arraylist가 속도는 가장 빠르지만 , get(size() - 1) 이런식으로 마지막 값을 꺼내야해서 가독성이? 다소 마음에 들지 않아 

선택한 것이 LinkedList였다.

4.list.getLast()  사용해 마지막 노드 값을 확인해 비교를 해 삽입을 하는 방법으로 통과 시킴.

 

 

 

내가 생각한 회고후 최선의 답?

import java.util.*;

public class Solution {
    public Deque<Integer> solution(int[] arr) {
        Deque<Integer> deque = new ArrayDeque<>();

        for (int num : arr) {
            if (deque.isEmpty() || deque.peekLast() != num) {
                deque.addLast(num);
            }
        }

        return deque;
    }
}

 

LinkedList는 노드를 사용하여, next() 등으로 내부적으로 tail 포인터 접근 (약간의 오버헤드)가 발생하여 비효율적이라는 걸 배운적 있다. 내 블로그 참고.(컬렉션 비교 글)

그래서 찾아본게 바로 ArrayDeque 이다. 

 ArrayDeque   더 효율적인 이유

1. 배열 기반 원형 버퍼 

2. 순회속도 배열 순회라 훨씬 빠름.   (linkedlist는 노드르 따라가야해서 느림.)

3. 메모리 사용량도 적음 (linkedlist는 값 + next + prev 포인터 라 사용량 많음.)

 

 

 

 

 

+++번외+++

import java.util.*;

public class Solution {
    public int[] solution(int []arr) {
        ArrayList<Integer> tempList = new ArrayList<Integer>();
        int preNum = 10;
        for(int num : arr) {
            if(preNum != num)
                tempList.add(num);
            preNum = num;
        }       
        int[] answer = new int[tempList.size()];
        for(int i=0; i<answer.length; i++) {
            answer[i] = tempList.get(i).intValue();
        }
        return answer;
    }
}

 

이건 다른 사람이 푼 답을 가져온 것이다. 

코드를 보고 아 정말 속도와 메모리 효율에 극대화된 코드구나를 느꼈다. 

 

처음엔 단순해 보였지만, 자세히 보면
속도와 메모리 효율 측면에서 굉장히 극대화된 방식이다.

  • int 기본형 변수 하나로 이전 값을 기억하고
  • 불필요한 자료구조 접근 없이 배열을 순회
  • 결과도 바로 ArrayList에 담기기 때문에
    불필요한 메모리 접근이나 메서드 호출이 없다.

나 역시 Deque 구조로 풀었지만,
내 방식은 매번 peekLast() 같은 메서드로
자료구조 내부를 참조해야 해서, 미세하지만
메모리 접근이나 비교 연산이 누적된다.

그런 점에서 이 코드는
코딩테스트에 최적화된 풀이라고 생각했다.


하지만 고민이 들었다.

이 코드는 정말 빠르지만,
확장성이나 실무 상황에 바로 적용할 수 있을까? 라는 의문이 생겼다.

  • 예를 들어, preNum의 초기값인 10은 "0~9" 조건을 가정한 값인데
    만약 범위가 바뀌거나 null, 음수, 객체 등으로 확장된다면?
  • 또는 "이전 값과만 다르면 추가"가 아니라
    "두 개 전 값과 다르면", "홀수일 때만 추가"처럼
    조건이 바뀌는 경우는?

조건이 바뀔수록 구조 자체를 손대야 한다.

이런 점에서 실무에서는 유지보수나 전략 분리가 중요하기 때문에
Deque 기반처럼 상태가 구조 안에 캡슐화된 방식이 더 유연할 수 있다.


💡 마무리 고민

코딩테스트는 효율 최우선.
실무는 확장성과 유지보수성이 최우선.

이 두 기준 사이에서 어떤 걸 더 우선순위에 둘지
문제의 성격, 쓰임의 맥락, 그리고 팀의 개발 철학에 따라 달라질 수 있다.

나 역시 아직 딜레마가 있다.
하지만 중요한 건,
단순히 "더 빠른 코드"를 쓰는 게 아니라,
"문제에 맞는 적절한 수준의 설계"를 할 줄 아는 개발자가 되는 것
이라고 느꼈다.


✨ 한줄 회고

좋은 코드는 빠른 코드일 수도 있지만,
진짜 좋은 코드는 변화에 잘 견디는 코드일지도 모른다.

 

 

 

 

 

참고하면 좋을 글

컬렉션-  https://moon-97.tistory.com/39

 

Java Map 컬렉션 정리 – HashMap, LinkedHashMap, TreeMap

먼저 이 글을 쓰게 된 시작 점은 treeMap을 굳이 왜 써야할까? 어떤 상황에서 사용하면 이점이 클까? 그리고 어떤 구조로 관리가 될까? DB에서 원하는 정렬된 값을 받아와 hashmap에 담으면 되는데? 라

moon-97.tistory.com