λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
πŸ’» Programming 개발/🍎 iOS 개발, Swift

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 - 챕터6-2. μ—°κ²°λ¦¬μŠ€νŠΈ

by kimdee 2022. 5. 30.
λ°˜μ‘ν˜•

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 - 챕터6-2. μ—°κ²°λ¦¬μŠ€νŠΈ  (μŠ€μœ„ν”„νŠΈ μ½œλ ‰μ…˜ ν”„λ‘œν† μ½œ, λ°Έλ₯˜ μ‹œλ§¨ν‹±, COW(μΉ΄ν”Ό-온-라이트))

Raywenderlich.com μ—μ„œ λ‚˜μ˜¨ Data Structures & Algorithms in Swift μ±…μ˜ 데λͺ¨ κ³΅κ°œλ³Έμ„ λ²ˆμ—­ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 즐겁게 λ΄μ£Όμ„Έμš”. 

https://www.raywenderlich.com/books/data-structures-algorithms-in-swift

 


 

μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 Elementary Data Structure

챕터 6. μ—°κ²° 리슀트 Linked Lists 

μŠ€μœ„ν”„νŠΈ μ½œλ ‰μ…˜ ν”„λ‘œν† μ½œ Swift collection protocols

μŠ€μœ„ν”„νŠΈ ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬μ— μžˆλŠ” ν”„λ‘œν† μ½œλ“€μ€ νŠΉμ • νƒ€μž…μ— λŒ€ν•΄ μ˜ˆμƒλ˜λŠ” 것을 μ •μ˜ν•˜λ„λ‘ λ„μ™€μ€λ‹ˆλ‹€. 이 ν”„λ‘œν† μ½œλ“€μ€ νŠΉμ„± 및 μ„±λŠ₯에 λŒ€ν•΄ 보μž₯을 μ œκ³΅ν•©λ‹ˆλ‹€. 이 ν”„λ‘œν† μ½œ μ„ΈνŠΈμ—μ„œ μ½œλ ‰μ…˜κ³Ό κ΄€κ³„λœ 4가지 ν”„λ‘œν† μ½œμ— μ΄ˆμ μ„ 맞좜 κ²ƒμž…λ‹ˆλ‹€.

각 ν”„λ‘œν† μ½œμ΄ ν•˜λŠ” 일에 λŒ€ν•΄ κ°„λž΅νžˆ μš”μ•½ν•˜μžλ©΄

  • 1 계측(Tier 1), μ‹œν€€μŠ€ (Sequence) : μ‹œν€€μŠ€ νƒ€μž…μ€ μš”μ†Œμ— 순차적 접근을 μ œκ³΅ν•©λ‹ˆλ‹€. μ£Όμ˜ν•΄μ•Ό 될 μ€‘μš”ν•œ 뢀뢄이 μžˆμŠ΅λ‹ˆλ‹€. 순차적 접근은 μš”μ†Œλ“€μ„ 파괴적으둜 μ†Œλͺ¨ν•˜λ―€λ‘œ λ‹€μ‹œ λ°©λ¬Έν•  수 μ—†μŠ΅λ‹ˆλ‹€.
  • 2 계측(Tier 2), μ½œλ ‰μ…˜(Collection) : μ½œλ ‰μ…˜ νƒ€μž…μ€ 좔가적인 보μž₯이 μ œκ³΅λ˜λŠ” μ‹œν€€μŠ€ νƒ€μž…μž…λ‹ˆλ‹€. μ½œλ ‰μ…˜ νƒ€μž„μ€ μœ ν•œν•˜λ©°, 반볡적인 λΉ„νŒŒκ΄΄μ  순차 접근을 ν—ˆμš©ν•©λ‹ˆλ‹€.
  • 3 계측(Tier 3), μ–‘λ°©ν–₯ μ½œλ ‰μ…˜(BidirectionalCollection) : μ½œλ ‰μ…˜ νƒ€μž…μ€, μ΄λ¦„μ—μ„œ μ•Œ 수 μžˆλ“―μ΄ μ‹œν€€μŠ€λ₯Ό μœ„μ•„λž˜ μ–‘λ°©ν–₯으둜 이동할 수 μžˆλŠ” μ–‘λ°©ν–₯ μ½œλ ‰μ…˜ νƒ€μž…μ΄ 될 수 μžˆμŠ΅λ‹ˆλ‹€. μ—°κ²°λ¦¬μŠ€νŠΈλŠ” 였직 headμ—μ„œ tail 둜만 이동할 수 μžˆμœΌλ―€λ‘œ μ—°κ²°λ¦¬μŠ€νŠΈμ—μ„œλŠ” λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.
  • 4 계측(Tier 4), 랜덀 μ•‘μ„ΈμŠ€ μ½œλ ‰μ…˜(RandomAccessCollection) : μ–‘λ°©ν–₯ μ½œλ ‰μ…˜μ€ νŠΉμ • μΈλ±μŠ€μ— μœ„μΉ˜ν•œ μš”μ†Œμ— μ ‘κ·Όν•˜λŠ” 것이 λ‹€λ₯Έ μΈλ±μŠ€μ— μœ„μΉ˜ν•œ μš”μ†Œμ— μ ‘κ·Όν•˜λŠ” κ²ƒλ§ŒνΌ κ±Έλ¦°λ‹€λŠ” 것이 보μž₯될 경우 랜덀 μ•‘μ„ΈμŠ€ μ½œλ ‰μ…˜ νƒ€μž…μ΄ 될 수 μžˆμŠ΅λ‹ˆλ‹€. 리슀트 μ•žμͺ½μ— κ°€κΉŒμš΄ λ…Έλ“œμ— μ ‘κ·Όν•˜λŠ” 것이 리슀트 λ’·μͺ½μ— μžˆλŠ” μš”μ†Œμ— μ ‘κ·Όν•˜λŠ” 것보닀 훨씬 λΉ λ₯΄κΈ° λ•Œλ¬Έμ— μ—°κ²°λ¦¬μŠ€νŠΈμ—μ„œλŠ” λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.

각각에 λŒ€ν•΄μ„œλŠ” 더 λ§Žμ€ λ‚΄μš©μ΄ μžˆμŠ΅λ‹ˆλ‹€. μ—¬λŸ¬λΆ„μ€ 각각의 적합성에 λŒ€ν•΄ μž‘μ„±ν•˜λ©΄μ„œ 이λ₯Ό λ°°μ›Œκ°€κ²Œ 될 κ²ƒμž…λ‹ˆλ‹€.

μ—°κ²°λ¦¬μŠ€νŠΈλŠ” μŠ€μœ„ν”„νŠΈ μ½œλ ‰μ…˜ ν”„λ‘œν† μ½œμ—μ„œ 두 가지 μžκ²©μ„ 얻을 수 μžˆμŠ΅λ‹ˆλ‹€. 첫번째둜, μ—°κ²°λ¦¬μŠ€νŠΈλŠ” λ…Έλ“œμ˜ μ²΄μΈμ΄λ―€λ‘œ Sequence ν”„λ‘œν† μ½œμ„ μ±„νƒν•˜λŠ” 것이 합리적이며 λ‘˜μ§Έλ‘œ λ…Έλ“œμ˜ 체인은 μœ ν•œν•œ μ‹œν€€μŠ€μ΄λ―€λ‘œ Collection ν”„λ‘œν† μ½œμ„ μ±„νƒν•˜λŠ” 것이 ν•©λ¦¬μ μž…λ‹ˆλ‹€.

 

 

μŠ€μœ„ν”„νŠΈ μ½œλ ‰μ…˜ κ΅¬ν˜„ν•˜κΈ°

이번 μ„Ήμ…˜μ—μ„œ μ—¬λŸ¬λΆ„μ€ Collection ν”„λ‘œν† μ½œμ„ κ΅¬ν˜„ν•˜λŠ” 것을 μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€. μ½œλ ‰μ…˜ νƒ€μž…μ€ μœ ν•œν•œ μ‹œν€€μŠ€λ‘œ λΉ„νŒŒκ΄΄μ μΈ 순차 접근을 μ œκ³΅ν•©λ‹ˆλ‹€. μŠ€μœ„ν”„νŠΈμ˜ Collection 은 인덱슀λ₯Ό μ»¬λ ‰μ…˜μ˜ 값에 맀핑할 수 μžˆλŠ” 멋진 μš©μ–΄, 첨자(subscript)λ₯Ό 톡해 μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μŠ€μœ„ν”„νŠΈ λ°°μ—΄μ—μ„œ 첨자λ₯Ό μ‚¬μš©ν•œ μ˜ˆμ œμž…λ‹ˆλ‹€.

 

array[5]

λ°°μ—΄μ˜ μΈλ±μŠ€λŠ” Int κ°’μž…λ‹ˆλ‹€. μœ„ μ˜ˆμ œμ—μ„œλŠ” 5κ°€ κ·Έ κ°’μž…λ‹ˆλ‹€. 첨자 연산은 [ λŒ€κ΄„ν˜Έ ]둜 μ •μ˜λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. 인덱슀둜 첨자λ₯Ό μ΄μš©ν•˜λ©΄ μ½œλ ‰μ…˜μ˜ 값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

 

μ»€μŠ€ν…€ μ½œλ ‰μ…˜ 인덱슀

Collection ν”„λ‘œν† μ½œ λ©”μ„œλ“œμ˜ μ„±λŠ₯을 μΈ‘μ •ν•˜λŠ” ν•­λͺ©μ€ Index 에 값을 λ§€ν•‘ν•˜λŠ” μ†λ„μž…λ‹ˆλ‹€. Array 와 같은 λ‹€λ₯Έ μŠ€ν† λ¦¬μ§€ μ˜΅μ…˜κ³Ό λ‹€λ₯΄κ²Œ μ—°κ²°λ¦¬μŠ€νŠΈλŠ” μ •μˆ˜ 인덱슀λ₯Ό μ‚¬μš©ν•˜μ—¬ O(1) 첨자 연산을 μˆ˜ν–‰ν•  수 μ—†μŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ―€λ‘œ μ—¬λŸ¬λΆ„μ˜ λͺ©ν‘œλŠ” ν•΄λ‹Ή λ…Έλ“œμ— λŒ€ν•œ 레퍼런슀λ₯Ό ν¬ν•¨ν•˜λŠ” μ»€μŠ€ν…€ 인덱슀λ₯Ό μ •μ˜ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

 

LinkedList.swift 에 μ•„λž˜ extension 을 μΆ”κ°€ν•©λ‹ˆλ‹€.

 

extension LinkedList: Collection {

  public struct Index: Comparable {

    public var node: Node<Value>?

    static public func ==(lhs: Index, rhs: Index) -> Bool {
      switch (lhs.node, rhs.node) {
      case let (left?, right?):
        return left.next === right.next
      case (nil, nil):
        return true
      default:
        return false
      }
    }

    static public func <(lhs: Index, rhs: Index) -> Bool {
      guard lhs != rhs else {
        return false
      }
      let nodes = sequence(first: lhs.node) { $0?.next }
      return nodes.contains { $0 === rhs.node }
    }
  }
}

Collection μš”κ΅¬μ‚¬ν•­μ„ λ§Œμ‘±ν•˜λŠ” μ»€μŠ€ν…€μ„ μ‚¬μš©ν•  κ²ƒμž…λ‹ˆλ‹€. μ•„λž˜ λ‚΄μš©μ„ extension μ•ˆμ— λ„£μ–΄ μ™„μ„±ν•˜μ„Έμš”.

// 1
public var startIndex: Index {
  Index(node: head)
}
// 2
public var endIndex: Index {
  Index(node: tail?.next)
}
// 3
public func index(after i: Index) -> Index {
  Index(node: i.node?.next)
}
// 4
public subscript(position: Index) -> Value {
  position.node!.value
}
  1. startIndex λŠ” μ—°κ²°λ¦¬μŠ€νŠΈ head 에 μ˜ν•΄ ν•©λ¦¬μ μœΌλ‘œ μ •μ˜λ©λ‹ˆλ‹€.
  2. Collection μ€  endIndex λ₯Ό λ§ˆμ§€λ§‰μœΌλ‘œ μ ‘κ·Όκ°€λŠ₯ν•œ κ°’ λ‹€μŒ 인덱슀둜 μ •μ˜ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ―€λ‘œ ail?.next λ₯Ό μ§€μ •ν•©λ‹ˆλ‹€.
  3. index(after:) λŠ” 인덱슀λ₯Ό μ¦κ°€μ‹œν‚€λŠ” 방법을 λ‚˜νƒ€λƒ…λ‹ˆλ‹€. λ‹¨μˆœνžˆ λ‹€μŒλ…Έλ“œμ˜ 인덱슀λ₯Ό μ§€μ •ν•˜λ©΄ λ©λ‹ˆλ‹€.
  4. subscript μ²¨μžλŠ” μ½œλ ‰μ…˜μ˜ 값을 Index 와 λ§€ν•‘μ‹œν‚€κΈ° μœ„ν•΄ μ‚¬μš©λ©λ‹ˆλ‹€. μ»€μŠ€ν…€ 인덱슀λ₯Ό μƒμ„±ν–ˆμœΌλ―€λ‘œ λ…Έλ“œμ˜ 값을 μ°Έμ‘°ν•˜μ—¬ μƒμˆ˜μ‹œκ°„(constant time) μ•ˆμ— μ‰½κ²Œ 달성할 수 μžˆμŠ΅λ‹ˆλ‹€.

이제 Collection 을 μ±„νƒν•˜λŠ” 절차λ₯Ό λ§ˆμΉ©λ‹ˆλ‹€. ν”Œλ ˆμ΄κ·ΈλΌμš΄λ“œ νŽ˜μ΄μ§€λ‘œ λŒμ•„κ°€μ„œ μ‹œμš΄μ „μ„ ν•΄λ΄…μ‹œλ‹€.

 

example(of: "using collection") {
  var list = LinkedList<Int>()
  for i in 0...9 {
    list.append(i)
  }

  print("List: \(list)")
  print("First element: \(list[list.startIndex])")
  print("Array containing first 3 elements: \(Array(list.prefix(3)))")
  print("Array containing last 3 elements: \(Array(list.suffix(3)))")

  let sum = list.reduce(0, +)
  print("Sum of all values: \(sum)")
}

μ•„λž˜ 결과물을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

---Example of using collection---
List: 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9
First element: 0
Array containing first 3 elements: [0, 1, 2]
Array containing last 3 elements: [7, 8, 9]
Sum of all values: 45

 

λ°Έλ₯˜ μ‹œλ§¨ν‹±κ³Ό μΉ΄ν”Ό-온-라이트 Value semantics and copy-on-write

 

μŠ€μœ„ν”„νŠΈ μ½œλ ‰μ…˜μ˜ 또 λ‹€λ₯Έ μ€‘μš”ν•œ ν’ˆμ§ˆμ€ λ°Έλ₯˜ μ‹œλ§¨ν‹±μ„ κ°€μ§„λ‹€λŠ” μ μž…λ‹ˆλ‹€. μ΄λŠ” 카우(COW)라고 ν•˜λŠ” μΉ΄ν”Ό-온-라이트λ₯Ό μ‚¬μš©ν•˜μ—¬ 효율적으둜 κ΅¬ν˜„λ©λ‹ˆλ‹€. λ°Έλ₯˜ μ‹œλ§¨ν‹±μ˜ κ°œλ…μ„ μ•ŒκΈ° μœ„ν•΄, 배열을 μ‚¬μš©ν•œ λ™μž‘μ„ 탐색할 κ²ƒμž…λ‹ˆλ‹€.

 

μ•„λž˜ μ½”λ“œλ₯Ό ν”Œλ ˆμ΄κ·ΈλΌμš΄λ“œ νŽ˜μ΄μ§€ μ•„λž˜μͺ½μ— μž‘μ„±ν•©λ‹ˆλ‹€.

example(of: "array cow") {
  let array1 = [1, 2]
  var array2 = array1

  print("array1: \(array1)")
  print("array2: \(array2)")

  print("---After adding 3 to array 2---")
  array2.append(3)
  print("array1: \(array1)")
  print("array2: \(array2)")
}

μ•„λž˜ 결과물을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

---Example of array cow---
array1: [1, 2]
array2: [1, 2]
---After adding 3 to array 2---
array1: [1, 2]
array2: [1, 2, 3]

array2 κ°€ μˆ˜μ •λ  λ•Œ array1 의 μš”μ†ŒλŠ” λ³€ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μ‹€μ œλ‘œλŠ” array2 λŠ” append κ°€ 호좜될 λ•Œ κΈ°λ³Έ μ €μž₯μ†Œμ˜ 볡사본을 λ§Œλ“­λ‹ˆλ‹€.

이제 μ—°κ²°λ¦¬μŠ€νŠΈκ°€ λ°Έλ₯˜ μ‹œλ§¨ν‹±μ„ 가지고 μžˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€. μ•„λž˜ λ‚΄μš©μ„ ν”Œλ ˆμ΄κ·ΈλΌμš΄λ“œ νŽ˜μ΄μ§€ μ•„λž˜μ— μž‘μ„±ν•©λ‹ˆλ‹€.

example(of: "linked list cow") {
  var list1 = LinkedList<Int>()
  list1.append(1)
  list1.append(2)
  var list2 = list1
  print("List1: \(list1)")
  print("List2: \(list2)")

  print("After appending 3 to list2")
  list2.append(3)
  print("List1: \(list1)")
  print("List2: \(list2)")
}

μ•„λž˜ 결과물을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

---Example of linked list cow---
List1: 1 -> 2
List2: 1 -> 2
After appending 3 to list2
List1: 1 -> 2 -> 3
List2: 1 -> 2 -> 3

λΆˆν–‰νžˆλ„ μ—°κ²°λ¦¬μŠ€νŠΈμ—λŠ” λ°Έλ₯˜ μ‹œλ§¨ν‹±μ΄ μ—†μŠ΅λ‹ˆλ‹€. μ™œλƒν•˜λ©΄ κΈ°λ³Έ μ €μž₯μ†ŒλŠ” 레퍼런슀 νƒ€μž…(Node) 을 μ‚¬μš©ν•˜κ³  있기 λ•Œλ¬Έμž…λ‹ˆλ‹€. μ΄λŠ” μ‹¬κ°ν•œ λ¬Έμ œμž…λ‹ˆλ‹€. LinkedList λŠ” ꡬ쑰체이고 λ°˜λ“œμ‹œ λ°Έλ₯˜ μ‹œλ§¨ν‹±μ„ μ‚¬μš©ν•΄μ•Όλ˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. 카우(COW)λ₯Ό κ΅¬ν˜„ν•˜λ©΄ 이 문제λ₯Ό ν•΄κ²°ν•˜κ²Œ λ©λ‹ˆλ‹€.

 

카우(COW)λ₯Ό μ‚¬μš©ν•˜μ—¬ λ°Έλ₯˜ μ‹œλ§¨ν‹±μ„ λ‹¬μ„±ν•˜λŠ” μ „λž΅μ€ κ½€λ‚˜ λ‹¨μˆœν•©λ‹ˆλ‹€. μ—°κ²°λ¦¬μŠ€νŠΈμ˜ μ½˜ν…μΈ λ₯Ό λ³΅μ œν•˜κΈ° 전에 κΈ°λ³Έμ €μž₯μ†Œμ˜ 볡사본을 μˆ˜ν–‰ν•˜κ³  μƒˆλ‘œμš΄ 볡사본에 λͺ¨λ“  레퍼런슀(head 와 tail)λ₯Ό μ—…λ°μ΄νŠΈ ν•˜κ³ μž ν•©λ‹ˆλ‹€.

 

LinkedList.swift 에 μ•„λž˜ λ©”μ„œλ“œλ₯Ό LinkedList에 μΆ”κ°€ν•©λ‹ˆλ‹€.

 

private mutating func copyNodes() {
  guard var oldNode = head else {
    return
  }

  head = Node(value: oldNode.value)
  var newNode = head

  while let nextOldNode = oldNode.next {
    newNode!.next = Node(value: nextOldNode.value)
    newNode = newNode!.next

    oldNode = nextOldNode
  }

  tail = newNode
}

 

이 λ©”μ„œλ“œλŠ” μ—°κ²°λ¦¬μŠ€νŠΈμ˜ κΈ°μ‘΄ λ…Έλ“œλ₯Ό λ™μΌν•œ 값을 가진 μƒˆλ‘œ ν• λ‹Ήλœ κ²ƒμœΌλ‘œ κ΅μ²΄ν•©λ‹ˆλ‹€.

이제 mutating ν‚€μ›Œλ“œλ‘œ ν‘œμ‹œλœ LinkedList 의 λ‹€λ₯Έ λͺ¨λ“  λ©”μ„œλ“œλ₯Ό μ°Ύκ³  λͺ¨λ“  λ©”μ„œλ“œμ˜ μƒλ‹¨μ—μ„œ copyNodes λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.

총 6개의 λ©”μ„œλ“œκ°€ μžˆμŠ΅λ‹ˆλ‹€.

  • push
  • append
  • insert(after:)
  • pop
  • removeLast
  • remove(after:)

개쑰(retrofit)을 μ™„λ£Œν•œ ν›„ λ§ˆμ§€λ§‰ example ν•¨μˆ˜ ν˜ΈμΆœμ€ μ•„λž˜ 결과물을 보여주어야 ν•©λ‹ˆλ‹€.

---Example of linked list cow---
List1: 1 -> 2
List2: 1 -> 2
After appending 3 to list2
List1: 1 -> 2
List2: 1 -> 2 -> 3

λ°”λ‘œ μ—¬λŸ¬λΆ„μ΄ μ›ν•œ κ²ƒμž…λ‹ˆλ‹€. μ•„λ‹ˆλ©΄ λͺ¨λ“  mutating ν˜ΈμΆœμ— O(n) μ˜€λ²„ν—€λ“œλ₯Ό λ„μž…ν•˜κ±°λ‚˜μš”..

 

카우(COW) μ΅œμ ν™”

λͺ¨λ“  mutating ν˜ΈμΆœμ— O(n) μ˜€λ²„ν—€λ“œλ₯Ό λ„μž…ν•˜λŠ” 것은 λΆˆκ°€ν•©λ‹ˆλ‹€. 두가지 μ „λž΅μ΄ 이 문제λ₯Ό μ™„ν™”ν•˜λŠ” 데 도움이 λ©λ‹ˆλ‹€. μ²«μ§ΈλŠ” λ…Έλ“œκ°€ ν•œ λͺ…μ˜ μ†Œμœ μžλ§Œ μžˆμ„ 경우 볡사λ₯Ό λ°©μ§€ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

 

isKnownUniquelyReferenced

μŠ€μœ„ν”„νŠΈ ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬μ—λŠ” isKnownUniquelyReferenced λΌλŠ” μ΄λ¦„μ˜ ν•¨μˆ˜κ°€ μžˆμŠ΅λ‹ˆλ‹€. 이 ν•¨μˆ˜λŠ” μ˜€λΈŒμ νŠΈκ°€ μ •ν™•νžˆ ν•˜λ‚˜μ˜ 레퍼런슀λ₯Ό 가지고 μžˆλŠ”μ§€ μ—¬λΆ€λ₯Ό ν™•μΈν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€. λ§ν¬λ“œλ¦¬μŠ€νŠΈ 카우 μ˜ˆμ œμ—μ„œ ν…ŒμŠ€νŠΈν•˜μ„Έμš”.

 

λ§ˆμ§€λ§‰ example ν•¨μˆ˜ ν˜ΈμΆœμ—μ„œ  var list2 = list1 라고 μž‘μ„±ν•œ 뢀뢄에 μ•„λž˜ μ½”λ“œλ₯Ό μ—…λ°μ΄νŠΈν•˜μ„Έμš”.

 

print("List1 uniquely referenced: \(isKnownUniquelyReferenced(&list1.head))")
var list2 = list1
print("List1 uniquely referenced: \(isKnownUniquelyReferenced(&list1.head))")

 

μ½˜μ†”μ—μ„œ μ•„λž˜ 두 쀄이 보일 κ²ƒμž…λ‹ˆλ‹€.

 

List1 uniquely referenced: true
List1 uniquely referenced: false

 

 isKnownUniquelyReferenced을 μ‚¬μš©ν•˜μ—¬ κΈ°λ³Έ λ…Έλ“œ 객체가 κ³΅μœ λœμ§€ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. 이 λ™μž‘μ„ ν™•μΈν–ˆμœΌλ―€λ‘œ λ‘κ°œμ˜ print ꡬ문을 μ‚­μ œν•˜μ„Έμš”. 길은 λΆ„λͺ…ν•©λ‹ˆλ‹€. μ•„λž˜μ˜ 쑰건문을 copyNodes 상단에 μΆ”κ°€ν•˜μ„Έμš”.

 

guard !isKnownUniquelyReferenced(&head) else {
  return
}

카우(COW)κ°€ μ—¬μ „νžˆ μœ νš¨ν•œ 것을 ν™•μΈν•˜κ³  기뻐할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

---Example of linked list cow---
List1: 1 -> 2
List2: 1 -> 2
After appending 3 to list2
List1: 1 -> 2
List2: 1 -> 2 -> 3

μ΄λ ‡κ²Œ λ°”κΏ”μ„œ, μ—°κ²°λ¦¬μŠ€νŠΈμ˜ μ„±λŠ₯은 카우(COW)의 이점을 가진 채 μ΄μ „μ˜ μ„±λŠ₯을 νšŒλ³΅ν•˜κ²Œ λ©λ‹ˆλ‹€.

 

μ•½κ°„μ˜ κ³€λž€ A minor predicament

μ•„λž˜ μ½”λ“œλ₯Ό μ΄μ „μ˜ 예제 μ½”λ“œ μ•ˆμ— μΆ”κ°€ν•˜μ„Έμš”.

print("Removing middle node on list2")
if let node = list2.node(at: 0) {
  list2.remove(after: node)
}
print("List2: \(list2)")

μ½˜μ†”μ—μ„œ μ•„λž˜ 결과물이 λ³΄μ—¬μ•Όν•©λ‹ˆλ‹€.

 

---Example of linked list cow---
List1: 1 -> 2
List2: 1 -> 2
After appending 3 to list2
List1: 1 -> 2
List2: 1 -> 2 -> 3
Removing middle node on list2
List2: 1 -> 2 -> 3

 

제거 연산은 더 이상 μž‘λ™ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μš°λ¦¬κ°€ λ§Œλ“  카우(COW)μ΅œμ ν™”μ— κ·Έ μ΄μœ κ°€ μžˆμŠ΅λ‹ˆλ‹€. μ™œλƒν•˜λ©΄ λͺ¨λ“  λ³€ν˜•μ€ λ…Έλ“œμ˜ 볡사본을 μΌμœΌν‚¬ 수 있기 λ•Œλ¬Έμ—, remove(after:) κ΅¬ν˜„μ€ 잘λͺ»λœ λ…Έλ“œ μ„ΈνŠΈμ—μ„œ 제거λ₯Ό μˆ˜ν–‰ν•©λ‹ˆλ‹€. 이λ₯Ό μˆ˜μ •ν•˜κΈ° μœ„ν•΄μ„œλŠ” copyNodes λ©”μ„œλ“œμ˜ νŠΉλ³„ν•œ 버전을 μž‘μ„±ν•΄μ•Ό ν•©λ‹ˆλ‹€. Sources 디렉토리에 μž‘μ„±ν•œ LinkedList.swift 둜 λŒμ•„κ°€μ„œ μ•„λž˜ λ‚΄μš©μ„ copyNodes λ©”μ„œλ“œ μ•„λž˜μ— μž‘μ„±ν•©λ‹ˆλ‹€.

 

private mutating func copyNodes(returningCopyOf node: Node<Value>?) -> Node<Value>? {
  guard !isKnownUniquelyReferenced(&head) else {
    return nil
  }
  guard var oldNode = head else {
    return nil
  }

  head = Node(value: oldNode.value)
  var newNode = head
  var nodeCopy: Node<Value>?

  while let nextOldNode = oldNode.next {
    if oldNode === node {
      nodeCopy = newNode
    }
    newNode!.next = Node(value: nextOldNode.value)
    newNode = newNode!.next
    oldNode = nextOldNode
  }

  return nodeCopy
}

 

이 λ©”μ„œλ“œλŠ” 이전에 κ΅¬ν˜„ν•œ 것과 λ§Žμ€ μœ μ‚¬μ„±μ„ κ³΅μœ ν•©λ‹ˆλ‹€. μ£Όμš” 차이점은 μ „λ‹¬λœ νŒŒλΌλ―Έν„°λ₯Ό 기반으둜 μƒˆλ‘­κ²Œ λ³΅μ‚¬λœ λ…Έλ“œλ₯Ό λ°˜ν™˜ν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. remove(after:) λ©”μ„œλ“œλ₯Ό μ•„λž˜μ™€ 같이 μ—…λ°μ΄νŠΈν•©λ‹ˆλ‹€.

 

@discardableResult
public mutating func remove(after node: Node<Value>) -> Value? {
  guard let node = copyNodes(returningCopyOf: node) else { return nil }
  defer {
    if node.next === tail {
      tail = node
    }
    node.next = node.next?.next
  }
  return node.next?.value
}

 

이제 쒀전에 λ§Œλ“  λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μƒˆλ‘œ λ³΅μ‚¬ν•œ λ…Έλ“œμ—μ„œ μ œκ±°μ—°μ‚°μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€.

 

λ…Έλ“œ 곡유

λ‘λ²ˆμ§Έ μ΅œμ ν™”λŠ” λ…Έλ“œμ˜ λΆ€λΆ„ κ³΅μœ μž…λ‹ˆλ‹€. 결과적으둜 볡사λ₯Ό ν”Όν•  수 μžˆλŠ” νŠΉμ • μ‹œλ‚˜λ¦¬μ˜€κ°€ μžˆμŠ΅λ‹ˆλ‹€. λͺ¨λ“  μ‹œλ‚˜λ¦¬μ— λŒ€ν•œ 포괄적인 ν‰κ°€λŠ” μ±…μ˜ λ²”μœ„λ₯Ό λ²—μ–΄λ‚˜μ§€λ§Œ κ΄€λ ¨λœ λ‚΄μš©μ— λŒ€ν•œ 아이디어λ₯Ό μ—¬λŸ¬λΆ„μ—κ²Œ μ œκ³΅ν•  κ²ƒμž…λ‹ˆλ‹€.

μ•„λž˜ 예제λ₯Ό μ‚΄νŽ΄λ³΄μ„Έμš”. (λ”°λΌμ„œ μž‘μ„±ν•  ν•„μš”λŠ” μ—†μŠ΅λ‹ˆλ‹€.)

 

var list1 = LinkedList<Int>()
(1...3).forEach { list1.append($0) }
var list2 = list1

 

카우(COW)κ°€ λΉ„ν™œμ„±ν™”λœ μƒνƒœμ—μ„œ list2 μ—μ„œ ν‘Έμ‹œ 연산을 μˆ˜ν–‰ν•œ κ²°κ³Όλ₯Ό κ³ λ €ν•΄λ³΄μ„Έμš”.

 

list2.push(0)

 

 

list1 μ΄ list2 μ—μ„œ μˆ˜ν–‰λœ push μ—°μ‚°μ— μ˜ν–₯을 λ°›μ„κΉŒμš”? μ΄ κ²½μš°μ—λŠ” μ•„λ‹™λ‹ˆλ‹€. 

두 λ¦¬μŠ€νŠΈλ₯Ό μΆœλ ₯ν•˜λ©΄ μ•„λž˜ κ²°κ³Όλ¬Όμ„ λ³΄κ²Œ λ  κ²ƒμž…λ‹ˆλ‹€.

 

List1: 1 -> 2 -> 3
List2: 0 -> 1 -> 2 -> 3

 

list1에  100을 ν‘Έμ‹œν•˜λŠ” μ΄ κ²½μš°λ„ μ•ˆμ „ν•©λ‹ˆλ‹€.

 

list1.push(100)

 

 

 

μ§€κΈˆ 두 리슀트λ₯Ό μΈμ‡„ν•˜λ©΄ μ•„λž˜ κ²°κ³Όλ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

 

List1: 100 -> 1 -> 2 -> 3
List2: 0 -> 1 -> 2 -> 3

 

μ—°κ²°λ¦¬μŠ€νŠΈμ˜ 일방ν–₯적인 νŠΉμ„±μ€ head-first μ‚½μž…μ΄ “카우(COW)의 λΆ€λ‹΄(tax)“을 λ¬΄μ‹œν•  수 μžˆμŒμ„ μ˜λ―Έν•©λ‹ˆλ‹€.

 

ν•΅μ‹¬μš”μ•½ Key points

  • μ—°κ²°λ¦¬μŠ€νŠΈλŠ” μ„ ν˜•μ μ΄κ³  일방ν–₯μ μž…λ‹ˆλ‹€. 레퍼런슀λ₯Ό μ΄λ™ν•˜λŠ” μ¦‰μ‹œ λ’€λ‘œ λŒμ•„κ°ˆ 수 μ—†μŠ΅λ‹ˆλ‹€.
  • μ—°κ²°λ¦¬μŠ€νŠΈλŠ” head-first μ‚½μž…μ˜ 경우 O(1) μ‹œκ°„λ³΅μž‘λ„λ₯Ό κ°€μ§‘λ‹ˆλ‹€. λ°°μ—΄μ˜ 경우 O(n) μ‹œκ°„λ³΅μž‘λ„λ₯Ό κ°€μ§‘λ‹ˆλ‹€.
  • Sequence 와 Collection κ³Ό 같은 μŠ€μœ„ν”„νŠΈ μ½œλ ‰μ…˜ ν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λ©΄ μžλ™μ μœΌλ‘œ λ§Žμ€ μœ μš©ν•œ λ©”μ„œλ“œμ— μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • μΉ΄ν”Ό-온-λΌμ΄νŠΈλŠ” 쒋은 μ„±λŠ₯을 μœ μ§€ν•˜λ©΄μ„œ λ°Έλ₯˜ μ‹œλ§¨ν‹±μ„ λ‹¬μ„±ν•˜λ„λ‘ ν•©λ‹ˆλ‹€.

 


πŸ”— λ²ˆμ—­ μ‹œλ¦¬μ¦ˆ 링크

0️⃣ μ„Ήμ…˜ 0. μ‹œμž‘ν•˜κΈ°μ „μ—

 

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 0. μ‹œμž‘ν•˜κΈ° 전에

μš”μ¦˜ μ•Œκ³ λ¦¬μ¦˜ 곡뢀λ₯Ό ν•˜λ©΄μ„œ Raywenderlich.com μ—μ„œ λ‚˜μ˜¨ Data Structures & Algorithms in Swift이 책을 보고 μžˆμŠ΅λ‹ˆλ‹€. https://www.raywenderlich.com/books/data-structures-algorithms-in-swift μ €λŠ” μ •..

kimdee.tistory.com

1️⃣ μ„Ήμ…˜ 1. μ†Œκ°œ Introduction

> 챕터 1.μ™œ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜μ„ λ°°μ›Œμ•Όν• κΉŒμš”? / μ±•ν„° 2. λ³΅μž‘λ„  Complexity

 

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 1. μ†Œκ°œ - 챕터1. μ™œ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜μ„

Raywenderlich.com μ—μ„œ λ‚˜μ˜¨ Data Structures & Algorithms in Swift μ±…μ˜ 데λͺ¨ κ³΅κ°œλ³Έμ„ λ²ˆμ—­ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 즐겁게 λ΄μ£Όμ„Έμš”. https://www.raywenderlich.com/books/data-structures-algorithms-in-swift μ„Ήμ…˜..

kimdee.tistory.com

> 챕터3. μŠ€μœ„ν”„νŠΈ ν‘œμ€€ 라이브러리 Swift Standard Library

 

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 1. μ†Œκ°œ - 챕터3. μŠ€μœ„ν”„νŠΈ ν‘œμ€€ 라이브러리 S

Raywenderlich.com μ—μ„œ λ‚˜μ˜¨ Data Structures & Algorithms in Swift μ±…μ˜ 데λͺ¨ κ³΅κ°œλ³Έμ„ λ²ˆμ—­ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 즐겁게 λ΄μ£Όμ„Έμš”. https://www.raywenderlich.com/books/data-structures-algorithms-in-swift μ„Ήμ…˜..

kimdee.tistory.com

 

1️⃣ μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 Elementary Data Structure

> 챕터 4. μŠ€νƒ Stacks /  μ±•ν„° 5. μŠ€νƒ λ„μ „κ³Όμ œ Stack Challenges

 

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 - 챕터4~5. μŠ€νƒ, μŠ€νƒ 도전

Raywenderlich.com μ—μ„œ λ‚˜μ˜¨ Data Structures & Algorithms in Swift μ±…μ˜ 데λͺ¨ κ³΅κ°œλ³Έμ„ λ²ˆμ—­ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 즐겁게 λ΄μ£Όμ„Έμš”. https://www.raywenderlich.com/books/data-structures-algorithms-in-swift μ„Ήμ…˜..

kimdee.tistory.com

> 챕터 6-1. μ—°κ²°λ¦¬μŠ€νŠΈ Linked List (μ—°κ²°λ¦¬μŠ€νŠΈ μ •μ˜, μš”μ†Œ μΆ”κ°€, μ‚­μ œ)

 

 

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 - 챕터6-1. μ—°κ²°λ¦¬μŠ€νŠΈ (μ •

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 - 챕터6-1. μ—°κ²°λ¦¬μŠ€νŠΈ Raywenderlich.com μ—μ„œ λ‚˜μ˜¨ Data Structures & Algorithms in Swift μ±…μ˜ 데λͺ¨ κ³΅κ°œλ³Έμ„ λ²ˆμ—­ν•˜μ˜€μŠ΅λ‹ˆλ‹€. 즐겁..

kimdee.tistory.com

> 챕터 6-2. μ—°κ²°λ¦¬μŠ€νŠΈ Linked List (μŠ€μœ„ν”„νŠΈ ν‘œμ€€ 라이브러리의 μ½œλ ‰μ…˜ ν”„λ‘œν† μ½œ, 카우 Copy-On-Write,  λ°Έλ₯˜ μ‹œλ§¨ν‹±)

 

 

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 - 챕터6-2. μ—°κ²°λ¦¬μŠ€νŠΈ

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 - 챕터6-2. μ—°κ²°λ¦¬μŠ€νŠΈ (μŠ€μœ„ν”„νŠΈ μ½œλ ‰μ…˜ ν”„λ‘œν† μ½œ, λ°Έλ₯˜ μ‹œλ§¨ν‹±, COW(μΉ΄ν”Ό-온-라이트)) Raywenderlich.com μ—μ„œ λ‚˜μ˜¨ Data Structures &..

kimdee.tistory.com

 

> 챕터 7. μ—°κ²°λ¦¬μŠ€νŠΈ λ„μ „κ³Όμ œ Linked List Challenges

 

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 - 챕터7. μ—°κ²°λ¦¬μŠ€νŠΈ 도전

[Swift][λ²ˆμ—­] μŠ€μœ„ν”„νŠΈμ˜ μžλ£Œκ΅¬μ‘°μ™€ μ•Œκ³ λ¦¬μ¦˜ - μ„Ήμ…˜ 2. 기초 자료ꡬ쑰 - 챕터7. μ—°κ²°λ¦¬μŠ€νŠΈ λ„μ „κ³Όμ œ Raywenderlich.com μ—μ„œ λ‚˜μ˜¨ Data Structures & Algorithms in Swift μ±…μ˜ 데λͺ¨ κ³΅κ°œλ³Έμ„ λ²ˆμ—­ν•˜μ˜€μŠ΅λ‹ˆλ‹€..

kimdee.tistory.com

 


마치며 

 

μ˜€λŠ˜λ„ μ½μ–΄μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€.

 

κΆκΈˆν•œ 점이 μžˆλ‹€λ©΄ λŒ“κΈ€λ‘œ, 도움이 λ˜μ…¨λ‹€λ©΄ 곡감 λΆ€νƒλ“œλ¦½λ‹ˆλ‹€.

ν˜Ήμ‹œ μˆ˜μ •ν•˜κ±°λ‚˜ ν”Όλ“œλ°±ν•˜μ‹€  λ‚΄μš© μžˆλ‹€λ©΄ μ–Έμ œλ“  λŒ“κΈ€λ‘œ λΆ€νƒλ“œλ¦½λ‹ˆλ‹€.

 

κ°μ‚¬ν•©λ‹ˆλ‹€.

 

 

 

λ°˜μ‘ν˜•

λŒ“κΈ€