Bibi's DevLog ๐ค๐
[Swift] ์ฐ๊ฒฐ๋ฆฌ์คํธ LinkedList, ์๋ฐฉํฅ ์ฐ๊ฒฐ๋ฆฌ์คํธ DoublyLinkedList ๋ณธ๋ฌธ
ํ๋ก๊ทธ๋๋ฐ/Data Structures ์๋ฃ๊ตฌ์กฐ
[Swift] ์ฐ๊ฒฐ๋ฆฌ์คํธ LinkedList, ์๋ฐฉํฅ ์ฐ๊ฒฐ๋ฆฌ์คํธ DoublyLinkedList
๋น๋น bibi 2022. 10. 6. 23:21์ฐ๊ฒฐ ๋ฆฌ์คํธ๋
์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ ๊ฐ ์์๊ฐ ๋ ธ๋๋ก ๋์ด ์๋ ์ฐ์๋ ๋ฐ์ดํฐ๋ค์ด๋ค.
- node ๋ ธ๋ : ์ฐ๊ฒฐ ๋ฆฌ์คํธ์ ํ๋์ ์์๋ฅผ ๋งํจ
- head : ์ฐ๊ฒฐ ๋ฆฌ์คํธ์ ์์ ๋ ธ๋๋ฅผ ์ฐธ์กฐํ๋ ํฌ์ธํฐ
- tail : ์ฐ๊ฒฐ ๋ฆฌ์คํธ์ ๋ ๋ ธ๋๋ฅผ ์ฐธ์กฐํ๋ ํฌ์ธํฐ
์ ํ
- ๋จ๋ฐฉํฅ ์ฐ๊ฒฐ ๋ฆฌ์คํธ singly linked list : ๊ฐ ํญ๋ชฉ์ด ๋ค์ ๋ ธ๋์ ๋ํ ์ฐธ์กฐ๋ง ๊ฐ์ง
- ์๋ฐฉํฅ ์ฐ๊ฒฐ ๋ฆฌ์คํธ doubly linked list : ๊ฐ ํญ๋ชฉ์ด ์, ๋ค ๋ ธ๋์ ๋ํ ์ฐธ์กฐ๋ฅผ ๋ชจ๋ ๊ฐ์ง
๋จ๋ฐฉํฅ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๊ตฌํํ๊ธฐ (Swift)
https://www.raywenderlich.com/books/data-structures-algorithms-in-swift/v3.0/chapters/6-linked-list
ํ๋กํ ์ฝ ์ฑํํ๋ ๋ถ๋ถ ์ด์ ๊น์ง ์ ๋ฆฌํ์ต๋๋ค.
Node
๋จ๋ฐฉํฅ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ ๋ ธ๋๋ก ๊ตฌ์ฑ๋์ด ์์ผ๋ฉฐ, ๊ฐ ๋ ธ๋๋ value, next๋ฅผ ์ง๋๋ค.
- value : ๋ ธ๋๊ฐ ๋ด๋ ๊ฐ
- next : ๋ค์ ๋ ธ๋์ ๋ํ ํฌ์ธํฐ
public class LinkedListNode<T> {
public var value: T
public var next: LinkedListNode?
public init(value: T, next: LinkedListNode? = nil) {
self.value = value
self.next = next
}
}
LinkedList
public struct LinkedList<T> {
public typealias Node = LinkedListNode<T>
public var head: Node?
public var tail: Node?
public init() {}
public var isEmpty: Bool {
head == nil
}
// node(at:) : O(i), i=index. ํน์ ์ธ๋ฑ์ค์ ๋
ธ๋๋ฅผ ์ฐพ๊ธฐ
public func node(at index: Int) -> Node? {
var currentNode = head
var currentIndex = 0
while currentNode != nil && currentIndex < index {
currentNode = currentNode!.next
currentIndex += 1
}
// ๋ชฉ๋ก์ด ๋น์ด์๊ฑฐ๋ ์ธ๋ฑ์ค๋ฅผ ๋์ด๊ฐ๋ฉด nil ๋ฐํ
return currentNode
}
// MARK: Adding ์ฝ์
// push : O(1), ๋งจ ์ฒ์์ ์ฝ์
head-first insertion
public mutating func push(_ value: T) {
head = Node(value: value, next: head)
if tail == nil { // ๋น ์ฐ๊ฒฐ๋ฆฌ์คํธ์ ๋ฃ์ ๊ฒฝ์ฐ ์ฒซ ์์๊ฐ head ์ด์ tail์
tail = head
}
}
// append: O(1), ๋งจ ๋ค์ ์ฝ์
tail-end insertion
public mutating func append(_ value: T) {
if isEmpty { // ๋น์ด ์๋ ๋ฐฐ์ด์ ๋ฃ์ ๋๋ push == append
push(value)
return
}
tail!.next = Node(value: value)
tail = tail!.next
}
@discardableResult // ๋ฆฌํด๊ฐ์ ์ฌ์ฉํ์ง ์๋๋ผ๋ ์ปดํ์ผ๋ฌ๊ฐ ๊ฒฝ๊ณ ๋ฅผ ๋ณด๋ด์ง ์๊ฒ ํจ
// insert(_:after:) : O(1), ํด๋น ๋
ธ๋ ๋ค์ ์ฝ์
.
public mutating func insert(_ value: T, after node: Node) -> Node {
guard tail !== node else { // ์ฐธ์กฐ ๋น๊ต
// node๊ฐ tail์ธ ๊ฒฝ์ฐ insert == append
append(value)
return tail!
}
node.next = Node(value: value, next: node.next)
return node.next!
}
// MARK: Removing ์ญ์
// pop : O(1). ๋งจ ์์์ ์ ๊ฑฐ.
@discardableResult
public mutating func pop() -> T? {
defer { // ํจ์ ์ข
๋ฃ ์ง์ ์ ์คํ
head = head?.next // head ์ ๊ฑฐ - ์ฐธ์กฐ๊ฐ ์์ผ๋ฏ๋ก
if isEmpty {
tail = nil
}
}
return head?.value
}
// removeLast() : O(n). ๋งจ ๋ค์์ ์ ๊ฑฐ.
@discardableResult
public mutating func removeLast() -> T? {
guard let head = head else { // ๋น์ด์์ ๋
return nil
}
guard head.next != nil else { // ๋
ธ๋๊ฐ 1๊ฐ๋ฟ์ผ๋
return pop()
}
// ๋
ธ๋๊ฐ 2๊ฐ์ด์์ผ๋ : ๋งจ ๋ ๋
ธ๋๋ฅผ ์ฐพ์์ผ ํจ
var prev = head
var current = head
while let next = current.next { // next๊ฐ nil์ด ์๋๋ฉด ๋ฐ๋ณต
prev = current
current = next
}
// ์ฌ๊ธฐ์ ๋๋ฌํ๋ฉด current๊ฐ ๋ง์ง๋ง ๋
ธ๋๊ฐ ๋๋ค.
// ๋ง์ง๋ง ๋
ธ๋์ ๋ํ ์ฐธ์กฐ๋ฅผ ์์ ์ญ์ ํ๋ค
prev.next = nil // ๋ฉ๋ชจ๋ฆฌ์์ ํด์
tail = prev
return current.value
}
// remove(after:) : O(1). ํน์ ์์น์ ํน์ ๋
ธ๋๋ฅผ ์ญ์
// ์ญ์ ๋
ธ๋์ ์ ๋
ธ๋์ ๋ท ๋
ธ๋๋ฅผ ์ด์ด๋ถ์ฌ์ค๋ค
@discardableResult
public mutating func remove(after node: Node) -> T? {
// node == ์ญ์ ํ ๋
ธ๋์ ์ ๋
ธ๋
defer {
if node.next === tail { // tail ์ ๋
ธ๋ ์ญ์ ์
tail = node
}
node.next = node.next?.next
}
return node.next?.value
}
}
extension LinkedListNode: CustomStringConvertible {
public var description: String {
guard let next = next else {
return "\(value)"
}
return "\(value) -> " + String(describing: next) + " "
}
}
extension LinkedList: CustomStringConvertible {
public var description: String {
guard let head = head else {
return "Empty list"
}
return String(describing: head)
}
}
์๋ฐฉํฅ ์ฐ๊ฒฐ ๋ฆฌ์คํธ ๊ตฌํํ๊ธฐ (Swift)
https://www.raywenderlich.com/947-swift-algorithm-club-swift-linked-list-data-structure
Node
์๋ฐฉํฅ ์ฐ๊ฒฐ ๋ฆฌ์คํธ๋ ๋ ธ๋๋ก ๊ตฌ์ฑ๋์ด ์์ผ๋ฉฐ, ๊ฐ ๋ ธ๋๋ value, next, previous๋ฅผ ์ง๋๋ค.
- value : ๋ ธ๋๊ฐ ๋ด๋ ๊ฐ
- next : ๋ค์ ๋ ธ๋์ ๋ํ ํฌ์ธํฐ
- previous : ์ด์ ๋ ธ๋์ ๋ํ ํฌ์ธํฐ
public class DoublyLinkedListNode<T> {
var value: T
var next: DoublyLinkedListNode<T>? // ๋ง์ง๋ง ๋
ธ๋์ next๋ nil์ด ๋๋ฏ๋ก
weak var previous: DoublyLinkedListNode<T>? // ์ํ์ฐธ์กฐ ๋ฐฉ์ง
// weak ์์ผ๋ฉด A-B์ผ ๋ A๋ next๋ก B๋ฅผ, B๋ previous๋ก A๋ฅผ ์ฐธ์กฐํ๊ฒ ๋์ด ์ํ์ฐธ์กฐ ๋ฐ์
init(value: T) {
self.value = value
}
}
DoublyLinkedList
์ฐ๊ฒฐ ๋ฆฌ์คํธ์ ์์๊ณผ ๋์ ์ถ์ ํ๋ ์ญํ ์ด๋ค.
append(value:)
: ์ฐ๊ฒฐ ๋ฆฌ์คํธ์ ์ ๋ ธ๋ ์ถ๊ฐnodeAt(index: Int) -> Node?
: ์ฐ๊ฒฐ ๋ฆฌ์คํธ์ ํด๋น ์ธ๋ฑ์ค์ ์๋ ๋ ธ๋๋ฅผ ๊ฐ์ ธ์ดremoveAll()
: ์ฐ๊ฒฐ ๋ฆฌ์คํธ์ ๋ชจ๋ ๋ ธ๋ ์ญ์ remove(node: Node) -> String
: ์ฐ๊ฒฐ ๋ฆฌ์คํธ์ ํด๋น ๋ ธ๋ ์ญ์ - ์ฒซ ๋ ธ๋ ์ญ์ ํ๋ ๊ฒฝ์ฐ : head์ previous ํฌ์ธํฐ ์์ ํ์
- ์ค๊ฐ ๋ ธ๋ ์ญ์ ํ๋ ๊ฒฝ์ฐ : previous์ next ํฌ์ธํฐ ์์ ํ์
- ๋ง์ง๋ง ๋ ธ๋ ์ญ์ ํ๋ ๊ฒฝ์ฐ : tail๊ณผ next ํฌ์ธํฐ ์์ ํ์
extension LinkedList: CustomStringConvertible
: ์ถ๋ ฅ(print) ํํ ์ง์
public class DoublyLinkedList<T> { // ์๋ฐฉํฅ ์ฐ๊ฒฐ ๋ฆฌ์คํธ
public typealias Node = DoublyLinkedListNode<T>
fileprivate var head: Node?
private var tail: Node?
public var isEmpty: Bool {
return head == nil
}
public var first: Node? {
return head
}
public var last: Node? {
return tail
}
public func append(value: T) {
// 1. value๋ฅผ ๊ฐ๋ ์ ๋
ธ๋ ์์ฑ
let newNode = Node(value: value)
// 2-1. tailNode๊ฐ nil์ด ์๋ = ๋งํฌ๋๋ฆฌ์คํธ์ ๋ฌด์ธ๊ฐ ์์. ๋ฐ๋ผ์ ๊ธฐ์กด์ tail ๋ค์ ์ ๋
ธ๋๋ฅผ ์ถ๊ฐ.
if let tailNode = tail {
newNode.previous = tailNode
tailNode.next = newNode
// 2-2. ๋งํฌ๋๋ฆฌ์คํธ์ ์๋ฌด๊ฒ๋ ์์. ์ ๋
ธ๋๊ฐ head๊ฐ ๋จ
} else {
head = newNode
}
// 3. ๋ ๊ฒฝ์ฐ ๋ชจ๋ newNode๋ฅผ tail๋ก ์ง์ ํ๊ฒ ๋จ
tail = newNode
}
public func nodeAt(index: Int) -> Node? {
// ํน์ ์ธ๋ฑ์ค์ ๋
ธ๋๋ฅผ ๋ฐํ
if index >= 0 { // index๊ฐ ์์์ผ ๋ ๋ฌดํ๋ฃจํ ๋ฐฉ์ง
var node = head
var i = index
while node != nil { // ํด๋น ์ธ๋ฑ์ค์ ๋
ธ๋์ ๋๋ฌํ ๋ ๊น์ง ๋ฐ๋ณต
if i == 0 {
return node
}
i -= 1
node = node!.next
}
}
return nil // index๊ฐ ์์์ด๊ฑฐ๋ ์ฐ๊ฒฐ๋ฆฌ์คํธ์ ํญ๋ชฉ๋ณด๋ค ํด ๋
}
public func removeAll() {
head = nil
tail = nil
}
// MARK: ๋น์ ์ ๋์
public func remove(node: Node) -> T {
let prev = node.previous
let next = node.next
if let prev = prev { // prev๊ฐ ์๋ค๋ฉด ์ฒซ ๋
ธ๋๊ฐ ์๋ ๊ฒ์ด๋ฏ๋ก prev์ next ์์
prev.next = next
} else { // prev๊ฐ ์๋ค๋ฉด head์ด๋ฏ๋ก ๋ค์ ๋
ธ๋๋ฅผ head๋ก ์ง์
head = next
}
next?.previous = prev // next์ previous๋ฅผ ์ญ์ ๋๋ ๋
ธ๋์ prev๋ก ์ง์ .
if next == nil { // next๊ฐ ์๋ tail์ธ ๊ฒฝ์ฐ tail์ ์์
tail = prev
}
node.previous = nil
node.next = nil // ์ญ์ ํ๋ ๋
ธ๋๋ฅผ ์ฐธ์กฐํ๋ previous์ next์ nil ํ ๋น (๋ฉ๋ชจ๋ฆฌ ํด์ )
return node.value
}
}
extension DoublyLinkedListNode: CustomStringConvertible {
public var description: String {
return "\(value)"
}
}
extension DoublyLinkedList: CustomStringConvertible {
public var description: String {
var text = "["
var node = head
while node != nil {
text += "\(node!.value)"
node = node!.next
if node != nil {
text += ", "
}
}
return text + "]"
}
}