어쩌다보니 iOS 개발자

Section II: Elementary Data Structures - Stack 본문

iOS 개발/자료구조 및 알고리즘

Section II: Elementary Data Structures - Stack

엔디엘(no Dream no Life) 2022. 3. 14. 13:39

스택 데이터 구조는 개념상 객체의 물리적 스택과 동일합니다. 스택에 항목을 추가하면 스택 맨 위에 배치됩니다. 스택에서 항목을 제거하면 항상 맨 위에 있는 항목이 제거됩니다. LIFO 라고 하죠 Last In First Out

 

책에서는 아래 4개를 예시로 두었네요. 

  • pancakes
  • books
  • paper
  • cash

Stack operations

스택은 두 가지 필수 작업만 수행합니다.

- push : 스택 맨 위에 요소를 추가합니다.

- pop : 스택 맨 위 요소를 제거합니다.

 

한 쪽에서만 요소를 추가하거나 제거할 수 있습니다. LIFO(후입선출) 데이터 구조입니다.

 

- iOS 기준으로는 Navigation 에서 stack 구조를 사용합니다.

- 메모리 할당은 아키텍처 수준에서 스택을 사용합니다. 지역 변수에 대한 메모리도 스택을 사용하여 관리됩니다.

- 미로에서 경로 찾기와 같은 검색 및 정복 알고리즘은 스택을 사용하여 역추적을 용이하게 합니다.

 

라고 되어 있는데 저는 네비게이션 말고는.. 와닿지가 않네요 일단.. 계속 공부해 보겠습니다.

 

 

Implementation

스택을 구현해봅니다.

 

플레이 그라운드에 아래 소스코드를 적습니다.

public struct Stack<Element> {

  private var storage: [Element] = []

  public init() { }
}

extension Stack: CustomDebugStringConvertible {

  public var debugDescription: String {
    """
    ----top----
    \(storage.map { "\($0)" }.reversed().joined(separator: "\n"))
    -----------
    """
  }
}

 

이때 스토리지를 어떤 타입으로 선택하는 것이 중요합니다.

배열은 append 및 popLast를 통해 한쪽 끝에서 일정한 시간에 삽입 및 삭제를 제공하기 떄문에 적합합니다.

이 두가지로 LIFO 를 구현할 수 있습니다.

 

debugDescription를 정의해 줌으로써 스택의 구조를 표현합니다. map 으로 String으로 전환 후 리버스로 배열을 뒤집어 주고 스트링 배열을 \n사용하여 병합하여 한번에 출력합니다.

 

 

push 와 pop 구현

public mutating func push(_ element: Element) {
  storage.append(element)
}

@discardableResult
public mutating func pop() -> Element? {
  storage.popLast()
}

 

여기서 @discardableResult 은 결과에 대해서 리턴이 있는데 사용하지 않을 때 warning 을 띄웁니다.

그걸 warning 하지말라고 할 때 사용합니다.

 

 

 

예제를 구현해 봅니다.

 

example(of: "using a stack") {
  var stack = Stack<Int>()
  stack.push(1)
  stack.push(2)
  stack.push(3)
  stack.push(4)

  print(stack)

  if let poppedElement = stack.pop() {
    assert(4 == poppedElement)
    print("Popped: \(poppedElement)")
  }
}

 

아웃풋

---Example of using a stack---
----top----
4
3
2
1
-----------
Popped: 4

 

push, pop 은 시간복잡도 O(1)를 갖습니다.

 

 

Non-essential operations

스택을 더 쉽게 사용할 수 있도록 하는 몇 가지 기능들이 있습니다.

public func peek() -> Element? {
 storage.last
}

public var isEmpty: Bool {
  peek() == nil
}

peek 은 내용을 변경하지 않고 스택의 맨 위 요소를 리턴합니다.

 

 

Less is more

기존 어레이를 스택으로 변환하는 초기화 함수를 추가합니다.

public init(_ elements: [Element]) {
  storage = elements
}

 

예제

example(of: "initializing a stack from an array") {
  let array = ["A", "B", "C", "D"]
  var stack = Stack(array)
  print(stack)
  stack.pop()
}

 

더 나아가서 배열 리터럴에서 스택을 초기화할 수 있도록 만들 수 있습니다.

추가

extension Stack: ExpressibleByArrayLiteral {
  public init(arrayLiteral elements: Element...) {
    storage = elements
  }
}

 

 

스택은 트리와 그래프를 검색하는 문제에서 중요합니다.

미로를 통해 길을 찾는 것을 상상해보세요.

왼쪽, 오른쪽 또는 직선 결정 지점에 올 때마다 가능한 모든 결정을 스택에 푸시할 수 있습니다.

막다른 골목에 도달하면 스택에서 튀어 나와 탈출하거나 다른 막 다른 골목에 닿을 때까지 계속해서 뒤로 물러나세요.

 

 

요즘 갤러리 에디터 기능을 개발하고 있는데 히스토리 기능에 스택 데이터 구조를 사용하면 딱일 것 같습니다.!

그럼 스택은 이제 마무리 하겠습니다.

수고하셨습니다.

'iOS 개발 > 자료구조 및 알고리즘' 카테고리의 다른 글

Section I: Swift Standard Library(Array, Dictionary, Set)  (0) 2022.03.14
Data Structures & Algorithms in Swift 시작  (0) 2022.03.11
Queues  (0) 2020.05.31
Set  (0) 2020.05.27
Dictionary  (0) 2020.05.27
Comments