Bibi's DevLog ๐ค๐
[Swift ์๋ฃ๊ตฌ์กฐ] 3. Linked List ์ฐ๊ฒฐ ๋ฆฌ์คํธ (Singly Linked List) ๋ณธ๋ฌธ
[Swift ์๋ฃ๊ตฌ์กฐ] 3. Linked List ์ฐ๊ฒฐ ๋ฆฌ์คํธ (Singly Linked List)
๋น๋น bibi 2022. 12. 19. 22:41Data 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 ๋จ๋ฐฉํฅ ์ฐ๊ฒฐ ๋ฆฌ์คํธ (๋จ์ผ ์ฐ๊ฒฐ ๋ฆฌ์คํธ)
โญ๏ธ ์ด๋ด ๋ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ฅผ ์ฌ์ฉํ๋ค
- ๋ฐ์ดํฐ์ ํฌ๊ธฐ๋ฅผ ๋ฏธ๋ฆฌ ์ ์ ์๋ ๊ฒฝ์ฐ
โ ์ด๋ด ๋ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค
- ๋ง์ ์์ ๋ฐ์ดํฐ์ ํ์ ์ฐ์ฐ์ ํด์ผ ํ๋ ๊ฒฝ์ฐ
- ๋ฐ์ดํฐ ์ํ๋ฅผ ์์ฃผ ํด์ผ ํ๋ ๊ฒฝ์ฐ
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
}
}
'ํ๋ก๊ทธ๋๋ฐ > Data Structures ์๋ฃ๊ตฌ์กฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Swift ์๋ฃ๊ตฌ์กฐ] 5. Queue ํ (0) | 2022.12.22 |
---|---|
[Swift ์๋ฃ๊ตฌ์กฐ] 4. Stack ์คํ (๋ฐฐ์ด/์ฐ๊ฒฐ๋ฆฌ์คํธ๋ก ๊ตฌํํ๊ธฐ) (0) | 2022.12.21 |
[Swift ์๋ฃ๊ตฌ์กฐ] 2. Dictionary ๋์ ๋๋ฆฌ (์์ฑ, CRUD) (0) | 2022.12.16 |
[Swift ์๋ฃ๊ตฌ์กฐ] 1. Array ๋ฐฐ์ด (์์ฑ, CRUD, COW) (0) | 2022.12.16 |
[Swift] ์ฐ๊ฒฐ๋ฆฌ์คํธ LinkedList, ์๋ฐฉํฅ ์ฐ๊ฒฐ๋ฆฌ์คํธ DoublyLinkedList (1) | 2022.10.06 |