스위프트 코딩테스트 강좌, 오큰수 구하기

작성자: 조광형 | 작성일: 2024년 11월 26일

1. 문제 정의

오큰수는 주어진 수열에서 특정 요소의 오른쪽에 위치하며, 그 요소보다 큰 첫 번째 수를 의미합니다. 만약 그러한 수가 없다면 -1을 반환합니다. 즉, 수열 a[0], a[1], ..., a[n-1]가 있을 때, 각 a[i]에 대해 a[j] (j > i)를 만족하는 가장 작은 j를 찾는 것이 목표입니다.

예를 들어, 주어진 배열이 [2, 1, 4, 3]라면, 각 요소의 오큰수는 다음과 같습니다:

  • 2의 오큰수는 4입니다.
  • 1의 오큰수는 4입니다.
  • 4의 오큰수는 -1입니다.
  • 3의 오큰수는 -1입니다.

결과적으로 반환되는 오큰수 배열은 [4, 4, -1, -1]가 됩니다.

2. 문제 접근 방법

이 문제를 해결하기 위해 스택을 사용하는 방법을 고려하겠습니다. 스택은 LIFO(Last In First Out) 구조로, 요소를 삽입하거나 삭제할 때 매우 효율적인 자료구조입니다. 오큰수를 찾는 과정에서 다음의 절차를 따릅니다:

  1. 빈 스택을 초기화합니다.
  2. 배열을 왼쪽에서 오른쪽으로 순회합니다.
  3. 현재 요소 a[i]를 확인하고:
    1. 스택이 비어있지 않고, 스택의 최상단 요소 인덱스가 가리키고 있는 수가 a[i]보다 작으면:
      • 스택의 최상단 인덱스에 대해 오큰수를 a[i]로 설정합니다.
      • 스택에서 해당 인덱스를 제거합니다.
    2. 현재 인덱스를 스택에 추가합니다.
  4. 배열의 모든 요소를 순회한 후, 스택에 남아있는 인덱스에 대해 오큰수를 -1로 설정합니다.

이 방법은 시간 복잡도 O(n)으로, 각 요소를 한 번만 스택에 추가하고 제거하기 때문입니다. 이는 효율적이며, 결론적으로 큰 입력값에서도 좋은 성능을 발휘할 수 있습니다.

3. 스위프트 코드 구현

이제 위에서 설명한 알고리즘을 스위프트로 구현해 보겠습니다. 아래는 오큰수를 계산하는 스위프트 함수입니다:

import Foundation

func nextGreaterElement(_ nums: [Int]) -> [Int] {
    let n = nums.count
    var result = Array(repeating: -1, count: n) // 결과 배열 초기화
    var stack = [Int]() // 인덱스를 저장할 스택
    
    for i in 0..

이 코드는 먼저 빈 결과 배열과 스택을 초기화한 후, 각 요소에 대해 왼쪽에서 오른쪽으로 탐색하며 오큰수를 찾습니다. 스택의 최상단 요소가 현재 요소보다 작다면, 스택에서 제거하고 오큰수를 설정하는 과정을 반복합니다. 최종적으로 결과 배열을 반환합니다.

4. 테스트 및 검증

작성한 코드를 여러 테스트 케이스로 검증해보겠습니다. 다음은 다양한 입력을 사용한 테스트입니다:

let test1 = [2, 1, 4, 3]
let test2 = [1, 3, 2, 4]
let test3 = [5, 4, 3, 2, 1]
let test4 = [1, 2, 3, 4, 5]

print(nextGreaterElement(test1)) // [4, 4, -1, -1]
print(nextGreaterElement(test2)) // [3, 4, 4, -1]
print(nextGreaterElement(test3)) // [-1, -1, -1, -1, -1]
print(nextGreaterElement(test4)) // [-1, -1, -1, -1, -1]

모든 테스트 케이스에서 예상한 결과와 일치하는 것을 확인할 수 있었습니다. 따라서 구현한 알고리즘이 올바르게 작동함을 알 수 있습니다.

5. 최적화 및 추가 고려사항

오큰수 구하기 문제를 스택을 이용하여 O(n)으로 해결할 수 있다는 점은 매우 유용합니다. 그러나 최적화를 더 고민해볼 여지도 있습니다. 예를 들어, 원형 배열을 응용하여 여러 번 순환하는 경우, 스택의 크기를 조절하여 메모리 사용량을 줄일 수 있습니다.

스윙(Swing) 서비스와 같은 대규모 어플리케이션에서는 사용자 입력에 따라 데이터의 동적 변경이 발생할 수 있습니다. 이 경우, 각 이벤트에도 최적의 성능을 유지하기 위해 적절한 데이터 구조를 사용하는 것이 중요합니다.

따라서 이 문제는 단순히 오큰수를 구하는 것 이상의 의미를 가질 수 있으며, 메모리 효율성, 처리 성능, 및 활용 가능성 등 다양한 요소들을 고려해야 합니다.

결론

스위프트에서 오큰수를 구하는 알고리즘 문제를 다루면서, 스택이 매우 유용한 자료구조라는 것을 다시금 확인할 수 있었습니다. 이 문제는 알고리즘 기초를 다지는 데 도움이 될 뿐만 아니라, 더 나아가 다양한 데이터 처리 문제를 해결하는 데도 응용될 수 있는 기초적인 예시입니다. 효율적인 알고리즘 설계의 중요성을 명심하며, 반복적인 연습과 다양한 문제 해결 경험을 통해 더욱 발전할 수 있기를 바랍니다.

감사합니다!