Computer Science/DataStructure
Linked List
삼쓰 웅쓰
2019. 11. 19. 00:27
728x90
- 선형 자료구조
- 순차적 접근방식 ( O(n) )
- 삽입, 삭제가 빠르다 ( O(1) )
- 다음 노드를 가리키는 주소를 저장할 공간 필요
Linked List는 순차적으로 모인 데이터들의 모음이다.
흔히 각각의 데이터를 Node(노드), 각 노드는 다음 차례의 노드의 reference(주소)를 갖고 있다.
대표적으로 두 가지 유형이 있는데,
Singly Linked List: 내 다음 노드(next node)의 주소만을 갖고 있다.
Double Linked List: 내 이전(previous)과 다음의 주소를 갖고 있다.
Linked List를 사용하기 위해서는 시작과 끝의 주소를 알아야 하는데 이를 head와 tail이라 부른다.
Performance
Linked List의 대부분의 연산은 O(n)의 시간복잡도를 갖는다. 대부분 배열보다 느리다. 해당 크기만큼 메모리를 미리 잡아야 하는 배열에 비해 리스트는 포인터만 옮겨주면 되기 때문에 훨씬 유연하다.
Linked List의 대부분의 연산이 O(n)인 이유는 특정 노드에 직접 접근할 수 없기 때문인데, 그 노드의 주소를 알고 있지 않다면 head 또는 tail에서 부터 찾아나가야 한다.
하지만 일단 노드를 찾았다면 추가 삭제는 매우 빠르다.(O(1))
Linked List Implementation in Swift 5
구현 내용
- head // O(1)
- tail // O(1)
- first // O(1)
- last // O(1)
- isEmpty // O(1)
- node(at index:) // O(n)
- append(_:) // O(1)
- insert(_:at:) // O(1)
- removeAll() // O(1)
- remove(node:) // O(1)
- remove(at:) // O(n)
- description
class LinkedList<T> {
class Node<T> {
var value: T
var next: Node?
weak var previous: Node?
init(_ value: T) {
self.value = value
}
}
private(set) var head: Node<T>?
private(set) var tail: Node<T>?
private(set) var count = 0
var first: T? {
return head?.value
}
var last: T? {
return tail?.value
}
var isEmpty: Bool {
return head == nil
}
func node(at index: Int) -> Node<T>? {
guard 0..<count ~= index else { return nil }
var node = head!
var current = 0
while index > current, let next = node.next {
node = next
current += 1
}
return node
}
func append(value: T) {
let newNode = Node(value)
guard !isEmpty else {
head = newNode
tail = newNode
count += 1
return
}
if count == 1 {
head?.next = tail
}
tail?.next = newNode
newNode.previous = tail
tail = newNode
count += 1
}
func insert(_ value: T, at index: Int) {
let node = Node(value)
if index == 0 {
node.next = head
head?.previous = node
head = node
count += 1
} else if index == count {
append(value: value)
} else {
assert(0..<count ~= index, "Out of Index: \(index), range: \(0)..<\(count)")
let next = self.node(at: index)
let before = next?.previous
node.previous = before
node.next = next
before?.next = node
next?.previous = node
count += 1
}
}
func removeAll() {
head = nil
tail = nil
count = 0
}
@discardableResult
func remove(node: Node<T>) -> T {
if node === head, node === tail {
head = nil
tail = nil
return node.value
} else if node === head {
defer {
head = node.next
head?.previous = nil
}
return node.value
} else if node === tail {
defer {
tail?.previous = tail
tail?.next = nil
}
return node.value
} else {
let before = node.previous
let next = node.next
before?.next = next
next?.previous = before
node.next = nil
node.previous = nil
count -= 1
return node.value
}
}
@discardableResult
func remove(at index: Int) -> T? {
assert(0..<count ~= index, "Out of Index: \(index), range: \(0)..<\(count)")
guard let node = node(at: index) else { return nil }
return remove(node: node)
}
}
extension LinkedList.Node: CustomStringConvertible {
var description: String {
guard let next = next else {
return "\(value)"
}
return "\(value) -> " + "\(next)" + " "
}
}
extension LinkedList: CustomStringConvertible {
var description: String {
guard let head = head else {
return "List is Empty"
}
return "\(head)"
}
}