안녕하세요. 탁입니다.

다섯번째 문제는 스택/큐 유형의 Level1 문제인 '같은 숫자는 싫어' 입니다.


같은 숫자는 싫어

문제 설명

배열 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보다 작거나 같은 정수

입출력 예

입출력 예
2열 × 2
arranswer
[1,1,3,3,0,1,1][1,3,0,1]
[4,4,4,3,3][4,3]

입출력 예 설명

입출력 예 #1,2 문제의 예시와 같습니다.


나의 풀이

문제 접근 아이디어는 이랬습니다.

배열의 첫 값은 무조건 정답 배열에 넣고,

그 뒤부터 하나씩 끝까지 진행하면서,

정답 배열의 마지막 값과 다르다면 정답배열에 넣는다.

1. 초기화 상태

Python
5 lines
def solution(arr):
    answer = []
    # [실행] 버튼을 누르면 출력 값을 볼 수 있습니다.
    print('Hello Python')
    return answer

2. 풀이 1

Python
8 lines
def solution(arr):
    answer = []
    for i in range(len(arr)):
        if i == 0:
            answer.append(arr[i])
        elif answer[-1] != arr[i]:
            answer.append(arr[i])
    return answer

3. 풀이 2

Python
6 lines
def solution(arr):
    answer = [arr[0]]
    for a in arr:
        if (answer[-1] != a):
            answer.append(a)
    return answer

이번 풀이 2는 풀이1을 먼저 작성한 뒤에 조금 더 깔끔하게 정리해본 것입니다.

예시 테스트케이스도 모두 성공했으니 답안을 제출했습니다.

제출 답안 분석 및 복잡도 확인

Gemini의 도움을 받아 저의 답안에 대한 평가를 해봅니다.

  1. 제출 답안 1 - 시간 복잡도: O(N)O(N)
  • 루프가 NN번 실행되고 각 분기 내부의 연산이 O(1)O(1)이므로 시간 복잡도는 O(N)O(N)입니다.
  1. 제출 답안 1 - 공간 복잡도: O(N)O(N)
  • 결과를 담는 answer 리스트로 인해 O(N)O(N)의 메모리를 사용합니다.
  1. 제출 답안 2 - 시간 복잡도: O(N)O(N)
  • arr의 길이 NN(N1,000,000N \le 1,000,000)에 대해 단일 루프를 1회 순회합니다.
  • 리스트의 마지막 원소 조회(answer[-1])와 덧붙이기(append)는 모두 O(1)O(1) 연산입니다.
  • 따라서 전체 시간 복잡도는 O(N)O(N)이며, 파이썬 환경에서 약 0.05~0.08초 내에 효율성 테스트를 가뿐히 통과합니다.
  1. 제출 답안 2 - 공간 복잡도: O(N)O(N)
  • 연속 중복이 전혀 없는 최악의 경우, 정답 리스트 answer에 최대 NN개의 원소가 저장되므로 공간 복잡도는 O(N)O(N)입니다.
  1. 제출 답안 1 총평
  • 불필요한 반복 조건 검사: 매 원소를 순회할 때마다 단 한 번(첫 인덱스)만 참이 되는 if (i == 0) 조건을 최대 100만 번 검사하여 불필요한 분기 오버헤드가 발생합니다.
  • 원소 자체를 바로 꺼내는 for a in arr 방식에 비해 range(len(arr))arr[i] 인덱싱 접근은 파이썬 인터프리터상에서 약간 더 느리고 덜 파이써닉합니다.
  1. 제출 답안 2 총평
  • 초기값 [arr[0]]을 미리 넣어두어 스택이 비어 있을 때 발생하는 IndexError를 깔끔하게 회피했습니다.
  • 루프 첫 회차에서 answer[-1](즉 arr[0])과 a(즉 arr[0])를 한 번 더 비교하는 사소한 중복 연산이 발생하지만, O(1)O(1) 연산이므로 성능에 영향을 주지 않는 우수한 코드입니다.

AI 추천 답안

아래는 Gemini가 작성해 준 내용입니다.

사용 AI 모델: Gemini 3.8 Flash (Extended)

추천 1: 단락 평가(Short-circuit)를 활용한 표준 스택 풀이 (가장 권장)

스택/큐 카테고리의 출제 의도를 온전히 살리면서 분기문을 최소화한 정석 코드입니다.

Python
7 lines
def solution(arr):
    stack = []
    for num in arr:
        # 스택이 비어있거나, 스택 최상단(top)과 현재 숫자가 다르면 push
        if not stack or stack[-1] != num:
            stack.append(num)
    return stack
  • 동작 원리: 파이썬 or 연산자의 단락 평가(Short-circuit Evaluation) 특성을 활용합니다. 스택이 비어있을 때는 앞선 조건 not stack이 참이 되므로 뒤쪽의 stack[-1]을 평가하지 않아 IndexError가 원천 차단됩니다.
  • 장점: 초기 배열 크기나 예외 케이스(빈 배열 등)에 구애받지 않고 어디서나 안전하게 작동하며, 코딩테스트 및 기술 면접에서 자료구조 활용 능력을 가장 깔끔하게 보여줄 수 있습니다.

추천 2: 리스트 슬라이싱을 활용한 파이써닉 트릭

파이썬의 슬라이싱 특성을 이용해 조건문을 한 줄로 압축한 풀이입니다.

Python
7 lines
def solution(arr):
    stack = []
    for num in arr:
        # stack이 비어있어도 stack[-1:]은 에러 없이 []를 반환함을 이용
        if stack[-1:] != [num]:
            stack.append(num)
    return stack
  • 동작 원리: 빈 리스트에서 stack[-1]IndexError를 일으키지만, 슬라이싱 stack[-1:]은 빈 리스트 []를 안전하게 반환합니다. 따라서 [] != [num]이 성립하여 첫 원소가 자연스럽게 들어갑니다.

추천 3: itertools.groupby 활용형 (원라이너 풀이)

파이썬 표준 라이브러리의 연속 그룹화 함수를 활용한 풀이입니다.

Python
5 lines
from itertools import groupby

def solution(arr):
    # 연속으로 중복되는 요소를 그룹화하여 키(key)만 추출
    return [key for key, _ in groupby(arr)]
  • 동작 원리: itertools.groupby는 SQL의 GROUP BY와 달리 "연속된 동일 값"을 묶어주는 함수이므로 문제의 요구조건과 100% 일치합니다. C 레벨에서 구현된 이터레이터라 속도가 매우 빠릅니다.

제출 답안 vs AI 추천 답안 비교

제출 답안 vs AI 추천 답안 비교
5열 × 5
비교 항목제출 답안1 (if i == 0)제출 답안2 (answer = [arr[0]])추천 답안 1 (not stack or ...)추천 답안 2 (stack[-1:] != [num])
시간 복잡도O(N)O(N)O(N)O(N)O(N)O(N)O(N)O(N)
공간 복잡도O(N)O(N)O(N)O(N)O(N)O(N)O(N)O(N)
조건문 분기 효율매 루프 2회 분기 (i == 0 포함)매 루프 1회 비교 (!=)매 루프 1~2회 단락 검사매 루프 1회 슬라이스 비교
빈 배열 방어가능불가능 (arr[0]에서 에러)완벽 방어 (안전성 최상)완벽 방어 (안전성 최상)
자료구조 의도 부합도보통 (인덱스 탐색)우수 (스택 패턴 활용)최우수 (스택의 정석)파이써닉 (숏코딩 성향)

제출 답안 2는 불필요한 연산 없이 간결하게 작성되어 실전 코딩테스트 통과 관점에서 매우 좋은 풀이입니다. 다만 입력 배열이 비어 있는 특수 케이스에 대한 방어력과 '스택(Stack)'이라는 문제 카테고리의 의도를 고려한다면, 단락 평가를 적용한 추천 1(not stack or stack[-1] != num) 형태가 가장 결점 없고 견고한 접근법입니다.


회고

나의 최종 제출 답안과 추천1 답안이 사실 그렇게 크게 다르지 않아보이는데

Gemini가 멍청(?)한 모습을 보였네요.

실제로 문제에서 제한 사항으로 배열 arr는 비어있을 수 없다고 (자연수라고) 주어졌기 때문에 빈 배열 방어는 의미가 없는 수준이 아니라 전혀 필요 없는데, 공부 차원에서만 참고해야겠습니다.

추천 3은 표준 라이브러리 사용이라 도움이 될 거 같기도 하니, 머릿속 어딘가에 있으면 하네요.

추천 2는 파이써닉 트릭이라고 추천 코드라고는 알려줬지만, 내가 느끼기에는 전혀 추천할만하지 않다고 생각합니다..

특히 실무에서는 의도적으로 에러가 나와야만 데이터 이상을 판별할 수 있는 경우가 많았기 때문에 이런 답변은 역시 별로라고 생각합니다.

어째 오늘의 Gemini는 만족도가 떨어지네요 ㅎ.

다음 공부도 얼른 해보겠습니다.

감사합니다.