내가 iOS 개-발자라니.

옆집 스위프트: 1-3) 데이터 구조 - 스택(Stack)과 큐(Queue) 본문

This is da(大) SWIFT

옆집 스위프트: 1-3) 데이터 구조 - 스택(Stack)과 큐(Queue)

옆집개 2025. 3. 12. 15:12

오늘은 스택과 큐!

솔직히 현업에서 많이 안썼지만 개념은 알아두면 좋다. 스위프트로 해보자,

3. 스택과 큐

  • 스택(stack): LIFO(Last In First Out, 후입선출)
// swift에서는 배열이나 연결 리스트를 이용하여 스택 구현함
// 일단 배열을 이용하여 스택 구현(구조체를 이용)
struct Stack<Element> {
	private var elements: [Element] = []

	// 요소 추가
	mutating func push(_ element: Element) {
		elements.append(element)
	}
	
	// 마지막에 추가된 요소를 빼고 나머지 반환(스택이 비어있을 경우 nil 반환)
	mutating func pop() -> Element? {
		return elements.popLast()
	}
	
	// 마지막에 추가된 요소를 확인(스택이 비어있을 경우 nil 반환)
	func peek() -> Element? {
		return elements.last
	}
	
	// 스택이 비어있는지 확인(true or false 반환)
	var isEmpty: Bool {
		return elements.isEmpty
	}
	
	// 스택에 저장된 요소의 개수를 반환
	var count: Int {
		return elements.count
	}
}

// 스택 호출 예시
// 정수형 스택 생성
var intStack = Stack<Int>()

// 요소 추가(push)
intStack.push(10)
intStack.push(20)
intStack.push(30)
print(intStack) // Stack<Int>(elements: [10, 20, 30])

// 스택의 요소 개수 확인
print("스택에 있는 요소 개수: \\(intStack.count)") // 출력: 스택에 있는 요소 개수: 3

// 최상단 요소 확인(peek)
if let topElement = intStack.peek() {
    print("스택의 최상단 요소: \\(topElement)") // 출력: 스택의 최상단 요소: 30
}

// 요소 제거(pop)
if let poppedElement = intStack.pop() {
    print("스택에서 제거된 요소: \\(poppedElement)") // 출력: 스택에서 제거된 요소: 30
}

// pop 후 스택 상태 확인
print("남은 요소 개수: \\(intStack.count)") // 출력: 남은 요소 개수: 2
if let newTop = intStack.peek() {
    print("새로운 최상단 요소: \\(newTop)") // 출력: 새로운 최상단 요소: 20
}

// 스택이 비어있는지 확인
print("스택이 비어있나요? \\(intStack.isEmpty)") // 출력: 스택이 비어있나요? false

// 모든 요소 제거
_ = intStack.pop() // 20 제거
_ = intStack.pop() // 10 제거
print("스택이 비어있나요? \\(intStack.isEmpty)") // 출력: 스택이 비어있나요? true

// 문자열 타입의 스택 사용 예시
var stringStack = Stack<String>()
stringStack.push("안녕")
stringStack.push("하세요")
stringStack.push("스위프트")

// 모든 요소 출력하기
while !stringStack.isEmpty {
    if let element = stringStack.pop() {
        print(element)
    }
}
// 출력:
// 스위프트
// 하세요
// 안녕
  • 스택은 언제 쓰일까?
    • 네비게이션 관리:
      • UINavigationController는 내부적으로 스택을 사용하여 뷰 컨트롤러를 관리
      • 화면 이동 시 push와 pop 메서드를 사용하는 것이 스택의 원리를 반영
    • 되돌리기(Undo) 기능 구현
      • 사용자 작업 기록을 스택에 저장하여 되돌리기 기능을 구현
    • 괄호 검증
      • 문자열에서 괄호의 짝이 맞는지 검증할 때 스택을 사용
    • 깊이 우선 탬색(DFS) 알고리즘 구현에 사용
    • 메모리 관리
      • 함수 호출 스택 관리에 사용
  • 큐(queue): FIFO(First In First Out, 선입선출)
// 마찬가지로 swift에서는 배열이나 연결 리스트를 이용하여 큐를 구현함
// 배열을 이용하여 큐 구현(구조체를 이용)
struct Queue<Element> {
	private var elements: [Element] = []
	
	// 요소 추가
	mutating func enqueue(_ element: Element) {
		elements.append(element)
	}
	
	// 가장 처음에 추가된 요소를 빼고 나머지 반환(큐가 비어있을 경우 nil 반환)
	mutating func dequeue() -> Element? {
		if isEmpty {
			return nil
		}
		return elements.removeFirst()
	}
	
	// 가장 처음에 추가된 요소를 확인(큐가 비어있을 경우 nil 반환)
	func peek() -> Element? {
		return elements.first
	}
	
	// 큐가 비어있는지 확인(true or false 반환)
	var isEmpty: Bool {
		return elements.isEmpty
	}
	
	// 큐에 저장된 요소의 개수를 반환
	var count: Int {
		return elements.count
	}
}

// 큐 호출 예시
// 정수형 큐 생성
var intQueue = Queue<Int>()

// 요소 추가(enqueue)
intQueue.enqueue(10)
intQueue.enqueue(20)
intQueue.enqueue(30)
print(intQueue) // Queue<Int>(elements: [10, 20, 30])

// 큐의 요소 개수 확인
print("큐에 있는 요소 개수: \\(intQueue.count)") // 출력: 큐에 있는 요소 개수: 3

// 맨 앞 요소 확인(peek)
if let frontElement = intQueue.peek() {
    print("큐의 맨 앞 요소: \\(frontElement)") // 출력: 큐의 맨 앞 요소: 10
}

// 요소 제거(dequeue)
if let dequeuedElement = intQueue.dequeue() {
    print("큐에서 제거된 요소: \\(dequeuedElement)") // 출력: 큐에서 제거된 요소: 10
}

// dequeue 후 큐 상태 확인
print("남은 요소 개수: \\(intQueue.count)") // 출력: 남은 요소 개수: 2
if let newFront = intQueue.peek() {
    print("새로운 맨 앞 요소: \\(newFront)") // 출력: 새로운 맨 앞 요소: 20
}

// 큐가 비어있는지 확인
print("큐가 비어있나요? \\(intQueue.isEmpty)") // 출력: 큐가 비어있나요? false

// 모든 요소 제거
_ = intQueue.dequeue() // 20 제거
_ = intQueue.dequeue() // 30 제거
print("큐가 비어있나요? \\(intQueue.isEmpty)") // 출력: 큐가 비어있나요? true

// 문자열 타입의 큐 사용 예시
var stringQueue = Queue<String>()
stringQueue.enqueue("멍뭉이가")
stringQueue.enqueue("멍멍")
stringQueue.enqueue("멍멍멍")

// 모든 요소 출력하기
while !stringQueue.isEmpty {
    if let element = stringQueue.dequeue() {
        print(element)
    }
}
// 출력:
// 멍뭉이가
// 멍멍
// 멍멍멍
  • 큐는 언제쓰일까?
    • 비동기 작업 관리
      • DispatchQueue를 사용하여 비동기 작업이나 동시성 작업을 관리
    • 애니메이션 시퀀스
      • 여러 애니메이션을 순차적으로 실행해야 할 때 큐를 사용
    • 데이터 버퍼링
      • 네트워크 요청이나 사용자 입력을 임시로 저장할 때 사용
    • 너비 우선 탐색 알고리즘 구현에 사용
    • 이벤트 처리
      • iOS의 이벤트 루프는 큐 방식으로 이벤트를 처리
    • 프린터 대기열, 다운로드 관리자 같은 기능 구현에 사용

 

실제 iOS/Swift 개발에서는 Foundation과 UIKit이 제공하는 내장 컬렉션이나 제어 메커니즘을 많이 사용하지만, 특정 알고리즘이나 데이터 처리 로직을 구현할 때 직접 스택이나 큐를 구현하여 사용한다.