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

[Swift ์ž๋ฃŒ๊ตฌ์กฐ] 3. Linked List ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ (Singly Linked List) ๋ณธ๋ฌธ

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

[Swift ์ž๋ฃŒ๊ตฌ์กฐ] 3. Linked List ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ (Singly Linked List)

๋น„๋น„ bibi 2022. 12. 19. 22:41

Data Structure in Swift (Linked List)โ€Š-โ€ŠPart 2

๊ธฐ๋ณธ์ ์œผ๋กœ ์œ„ ๋ฌธ์„œ๋ฅผ ๋ฒˆ์—ญํ•ด ์ •๋ฆฌํ•œ ๋ฌธ์„œ์ด๋ฉฐ,

Data Structures & Algorithms in Swift, Chapter 6: Linked List

์ด ๋ฌธ์„œ๋ฅผ ํ•จ๊ป˜ ์ฐธ์กฐํ–ˆ์Šต๋‹ˆ๋‹ค.

Linked List ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ

  • ๋…ธ๋“œ๋ฅผ ๊ธฐ๋ณธ ๋‹จ์œ„๋กœ ํ•˜๋Š” ์„ ํ˜• ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ.
  • ๋…ธ๋“œ : ๋ฐ์ดํ„ฐ + ํฌ์ธํ„ฐ
    • ๋ฐ์ดํ„ฐ : ํ•ด๋‹น ๋…ธ๋“œ์— ์ €์žฅ๋œ ๊ฐ’
    • ํฌ์ธํ„ฐ : ๋‹ค์Œ ๋…ธ๋“œ์˜ ์œ„์น˜์— ๋Œ€ํ•œ ์ฐธ์กฐ๊ฐ’
  • ๋ฉ”๋ชจ๋ฆฌ์ƒ์— ์—ฐ์†์ ์œผ๋กœ ์ €์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค.
    • ๋ฐฐ์—ด์ฒ˜๋Ÿผ, ์„œ๋ธŒ์Šคํฌ๋ฆฝํŠธ๋กœ ์š”์†Œ์— ์ž„์˜์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค. (list[0] - ๋ถˆ๊ฐ€๋Šฅ)
  • ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋Š” ์—ฌ๋Ÿฌ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค
    • Singly Linked List ๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ (๋‹จ์ผ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ)
      • ๊ฐ ํ•ญ๋ชฉ์ด ๋‹ค์Œ ๋…ธ๋“œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋งŒ ๊ฐ€์ง
    • Doubly Linked List ์–‘๋ฐฉํ–ฅ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ
      • ๊ฐ ํ•ญ๋ชฉ์ด ์•ž, ๋’ค ๋…ธ๋“œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋ชจ๋‘ ๊ฐ€์ง
    • Circular Linked List ์›ํ˜• ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ

โญ•๏ธ ์ด๋Ÿด ๋•Œ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค

  • ๋ฐ์ดํ„ฐ์˜ ํฌ๊ธฐ๋ฅผ ๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ

โŒ ์ด๋Ÿด ๋•Œ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค

  • ๋งŽ์€ ์–‘์˜ ๋ฐ์ดํ„ฐ์— ํƒ์ƒ‰ ์—ฐ์‚ฐ์„ ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ
  • ๋ฐ์ดํ„ฐ ์ˆœํšŒ๋ฅผ ์ž์ฃผ ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ

Singly Linked List ๊ตฌํ˜„

๋‹จ๋ฐฉํ–ฅ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋Š” ๊ฐ ์š”์†Œ๊ฐ€ ๋…ธ๋“œ๋กœ ๋˜์–ด ์žˆ๋Š” ์—ฐ์†๋œ ๋ฐ์ดํ„ฐ๋“ค์ด๋‹ค.

  • node ๋…ธ๋“œ : ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ํ•˜๋‚˜์˜ ์š”์†Œ๋ฅผ ๋งํ•จ. value ์™€ next๋กœ ๊ตฌ์„ฑ
    • value : ๋…ธ๋“œ๊ฐ€ ๋‹ด๋Š” ๋ฐ์ดํ„ฐ ๊ฐ’
    • next : ๋‹ค์Œ ๋…ธ๋“œ์— ๋Œ€ํ•œ ํฌ์ธํ„ฐ
  • head : ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ์‹œ์ž‘ ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํฌ์ธํ„ฐ
  • tail : ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ์˜ ๋ ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํฌ์ธํ„ฐ

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

์ฃผ์„์ด ์—†๋Š” ์ „์ฒด ๊ตฌํ˜„ ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค.

Node

๋‹จ์ผ ์—ฐ๊ฒฐ ๋ฆฌ์ŠคํŠธ๋Š” ๋…ธ๋“œ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.

Node ํด๋ž˜์Šค์—์„œ๋Š” ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š” ๋…ธ๋“œ๋ฅผ ๋งŒ๋“ ๋‹ค.

๊ฐ ๋…ธ๋“œ๋Š” ์ž์‹ ์˜ ๋ฐ์ดํ„ฐ ๊ฐ’๊ณผ ํฌ์ธํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

// ์š”์†Œ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š” ํด๋ž˜์Šค
class Node<T>: CustomStringConvertible, Equatable {

    var value: T
    var next: Node?

    // required conforms to CustomStringConvertible
    var description: String {
        guard let next = next else { return "\(value)" }
        return "\(value) -> " + String(describing: next)
    }

    init(value: T, next: Node? = nil) {
        self.value = value
        self.next = next
    }

    // required conforms to Equatable
    static func ==(lhs: Node, rhs: Node) -> Bool {
        return lhs.next == rhs.next
    }
}
  • ์–ด๋–ค ์ข…๋ฅ˜์˜ ๋ฐ์ดํ„ฐ๋“  ๋‹ด์„ ์ˆ˜ ์žˆ๊ฒŒ ์ œ๋„ค๋ฆญ ์‚ฌ์šฉ
  • ๋‘ ๋…ธ๋“œ๋ฅผ ๋น„๊ตํ•˜๊ธฐ ์œ„ํ•ด == ์—ฐ์‚ฐ์ž๋ฅผ ์˜ค๋ฒ„๋กœ๋“œํ•จ

LinkedList

๋ชจ๋“  ๋…ธ๋“œ๋ฅผ ๋‹ด๋Š” ๊ตฌ์กฐ์ฒด์ธ LinkedList๋ฅผ ๋งŒ๋“ ๋‹ค.

// A struct to hold the information of all the nodes
struct LinkedList<T>: CustomStringConvertible {

    // to hold the starting point of list
    var head: Node<T>?

    // to hold the ending point of list
    var tail: Node<T>?

    init() { }

    // if head is nil simply list is empty right now
    var isEmpty: Bool {
        return head == nil
    }

    // required conforms to CustomStringConvertible
    var description: String {
        guard let head = head else { return "Empty list" }
        return String(describing: head)
    }
}
  • head : ์‹œ์ž‘ ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐํ•จ
  • tail : ๋ ๋…ธ๋“œ๋ฅผ ์ฐธ์กฐํ•จ

โ“ LinkedList์—์„œ head์™€ tail์„ ์™œ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?

→ ๋ชจ๋“  ๋…ธ๋“œ๋“ค์€ ์ฐธ์กฐ์— ์˜ํ•ด ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋ฆฌ์ŠคํŠธ๋ฅผ ์ˆœํšŒํ•˜๊ฑฐ๋‚˜ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‹œ์ž‘์ ๊ณผ ๋์ ์„ ๋ถ™์žก๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค.

๐Ÿ” Search ํƒ์ƒ‰

node(at:) : ๋ฆฌ์ŠคํŠธ์˜ ํŠน์ • ์œ„์น˜์˜ ๋…ธ๋“œ ์ฐพ๊ธฐ. O(i)

O(i) ์˜ i ๋Š” ์ธ๋ฑ์Šค๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

// node(at:) : O(i), i=index. ํŠน์ • ์ธ๋ฑ์Šค์˜ ๋…ธ๋“œ๋ฅผ ์ฐพ๊ธฐ
func node(at index: Int) -> Node<T>? {
    // if list is empty then simply return nil
    if isEmpty {
        return nil
    }

    var currentNode = head
    var currentIndex = 0
    while currentNode != nil && currentIndex < index {
        currentNode = currentNode?.next
        currentIndex += 1
    }
    return currentNode
}
  • ๋งŒ์•ฝ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋น„์–ด ์žˆ๋‹ค๋ฉด, ์ฐพ๋Š” ๋…ธ๋“œ๊ฐ€ ๋‹น์—ฐํžˆ ์—†์œผ๋ฏ€๋กœ nil ๋ฐ˜ํ™˜
  • head์˜ ์ธ๋ฑ์Šค๋ฅผ 0์œผ๋กœ ํ•˜๊ณ , head๋ถ€ํ„ฐ ์—ฐ๊ฒฐ๋œ ๋…ธ๋“œ๋ฅผ ์ฐจ๋ก€๋กœ ํƒ์ƒ‰

count() : ๋ฆฌ์ŠคํŠธ์˜ ๋…ธ๋“œ์˜ ์ˆ˜ ๋ฐ˜ํ™˜. O(n)

// get number of nodes in the list
func count() -> Int {
    var currentNode = head
    var count = 0
    while currentNode != nil {
        currentNode = currentNode?.next
        count += 1
    }
    return count
}
  • head๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด count๋ฅผ ํ•˜๋‚˜์”ฉ ์„ธ๋ฉฐ tail์— ๋„๋‹ฌํ•  ๋•Œ๊นŒ์ง€ ๋ฐ˜๋ณต.

โž• Insertion ์‚ฝ์ž…

push(_:) : ๋ฆฌ์ŠคํŠธ์˜ ๋งจ ์•ž์— ๋…ธ๋“œ ์‚ฝ์ž…. O(1)

// push(_:) : O(1), ๋งจ ์•ž์— ์‚ฝ์ž… head-first insertion
mutating func push(_ value: T) {

    // create a new node and passing head (starting point) as a next node
    let newNode = Node(value: value, next: head)

    // change the head to the new node because head will hold the starting node
    head = newNode

    // if tail (ending node) is nil, it means head and tail pointing the same node as it is first node
    if tail == nil {
        tail = head
    }
}
  • ์ƒˆ ๋…ธ๋“œ๋ฅผ ๋งŒ๋“ค์–ด ๊ฐ’์„ ๋„ฃ๊ณ , ํฌ์ธํ„ฐ๋Š” ๊ธฐ์กด head๋กœ ์„ค์ •ํ•œ๋‹ค.
  • ์ƒˆ ๋…ธ๋“œ๋ฅผ ๋ฆฌ์ŠคํŠธ์˜ head๋กœ ๋งŒ๋“ ๋‹ค.
  • ๋งŒ์•ฝ tail์ด nil์ด๋ฉด ํ˜„์žฌ ๋„ฃ๋Š” ๊ฐ’์ด ์ฒซ ๋…ธ๋“œ์ž„์„ ์˜๋ฏธํ•œ๋‹ค.
    • ์›์†Œ๊ฐ€ 1๊ฐœ๋ฟ์ธ ๋งํฌ๋“œ๋ฆฌ์ŠคํŠธ์—์„œ, ๋ฆฌ์ŠคํŠธ์˜ ์ฒซ ์š”์†Œ๋Š” head ์ด์ž tail ์ด๋ฏ€๋กœ tail์— head๋ฅผ ํ• ๋‹นํ•œ๋‹ค.

append(_:) : ๋ฆฌ์ŠคํŠธ์˜ ๋งจ ๋’ค์— ๋…ธ๋“œ ์‚ฝ์ž…. O(1)

// append(_:) : O(1), ๋งจ ๋’ค์— ์‚ฝ์ž… tail-end insertion
mutating func append(_ value: T) {

    // if list is empty then simply perform push operation
    if isEmpty {
        self.push(value)
        return
    }

    // create new node and assign it to tail's next because tail will always point the last node
    tail?.next = Node(value: value)

    // replace the tail with tail's next because before doing it tail's next pointing the last node
    tail = tail?.next
}
  • ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋น„์–ด์žˆ๋‹ค๋ฉด append() == push() ์ด๋ฏ€๋กœ push()๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • ์ง€๊ธˆ ๋งจ ๋’ค์— ์‚ฝ์ž…ํ•˜๋ ค๋Š” ๋…ธ๋“œ๊ฐ€ ๋งˆ์ง€๋ง‰ ๋…ธ๋“œ๊ฐ€ ๋˜์–ด์•ผ ํ•˜๋ฏ€๋กœ, tail์˜ next์— ์ƒˆ๋กœ์šด ๊ฐ’์œผ๋กœ ๋…ธ๋“œ๋ฅผ ๋งŒ๋“ค์–ด ๋ถ™์ธ๋‹ค.
  • ์ƒˆ๋กœ ์„ค์ •ํ•œ tail์˜ next๊ฐ€ ์ƒˆ๋กœ์šด tail์ด ๋œ๋‹ค.

insert(_:after:) : ๋ฆฌ์ŠคํŠธ์˜ ํŠน์ • ์ธ๋ฑ์Šค์— ๋…ธ๋“œ ์‚ฝ์ž…. O(n)

ํŠน์ • ์œ„์น˜๋ฅผ ์ฐพ์•„์„œ(ํƒ์ƒ‰) ๋…ธ๋“œ๋ฅผ ์‚ฝ์ž…ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์‹œ๊ฐ„์ด ๋” ๊ฑธ๋ฆฐ๋‹ค.

O(n) ์˜ n์€ ์‚ฝ์ž… ์ธ๋ฑ์Šค์— ํ•ด๋‹นํ•œ๋‹ค.

// insert(_:after:) : O(n), ์ฃผ์–ด์ง„ ์ธ๋ฑ์Šค ๋’ค์— ์‚ฝ์ž…
mutating func insert(_ value: T, after index: Int) {

    // if given index is not valid as per current list, it will be added in last
    if index < 0 || index >= self.count() {
        self.append(value)
        return
    }

    // if node not found just before to given insert index, perform push operation
    if let beforeNode = self.node(at: index), let tail = tail {

        // if insert index ie equal to last, simply append new node at last
        if beforeNode == tail {
            self.append(value)
        } else {
            // create new node and assign as a next node for the before node
            beforeNode.next = Node(value: value, next: beforeNode.next)
        }
    } else {
        self.push(value)
    }
}
  • (์ด ๊ตฌํ˜„์—์„œ๋Š”) ์ฃผ์–ด์ง„ ์ธ๋ฑ์Šค๊ฐ€ ํ˜„์žฌ ๋ฆฌ์ŠคํŠธ์— ์œ ํšจํ•˜์ง€ ์•Š์œผ๋ฉด ๊ทธ๋ƒฅ append๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
  • node(at:) ๋ฅผ ํ™œ์šฉํ•ด ์‚ฝ์ž…ํ•˜๋ ค๋Š” ์œ„์น˜์˜ ๋…ธ๋“œ๋ฅผ ์ฐพ๋Š”๋‹ค
    • ๋งŒ์•ฝ ์‚ฝ์ž…ํ•˜๋ ค๋Š” ์œ„์น˜์˜ ๋…ธ๋“œ๊ฐ€ tail์ด๋ผ๋ฉด, ๊ทธ๋ƒฅ ๋งจ ๋์— ์‚ฝ์ž…ํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์œผ๋ฏ€๋กœ append๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
    • ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๊ธฐ์กด ๋…ธ๋“œ์˜ next๋กœ ์ƒˆ ๋…ธ๋“œ๋ฅผ ๋งŒ๋“ค์–ด ์‚ฝ์ž…ํ•˜๊ณ , ์ƒˆ ๋…ธ๋“œ์˜ next๋Š” ๊ธฐ์กด ๋…ธ๋“œ์˜ next๋กœ ์„ค์ •ํ•ด ์ด์–ด๋ถ™์—ฌ ์ค€๋‹ค.

โž– Remove ์‚ญ์ œ

pop() : ๋ฆฌ์ŠคํŠธ์˜ ๋งจ ์•ž์˜ ๋…ธ๋“œ ์‚ญ์ œ. O(1)

// pop() : O(1). ๋งจ ์•ž์—์„œ ์ œ๊ฑฐ.

@discardableResult
mutating func pop() -> T? {

    defer { // ํ•จ์ˆ˜ ์ข…๋ฃŒ ์ง์ „์— ์‹คํ–‰
        head = head?.next
        if isEmpty {
            tail = nil
        }
    }
    return head?.value
}
  • @discardableResult: pop() ์ดํ›„ ๋ฆฌํ„ด๊ฐ’์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„๋„ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๊ฒฝ๊ณ ๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š๋„๋ก ํ•จ.
  • defer { … }: ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋˜๊ธฐ ์ง์ „์— ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๋ธ”๋Ÿญ.
    • ์™œ ์ผ๋Š”๊ฐ€? defer ๋‚ด๋ถ€ ์ฝ”๋“œ๊ฐ€ ๋จผ์ € ์‹คํ–‰๋˜๊ณ  returnํ•˜๋ฉด head์˜ ๊ฐ’์ด ๋‹ฌ๋ผ์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ
    • pop๋œ ๊ฐ’์ด ๋จผ์ € ๋ฐ˜ํ™˜๋˜๊ณ , ๊ทธ ๋‹ค์Œ defer ๋ธ”๋ก์ด ์‹คํ–‰๋œ๋‹ค.
    • ์ฆ‰, ๊ธฐ์กด head๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ ๋‹ค์Œ์— ๊ธฐ์กด head๊ฐ’์„ ์‚ญ์ œํ•˜๊ธฐ ์œ„ํ•ด defer๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒƒ์ž„.
  • pop์€ ๋งจ ์•ž ๋…ธ๋“œ๋ฅผ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ = head๋ฅผ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ
    • head๋ฅผ head์˜ ๋‹ค์Œ ๋…ธ๋“œ๋กœ ๋ฐ”๊ฟ”์ค€๋‹ค (์‚ญ์ œ)
    • ์‚ญ์ œ ํ›„์— ๋งŒ์•ฝ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋น„์–ด์žˆ๋‹ค๋ฉด (head๊ฐ€ nil์ด๋ผ๋ฉด) ๋นˆ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋œ ๊ฒƒ์ด๋ฏ€๋กœ, tail์—๋„ nil์„ ํ• ๋‹นํ•ด ์ค€๋‹ค

removeLast() : ๋ฆฌ์ŠคํŠธ์˜ ๋งจ ๋’ค์˜ ๋…ธ๋“œ ์‚ญ์ œ. O(n)

//removeLast() : O(n). ๋งจ ๋’ค์—์„œ ์ œ๊ฑฐ.
@discardableResult
mutating func removeLast() -> T? {

    // if head is nil, means no node exists.
    guard let head = head else { return nil } // <- isEmpty๋กœ ๋Œ€์ฒด ๊ฐ€๋Šฅ

    // if head is not have any reference of next node, it means we have only one node in list.
    guard head.next != nil else { return pop() }

    // first we have to traverse to reach at last two nodes.
    var currentNode = head
    var previousNode = head

    while let next = currentNode.next {
        previousNode = currentNode
        currentNode = next
    }
    // ์—ฌ๊ธฐ์— ๋„๋‹ฌํ•˜๋ฉด currentNode๊ฐ€ ๋งˆ์ง€๋ง‰ ๋…ธ๋“œ๊ฐ€ ๋œ๋‹ค. ๋งˆ์ง€๋ง‰ ๋…ธ๋“œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์—†์•  ์‚ญ์ œํ•œ๋‹ค.
    // disconnected the next of last previous node
    previousNode.next = nil
    tail = previousNode
    return currentNode.value
}
  • head๊ฐ€ nil์ด๋ฉด ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋น„์–ด์žˆ๋‹ค๋Š” ๋œป์ด๋ฏ€๋กœ, ์‚ญ์ œํ•  ๊ฒŒ ์—†์œผ๋‹ˆ nil๋ฐ˜ํ™˜
  • head๊ฐ€ ์žˆ์ง€๋งŒ head.next๊ฐ€ ์—†๋‹ค๋ฉด ์š”์†Œ๊ฐ€ 1๊ฐœ๋ฟ์ด๋ฏ€๋กœ pop์œผ๋กœ ์ฒ˜๋ฆฌ
  • currentNode์™€ previousNode๋ฅผ ์ €์žฅํ•˜๋ฉฐ ๋ฆฌ์ŠคํŠธ์˜ ๋งจ ๋์œผ๋กœ ์ด๋™
  • ๋งจ ๋ ๋…ธ๋“œ์˜ previousNode.next๋ฅผ nil๋กœ ๋งŒ๋“ค๊ณ , previousNode๋ฅผ tail๋กœ ์ง€์ •
  • ์‚ญ์ œํ•œ ๋งจ ๋ ๋…ธ๋“œ์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜

remove(after:) : ๋ฆฌ์ŠคํŠธ์˜ ํŠน์ • ์ธ๋ฑ์Šค์˜ ๋…ธ๋“œ ์‚ญ์ œ. O(n)

O(n) ์˜ n์€ ์‚ญ์ œ ์ธ๋ฑ์Šค์— ํ•ด๋‹นํ•œ๋‹ค.

์‚ญ์ œ ๋…ธ๋“œ์˜ ์•ž ๋…ธ๋“œ์™€ ๋’ท ๋…ธ๋“œ๋ฅผ ์ด์–ด๋ถ™์—ฌ์ค€๋‹ค.

ํŠน์ • ์œ„์น˜๋ฅผ ์ฐพ์•„์„œ(ํƒ์ƒ‰) ๋…ธ๋“œ๋ฅผ ์‚ญ์ œํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์‹œ๊ฐ„์ด ๋” ๊ฑธ๋ฆฐ๋‹ค.

// remove(after:) : O(n). ํŠน์ • ์ธ๋ฑ์Šค ๋’ค์˜ ๋…ธ๋“œ๋ฅผ ์‚ญ์ œ
mutating func remove(after index: Int) -> T? {

    if index < 0 || index >= self.count() {
        return nil
    }

    if let beforeNode = self.node(at: index) {
        defer {
            if beforeNode.next == tail {
                tail = beforeNode
            }
            beforeNode.next = beforeNode.next?.next
        }
        return beforeNode.next?.value
    }
    return nil
}
  • insert(after:)์™€ ๋น„์Šทํ•˜๊ฒŒ, ์ธ๋ฑ์Šค๊ฐ€ ์œ ํšจํ•œ์ง€ ๋จผ์ € ๊ฒ€์‚ฌํ•œ๋‹ค
    • ์œ ํšจํ•˜์ง€ ์•Š์œผ๋ฉด nil ๋ฐ˜ํ™˜
  • ์ฃผ์–ด์ง„ ์ธ๋ฑ์Šค์— ์œ„์น˜ํ•œ ๋…ธ๋“œ๋ฅผ ์ฐพ๋Š”๋‹ค
    • ์—†์œผ๋ฉด nil๋ฐ˜ํ™˜
  • defer๋ธ”๋ก์€ ํ•จ์ˆ˜ ์ข…๋ฃŒ ์ง์ „์— ํ˜ธ์ถœ๋˜๋ฏ€๋กœ, return beforeNode.next?.value ๊ฐ€ ์‹คํ–‰๋˜๊ฒŒ ๋œ๋‹ค.
    • ๋…ธ๋“œ๊ฐ€ ์‚ญ์ œ๋˜๊ธฐ ์ „์— ๊ทธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ์œ„ํ•จ
  • ๋งŒ์•ฝ ์ฐพ์€ ๋…ธ๋“œ๊ฐ€ tail ์ง์ „์˜ ๋…ธ๋“œ๋ผ๋ฉด, ๋…ธ๋“œ ์‚ญ์ œ ์ „์— tail์„ ์—…๋ฐ์ดํŠธํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
    • tail์„ tail์ง์ „ ๋…ธ๋“œ๋กœ ๋ฐ”๊ฟ” ์ค€๋‹ค
  • ์ฐพ์€ ๋…ธ๋“œ์˜ next๋Š”, ์ฐพ์€ ๋…ธ๋“œ์˜ ๋‹ค๋‹ค์Œ ๋…ธ๋“œ์™€ ์—ฐ๊ฒฐํ•ด ์ค€๋‹ค
    • ์ฐธ์กฐ๊ฐ€ ์‚ฌ๋ผ์ง„ ๋…ธ๋“œ๋Š” ์‚ฌ๋ผ์ง€๊ฒŒ ๋œ๋‹ค.

removeAll() : ๋ฆฌ์ŠคํŠธ์˜ ๋ชจ๋“  ๋…ธ๋“œ ์‚ญ์ œ. O(1)

// to remove all the nodes from the list
mutating func removeAll() {
    head = nil
    tail = nil
}
  • ์ฐธ์กฐ๋ฅผ ์—†์•  ๋…ธ๋“œ๋“ค์ด ์‚ญ์ œ๋˜๋„๋ก head์™€ tail์„ ๋ชจ๋‘ nil๋กœ ๋งŒ๋“ ๋‹ค.

๊ตฌํ˜„ - ๋™์ž‘ ์˜ˆ์‹œ

var integerLinkedList = LinkedList<Int>()
integerLinkedList.push(1) // ์•ž์— ์ถ”๊ฐ€ : 1
integerLinkedList.append(2) // ๋’ค์— ์ถ”๊ฐ€ : 1 -> 2
integerLinkedList.push(0) // 0 -> 1 -> 2
integerLinkedList.insert(3, after: 2) // ์ธ๋ฑ์Šค2 ๋’ค์— ์ถ”๊ฐ€ : 0 -> 1 -> 2 -> 3

integerLinkedList.pop() // ์•ž์—์„œ ์‚ญ์ œ : 1 -> 2 -> 3
integerLinkedList.removeLast() // ๋’ค์—์„œ ์‚ญ์ œ : 1 -> 2
integerLinkedList.remove(after: 0) // ์ธ๋ฑ์Šค0 ๋’ค ์š”์†Œ ์‚ญ์ œ : 1
integerLinkedList.removeAll() // ๋ชจ๋“  ์š”์†Œ ์‚ญ์ œ : Empty list

LinkedList ๊ตฌํ˜„ ์ „์ฒด ์ฝ”๋“œ

์ฃผ์„์ด ์—†๋Š” ๋ฒ„์ „์€ ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค.

// ์š”์†Œ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š” ํด๋ž˜์Šค
class Node<T>: CustomStringConvertible, Equatable {

    var value: T
    var next: Node?

    // required conforms to CustomStringConvertible
    var description: String {
        guard let next = next else { return "\(value)" }
        return "\(value) -> " + String(describing: next)
    }

    init(value: T, next: Node? = nil) {
        self.value = value
        self.next = next
    }

    // required conforms to Equatable
    static func ==(lhs: Node, rhs: Node) -> Bool {
        return lhs.next == rhs.next
    }
}

// A struct to hold the information of all the nodes
struct LinkedList<T>: CustomStringConvertible {

    // to hold the starting point of list
    var head: Node<T>?

    // to hold the ending point of list
    var tail: Node<T>?

    init() { }

    // if head is nil simply list is empty right now
    var isEmpty: Bool {
        return head == nil
    }

    // required conforms to CustomStringConvertible
    var description: String {
        guard let head = head else { return "Empty list" }
        return String(describing: head)
    }
}

// Common Methods
extension LinkedList {
    // node(at:) : O(i), i=index. ํŠน์ • ์ธ๋ฑ์Šค์˜ ๋…ธ๋“œ๋ฅผ ์ฐพ๊ธฐ
    func node(at index: Int) -> Node<T>? {
        // if list is empty then simply return nil
        if isEmpty {
            return nil
        }

        var currentNode = head
        var currentIndex = 0
        while currentNode != nil && currentIndex < index {
            currentNode = currentNode?.next
            currentIndex += 1
        }
        return currentNode
    }

    // get number of nodes in the list
    func count() -> Int {
        var currentNode = head
        var count = 0
        while currentNode != nil {
            currentNode = currentNode?.next
            count += 1
        }
        return count
    }

    // push(_:) : O(1), ๋งจ ์•ž์— ์‚ฝ์ž… head-first insertion
    mutating func push(_ value: T) {

        // create a new node and passing head (starting point) as a next node
        let newNode = Node(value: value, next: head)

        // change the head to the new node because head will hold the starting node
        head = newNode

        // if tail (ending node) is nil, it means head and tail pointing the same node as it is first node
        if tail == nil {
            tail = head
        }
    }

    // append(_:) : O(1), ๋งจ ๋’ค์— ์‚ฝ์ž… tail-end insertion
    mutating func append(_ value: T) {

        // if list is empty then simply perform push operation
        if isEmpty {
            self.push(value)
            return
        }

        // create new node and assign it to tail's next because tail will always point the last node
        tail?.next = Node(value: value)

        // replace the tail with tail's next because before doing it tail's next pointing the last node
        tail = tail?.next
    }

    // insert(_:after:) : O(n), ์ฃผ์–ด์ง„ ์ธ๋ฑ์Šค ๋’ค์— ์‚ฝ์ž…
    mutating func insert(_ value: T, after index: Int) {

        // if given index is not valid as per current list, it will be added in last
        if index < 0 || index >= self.count() {
            self.append(value)
            return
        }

        // if node not found just before to given insert index, perform push operation
        if let beforeNode = self.node(at: index), let tail = tail {

            // if insert index ie equal to last, simply append new node at last
            if beforeNode == tail {
                self.append(value)
            } else {
                // create new node and assign as a next node for the before node
                beforeNode.next = Node(value: value, next: beforeNode.next)
            }
        } else {
            self.push(value)
        }
    }

    // pop() : O(1). ๋งจ ์•ž์—์„œ ์ œ๊ฑฐ.

    @discardableResult
    mutating func pop() -> T? {

        defer { // ํ•จ์ˆ˜ ์ข…๋ฃŒ ์ง์ „์— ์‹คํ–‰
            head = head?.next
            if isEmpty {
                tail = nil
            }
        }
        return head?.value
    }

    //removeLast() : O(n). ๋งจ ๋’ค์—์„œ ์ œ๊ฑฐ.
    @discardableResult
    mutating func removeLast() -> T? {

        // if head is nil, means no node exists.
        guard let head = head else { return nil } // <- isEmpty๋กœ ๋Œ€์ฒด ๊ฐ€๋Šฅ

        // if head is not have any reference of next node, it means we have only one node in list.
        guard head.next != nil else { return pop() }

        // first we have to traverse to reach at last two nodes.
        var currentNode = head
        var previousNode = head

        while let next = currentNode.next {
            previousNode = currentNode
            currentNode = next
        }
        // ์—ฌ๊ธฐ์— ๋„๋‹ฌํ•˜๋ฉด currentNode๊ฐ€ ๋งˆ์ง€๋ง‰ ๋…ธ๋“œ๊ฐ€ ๋œ๋‹ค. ๋งˆ์ง€๋ง‰ ๋…ธ๋“œ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์—†์•  ์‚ญ์ œํ•œ๋‹ค.
        // disconnected the next of last previous node
        previousNode.next = nil
        tail = previousNode
        return currentNode.value
    }

    // remove(after:) : O(n). ํŠน์ • ์ธ๋ฑ์Šค ๋’ค์˜ ๋…ธ๋“œ๋ฅผ ์‚ญ์ œ
    mutating func remove(after index: Int) -> T? {

        if index < 0 || index >= self.count() {
            return nil
        }

        if let beforeNode = self.node(at: index) {
            defer {
                if beforeNode.next == tail {
                    tail = beforeNode
                }
                beforeNode.next = beforeNode.next?.next
            }
            return beforeNode.next?.value
        }
        return nil
    }

    // to remove all the nodes from the list
    mutating func removeAll() {
        head = nil
        tail = nil
    }
}