Bibi's DevLog ๐Ÿค“๐ŸŽ

[Swift ์ž๋ฃŒ๊ตฌ์กฐ] 4. Stack ์Šคํƒ (๋ฐฐ์—ด/์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ) ๋ณธ๋ฌธ

ํ”„๋กœ๊ทธ๋ž˜๋ฐ/Data Structures ์ž๋ฃŒ๊ตฌ์กฐ

[Swift ์ž๋ฃŒ๊ตฌ์กฐ] 4. Stack ์Šคํƒ (๋ฐฐ์—ด/์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ)

๋น„๋น„ bibi 2022. 12. 21. 21:01

Data Structure in Swift (Stack)โ€Š-โ€ŠPart 3

Swift Algorithm Club: Swift Stack Data Structure

์ด ๊ธ€์€ ์œ„์˜ ๋‘ ๋ฌธ์„œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด์„ ๋ฐ ์ •๋ฆฌํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

์Šคํƒ Stack

์Šคํƒ์€ ์š”์†Œ๋ฅผ ์„ ํ˜•(linear)์œผ๋กœ ์ •๋ฆฌํ•˜๊ฒŒ ํ•ด ์ฃผ๋Š” ์ž๋ฃŒ๊ตฌ์กฐ์ž…๋‹ˆ๋‹ค.
์Šคํƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ LIFO(Last In First Out) ๋งค์ปค๋‹ˆ์ฆ˜์„ ํ†ตํ•ด ์š”์†Œ๋ฅผ ์‚ฝ์ž…ํ•˜๊ณ  ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.
์ด๋Š” ๊ฐ€์žฅ ๋‚˜์ค‘์— ๋”ํ•ด์ง„ ์š”์†Œ๊ฐ€ ๊ฐ€์žฅ ๋จผ์ € ์‚ญ์ œ๋จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์Šคํƒ์˜ ์—ฐ์‚ฐ

์Šคํƒ์€ ๊ต‰์žฅํžˆ ํ•œ์ •๋œ ๋ฒ”์œ„์˜ ์—ฐ์‚ฐ์„ ์ œ๊ณตํ•œ๋‹ค.

  • push() : O(1). ์Šคํƒ์— ์ƒˆ๋กœ์šด ์š”์†Œ๋ฅผ ์‚ฝ์ž…ํ•˜๋Š” ์—ฐ์‚ฐ
  • pop() : O(1). ์Šคํƒ์˜ ๋งจ ์œ„์˜ ์š”์†Œ๋ฅผ ์‚ญ์ œํ•˜๋Š” ์—ฐ์‚ฐ
  • peek() : ์Šคํƒ์˜ ๋งจ ์œ„์˜ ์š”์†Œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์กฐํšŒํ•˜๋Š” ์—ฐ์‚ฐ
    • ์Šคํƒ์€ ์š”์†Œ๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค. ์˜ˆ์™ธ์ ์œผ๋กœ peek()์„ ํ†ตํ•ด ๋งจ ์œ„์˜ ์š”์†Œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค.

Swift์—์„œ์˜ ์Šคํƒ

์Šคํƒ์€ ๊ฑฐ์˜ ๋ชจ๋“  ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ์‚ฌ์šฉ๋œ๋‹ค. iOS์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ณณ์— ์Šคํƒ ์ž๋ฃŒ๊ตฌ์กฐ๊ฐ€ ์‚ฌ์šฉ๋œ๋‹ค.

  • UINavigationController๊ฐ€ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ๋“ค์„ pushํ•˜๊ณ  popํ•  ๋•Œ
  • ์ง€์—ญ ๋ณ€์ˆ˜์— ๋Œ€ํ•œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•  ๋•Œ

โ—๏ธ Swift๋Š” ์Šคํƒ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ์ง์ ‘ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.

Swift๋กœ ์Šคํƒ ๊ตฌํ˜„ํ•˜๊ธฐ - (1) ๋ฐฐ์—ด ์‚ฌ์šฉ

์ฃผ์„์ด ์—†๋Š” ์ „์ฒด ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค. (๋ฐฐ์—ด, ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ ๊ตฌํ˜„๋ฐฉ์‹ ๋ชจ๋‘ ํฌํ•จ)

struct Stack<Element> {
    fileprivate var array: [Element] = []

    // isEmpty : ์Šคํƒ์ด ๋น„์–ด์žˆ๋Š”์ง€ ํ™•์ธ
    var isEmpty: Bool {
        return array.isEmpty
    }

    // count : ์Šคํƒ์ด ๋ช‡ ๊ฐœ์˜ ์š”์†Œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ
    var count: Int {
        return array.count
    }

    // push : ์Šคํƒ ๋งจ ์œ„์— ์ƒˆ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•จ
    mutating func push(_ element: Element) {
        array.append(element)
    }

    // pop : ์Šคํƒ ๋งจ ์œ„์˜ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•จ
    // ์Šคํƒ์ด ๋น„์–ด ์žˆ์„ ๋•Œ nil ๋ฐ˜ํ™˜
    mutating func pop() -> Element? {
        return array.popLast()
    }

    // peek : ์Šคํƒ ๋งจ ์œ„์˜ ์š”์†Œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์กฐํšŒํ•จ
    // ์—ญ์‹œ ์Šคํƒ์ด ๋น„์–ด ์žˆ์„ ๋•Œ nil ๋ฐ˜ํ™˜
    func peek() -> Element? {
        return array.last
    }
}

extension Stack: CustomStringConvertible {
    var description: String {
        let topDivider = "---Stack---\n"
        let bottomDivider = "\n-----------\n"

        let stackElements = array.map { "\($0)" }.reversed().joined(separator: "\n")
        return topDivider + stackElements + bottomDivider
    }
}
  • isEmpty : ์Šคํƒ์ด ๋น„์–ด์žˆ๋Š”์ง€ ํ™•์ธ
  • count : ์Šคํƒ์ด ๋ช‡ ๊ฐœ์˜ ์š”์†Œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ
  • push() : ์Šคํƒ ๋งจ ์œ„์— ์ƒˆ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•จ
    • ์ด ์Šคํƒ์€ ๋‚ด๋ถ€์ ์œผ๋กœ ๋ฐฐ์—ด์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, pop() ์„ ํ†ตํ•ด ๋ฐฐ์—ด์˜ ๋งจ ๋์— ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋ฐฐ์—ด์˜ ๊ฒฝ์šฐ ์š”์†Œ๋ฅผ ๋งจ ์•ž์ด๋‚˜ ์ค‘๊ฐ„์— ์‚ฝ์ž…ํ•  ๋•Œ(O(n)) ์— ๋น„ํ•ด ๋งจ ๋์— ์‚ฝ์ž…ํ•˜๋Š” ๊ฒƒ(O(1)) ์ด ๋” ๋น ๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
    • Swift๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ์ธ append() ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. - O(1)
  • pop() : ์Šคํƒ ๋งจ ์œ„์˜ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•จ
    • ์ด ์Šคํƒ์€ ๋งจ ๋์—์„œ๋ถ€ํ„ฐ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฏ€๋กœ, ์‚ญ์ œํ•  ๋•Œ๋„ ๋งจ ๋์˜ ์š”์†Œ๋ฅผ ์‚ญ์ œํ•ด์•ผ ํ•œ๋‹ค.
    • Swift๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ์ธ popLast() ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. - O(1)
    • ๋ฆฌํ„ด๊ฐ’์ด ์˜ต์…”๋„์ด๋‹ค : ์Šคํƒ์ด ๋น„์–ด ์žˆ๋Š” ์ƒํƒœ์—์„œ pop() ์„ ํ•˜๋ฉด nil ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • peek() : ์Šคํƒ ๋งจ ์œ„์˜ ์š”์†Œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์กฐํšŒํ•จ
    • ์—ญ์‹œ ๋งจ ๋์˜ ์š”์†Œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์กฐํšŒํ•ด ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋œ๋‹ค.
    • Swift๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํ”„๋กœํผํ‹ฐ์ธ .last ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. - O(1)
    • ๋ฆฌํ„ด๊ฐ’์ด ์˜ต์…”๋„์ด๋‹ค : ์Šคํƒ์ด ๋น„์–ด ์žˆ๋Š” ์ƒํƒœ์—์„œ peek() ์„ ํ•˜๋ฉด nil ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • extension Stack: CustomStringConvertible {...}
    • ์Šคํƒ์„ ์ถœ๋ ฅ(print) ํ–ˆ์„ ๋•Œ ๋ณด๊ธฐ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • reversed() : ์Šคํƒ์˜ ์š”์†Œ๋“ค์ด ๋ฐฐ์—ด์— ๋’ค์—์„œ๋ถ€ํ„ฐ ์ €์žฅ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์ถœ๋ ฅํ•  ๋•Œ๋Š” ์ตœ๊ทผ ์š”์†Œ(๋งจ ๋’ค์˜ ์š”์†Œ)๊ฐ€ ๋งจ ์œ„์— ์ถœ๋ ฅ๋˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
    • .joined(separator: "\n") : ์š”์†Œ ์‚ฌ์ด์— ์ค„๋ฐ”๊ฟˆ๋ฌธ์ž(\n) ๋ฅผ ๋„ฃ์–ด ๋ณด๊ธฐ ํŽธํ•˜๊ฒŒ ํ•œ๋‹ค.
  • <Element> : ์ œ๋„ค๋ฆญ ์‚ฌ์šฉ
    • ํŠน์ • ํƒ€์ž…์˜ ์š”์†Œ ๋ฟ ์•„๋‹ˆ๋ผ ๋ชจ๋“  ํƒ€์ž…์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด.

Swift๋กœ ์Šคํƒ ๊ตฌํ˜„ํ•˜๊ธฐ - (2) ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ ์‚ฌ์šฉ

์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ์™€ ๊ฐ™์ด Node์˜ ๊ฐœ๋…์„ ์‚ฌ์šฉํ•ด ์Šคํƒ์„ ๊ตฌํ˜„ํ•ด ๋ณธ๋‹ค.

// Node class to hold the data
class Node<T> {

    let value: T
    var next: Node?

    init(value: T) {
        self.value = value
    }
}

// Stack  class to hold the all items
class Stack<T> {

    var top: Node<T>?

    func push(_ value: T) {
        let currentTop = top
        top = Node(value: value)
        top?.next = currentTop
    }

    @discardableResult
    func pop() -> T? {
        let currentTop = top
        top = top?.next
        return currentTop?.value
    }

    func peek() -> T? {
        return top?.value
    }
}

extension Node: CustomStringConvertible {
    var description: String {
        guard let next = next else { return "\(value)" }
        return "\(value)\n" + String(describing: next) // ์—ฌ๊ธฐ์„œ ์—ฐ์‡„์ ์œผ๋กœ ๋…ธ๋“œ๋ฅผ String์œผ๋กœ ๋ณ€ํ™˜
    }
}

extension Stack: CustomStringConvertible {
    var description: String {
        guard let top = top else { return "---- Stack is EMPTY ----" }
        return "---- Stack ----\n" + String(describing: top) + "\n---------------"
    }
}

var stackString = Stack<String>()
print(stackString.pop())
  • top : ์Šคํƒ์˜ ์ตœ์ƒ์œ„ ๋…ธ๋“œ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
  • push() : ์Šคํƒ ๋งจ ์œ„์— ์ƒˆ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•จ
    • ๊ธฐ์กด top ์œ„์— ์ƒˆ ๋…ธ๋“œ๋ฅผ ๋งŒ๋“ค์–ด ์Œ“๋Š”๋‹ค.
  • pop() : ์Šคํƒ ๋งจ ์œ„์˜ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•จ
    • ์ตœ์ƒ์œ„ ๋…ธ๋“œ์˜ next(nil)๋ฅผ ์ตœ์ƒ์œ„ ๋…ธ๋“œ๋กœ ๋งŒ๋“  ๋‹ค์Œ, ๊ธฐ์กด ์ตœ์ƒ์œ„ ๋…ธ๋“œ์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    • ๋ฆฌํ„ด๊ฐ’์ด ์˜ต์…”๋„์ด๋‹ค : ์Šคํƒ์ด ๋น„์–ด ์žˆ๋Š” ์ƒํƒœ์—์„œ pop() ์„ ํ•˜๋ฉด nil ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • peek() : ์Šคํƒ ๋งจ ์œ„์˜ ์š”์†Œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์กฐํšŒํ•จ
    • ํ˜„์žฌ ์ตœ์ƒ์œ„ ๋…ธ๋“œ (= top) ์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค
    • ๋ฆฌํ„ด๊ฐ’์ด ์˜ต์…”๋„์ด๋‹ค : ์Šคํƒ์ด ๋น„์–ด ์žˆ๋Š” ์ƒํƒœ์—์„œ peek() ์„ ํ•˜๋ฉด nil ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • extension Node: CustomStringConvertible {...}
    • ๋…ธ๋“œ๋ฅผ ์ถœ๋ ฅ(print) ํ–ˆ์„ ๋•Œ ๋ณด๊ธฐ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • next๊ฐ€ ์žˆ์œผ๋ฉด value์™€ next๋ฅผ, ์—†์œผ๋ฉด value๋ฅผ ๋ฐ˜ํ™˜
    • next ๋˜ํ•œ Node์ด๋ฏ€๋กœ next๊ฐ€ ์žˆ๋‹ค๋ฉด ๋‹ค์Œ Node๋„ ์—ฐ์‡„์ ์œผ๋กœ ์ถœ๋ ฅํ•˜๊ฒŒ ๋œ๋‹ค.
  • extension Stack: CustomStringConvertible {...}
    • ์Šคํƒ์„ ์ถœ๋ ฅ(print) ํ–ˆ์„ ๋•Œ ๋ณด๊ธฐ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • top์˜ ๊ฐ’์ด ์žˆ์œผ๋ฉด top์˜ ๊ฐ’์„, ์—†์œผ๋ฉด ๋น„์–ด์žˆ๋‹ค๋Š” ๋ฌธ๊ตฌ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    • top์˜ ๊ฐ’์ด ์žˆ์œผ๋ฉด String(describing: top) ๋ฅผ ์ถœ๋ ฅํ•˜๋Š”๋ฐ, ์ด๋•Œ top ์ด Node์ด๋ฏ€๋กœ Node์˜ CustomStringConvertible์— ๋”ฐ๋ผ ์ถœ๋ ฅ๋œ๋‹ค.
  • <Element> : ์ œ๋„ค๋ฆญ ์‚ฌ์šฉ
    • ํŠน์ • ํƒ€์ž…์˜ ์š”์†Œ ๋ฟ ์•„๋‹ˆ๋ผ ๋ชจ๋“  ํƒ€์ž…์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด.

Swift๋กœ ์Šคํƒ ๊ตฌํ˜„ํ•˜๊ธฐ - (1)๋ฐฐ์—ด๊ณผ (2)์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ ๋น„๊ต

import Foundation

let startTime = CFAbsoluteTimeGetCurrent()

var stackString = Stack<String>()
stackString.push("First")
stackString.pop()
stackString.push("Second")
stackString.push("Third")
stackString.push("Third")
stackString.push("Third")
stackString.push("Third")
stackString.push("Third")
stackString.push("Third")
stackString.push("Third")
stackString.push("Third")
stackString.push("Third")
stackString.push("Third")
stackString.push("Third")
stackString.push("Third")
stackString.push("First")
stackString.pop()
stackString.push("Second")
stackString.push("Third")

let processTime = CFAbsoluteTimeGetCurrent() - startTime
print(processTime) 

// 0.004179954528808594 - (1) ๋ฐฐ์—ด
// 0.0066149234771728516 - (2) ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ

์•„๋ฌด ๋ฐ์ดํ„ฐ๋‚˜ ๋„ฃ์–ด์„œ ๋‘˜์˜ ์„ฑ๋Šฅ์„ ๋น„๊ตํ•ด ๋ดค๋Š”๋ฐ, ๋ฐฐ์—ด์€ ๋ณดํ†ต 0.004์ดˆ๋Œ€๊ฐ€ ๋‚˜์™”๊ณ  ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋Š” ๋ณดํ†ต 0.006์ดˆ๋Œ€๊ฐ€ ๋‚˜์™”๋‹ค.

์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋” ๋น ๋ฅผ๊ฑฐ๋ผ๋Š” ๋‚ด ์ƒ๊ฐ๊ณผ๋Š” ๋‹ค๋ฅธ ๊ฒฐ๊ณผ์˜€๋‹ค๐Ÿ˜ฎ

๊ทธ๋ž˜์„œ ํ์™€๋Š” ๋‹ฌ๋ฆฌ ์Šคํƒ์€ ๋ฐ˜๋“œ์‹œ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ•  ํ•„์š”๋Š” ์—†๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

์ด๋ฏธ ์—ฐ๊ฒฐ๋ฆฌ์ŠคํŠธ๋ฅผ ์“ธ๋•Œ์™€ ๊ฐ™์€ ์‹œ๊ฐ„๋ณต์žก๋„(O(1))์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋ฐฐ์—ด์˜ ๋ฉ”์„œ๋“œ์™€ ํ”„๋กœํผํ‹ฐ(append(), popLast(), .last)๋กœ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.