單鏈表和數組同屬於線性表結構,爲何有了數組還須要設計單鏈表這種結構呢?node
答:當連續插入、或者連續刪除操做較多時,鏈表比數組更加合適。由於當連續插入或者刪除時,鏈表的第一次的時間複雜度爲O(n),後續操做的時間複雜度均爲O(1)。而對於數組來講,屢次操做的時間複雜度均爲O(n)。git
數組和鏈表對於查找、更新、插入、刪除四種操做的時間複雜度對好比下:github
因此,若是咱們的需求是查找、更新的操做較多的話,推薦使用數組;而若是是連續插入、刪除的操做較多的話,推薦使用鏈表。swift
數組需佔用一整塊連續的內存,而鏈表不須要,它能夠分散存儲於內存上。能夠對內存進行更加高效的使用。數組
完整代碼地址bash
首先對於鏈表來講,要先定義它的節點:app
/// 單鏈表的節點
class LinkNode<E: Equatable> {
var val: E
var next: LinkNode?
init(val: E) {
self.val = val
}
static func == (lhs: LinkNode, rhs: LinkNode) -> Bool {
return lhs.val == rhs.val
}
}
複製代碼
接下來須要定義鏈表支持的接口,由於後續可能須要建立雙向鏈表和循環鏈表,因此這裏將通用的接口用 Protocol 來實現:函數
/// 單鏈表和雙鏈表的公共接口
protocol LinkedListFunction {
associatedtype E: Equatable
/// 在鏈表頭部添加節點
/// - Parameter newElement: 添加的節點
func append(atHead newElement: E)
/// 在鏈表尾部添加節點
/// - Parameter newElement: 添加的節點
func append(atTail newElement: E)
/// 插入節點
/// - Parameters:
/// - newElement: 添加的節點
/// - i: 添加的位置
func insert(_ newElement: E, at i: Int)
/// 移除節點
/// - Parameter index: 移除的位置
/// - Returns: 被移除的節點
func remove(at index: Int) -> E?
/// 移除頭部節點
/// - Returns: 被移除的節點
func removeFirst() -> E?
/// 移除尾部節點
/// - Returns: 被移除的節點
func removeLast() -> E?
/// 移除全部節點
func removeAll()
/// 更新節點
/// - Parameters:
/// - index: 更新節點的位置
/// - newElement: 新節點
func update(at index: Int, _ newElement: E)
/// 獲取節點值
/// - Parameter index: 獲取位置
/// - Returns: 當前 index 的節點值
func index(of index: Int) -> E?
/// 是否包含 element
/// - Parameter element: 須要查找的 element
/// - Returns: 若是鏈表中包含該元素,返回 true,反之則返回 false
func contains(_ element: E) -> Bool
}
複製代碼
在定義完節點和接口後,下面來實現單鏈表的類:測試
class SingleLinkedList<E: Equatable> {
/// 鏈表的頭結點
var head: LinkNode<E>?
/// 鏈表的長度
private(set) var count = 0
/// 內部獲取節點的方法
/// - Parameter index: 獲取位置
/// - Returns: 當前 index 的節點
private func _node(_ index: Int) -> LinkNode<E>? {
guard index < count else {
return nil
}
if index == 0 { return head }
var curNode = head
var curIndex = index
while curIndex > 0 {
curNode = curNode?.next
curIndex -= 1
}
return curNode
}
/// 打印鏈表當前元素 - 方便調試
func linkedListPrint() -> [E] {
var nodes = [E]()
var curNode = head
while curNode != nil {
nodes.append(curNode!.val)
curNode = curNode?.next
}
print("linkedListPrint ==== \(nodes)")
return nodes
}
}
複製代碼
func append(atHead newElement: E) {
if head == nil {
head = LinkNode(val: newElement)
} else {
let newHead = LinkNode(val: newElement)
newHead.next = head
head = newHead
}
count += 1
}
func append(atTail newElement: E) {
if let tail = _node(count - 1) {
tail.next = LinkNode(val: newElement)
count += 1
}
}
func insert(_ newElement: E, at i: Int) {
guard i <= count else { return }
if i == 0 {
append(atHead: newElement)
} else if i == count {
append(atTail: newElement)
} else {
if let curNode = _node(i - 1) {
let insertNode = LinkNode(val: newElement)
insertNode.next = curNode.next
curNode.next = insertNode
count += 1
}
}
}
複製代碼
@discardableResult
func remove(at index: Int) -> E? {
guard head != nil else { return nil }
guard index < count else { return nil }
if index == 0 {
return removeFirst()
} else if index == count - 1 {
return removeLast()
} else {
let prevTail = _node(index - 1)
let val = prevTail?.next?.val
prevTail?.next = prevTail?.next?.next
count -= 1
return val
}
}
@discardableResult
func removeFirst() -> E? {
let val = head?.val
if count == 1 {
head = nil
} else {
head = head?.next
}
count -= 1
return val
}
@discardableResult
func removeLast() -> E? {
guard head != nil else { return nil }
if count == 1 {
let val = head?.val
head = nil
count -= 1
return val
} else {
let prevTail = _node(count - 2)
let val = prevTail?.next?.val
prevTail?.next = prevTail?.next?.next
count -= 1
return val
}
}
func removeAll() {
count = 0
head = nil
}
複製代碼
func update(at index: Int, _ newElement: E) {
guard let curNode = _node(index) else { fatalError("Index out of range") }
curNode.val = newElement
}
@discardableResult
func index(of index: Int) -> E? {
return _node(index)?.val
}
func contains(_ element: E) -> Bool {
guard head != nil else { return false }
var curNode = head
while curNode != nil {
if curNode!.val == element {
return true
}
curNode = curNode?.next
}
return false
}
複製代碼
上面就是用 Swift 實現單鏈表的所有代碼,下面的是測試代碼用來保證代碼的正確性。ui
struct SingleLinkedListTest {
static func test() {
self.testAppend()
self.testRemove()
self.testUpdate()
self.testContains()
}
static func testAppend() {
let linkedList = SingleLinkedList<Int>()
linkedList.append(atHead: 1)
assert(linkedList.count == 1, "append(atHead newElement: Int) error")
linkedList.append(atTail: 3)
assert(linkedList.count == 2, "append(atTail newElement: Int) error")
linkedList.insert(2, at: 1)
assert(linkedList.count == 3, "insert(_ newElement: Int, at i: Int) error")
let res1 = linkedList.index(of: 1)!
assert(res1 == 2, "index(of index: Int) error")
}
static func testRemove() {
let linkedList = SingleLinkedList<Int>()
linkedList.append(atHead: 1)
linkedList.append(atTail: 3)
linkedList.insert(2, at: 1)
linkedList.removeLast()
assert(linkedList.linkedListPrint() == [1, 2])
linkedList.removeLast()
assert(linkedList.linkedListPrint() == [1])
linkedList.removeLast()
assert(linkedList.linkedListPrint() == [])
linkedList.append(atHead: 1)
linkedList.append(atTail: 3)
linkedList.insert(2, at: 1)
linkedList.removeFirst()
assert(linkedList.linkedListPrint() == [2, 3])
linkedList.removeFirst()
assert(linkedList.linkedListPrint() == [3])
linkedList.removeFirst()
assert(linkedList.linkedListPrint() == [])
linkedList.append(atHead: 1)
linkedList.append(atTail: 3)
linkedList.insert(2, at: 1)
linkedList.remove(at: 1)
assert(linkedList.linkedListPrint() == [1, 3])
linkedList.remove(at: 2)
assert(linkedList.linkedListPrint() == [1, 3])
linkedList.remove(at: 1)
assert(linkedList.linkedListPrint() == [1])
linkedList.remove(at: 0)
assert(linkedList.linkedListPrint() == [])
}
static func testUpdate() {
let linkedList = SingleLinkedList<Int>()
linkedList.append(atHead: 1)
linkedList.append(atTail: 3)
linkedList.insert(2, at: 1)
linkedList.update(at: 1, 5)
assert(linkedList.index(of: 1)! == 5, "update(at index: Int, _ newElement: E) error")
}
static func testContains() {
let linkedList = SingleLinkedList<Int>()
linkedList.append(atHead: 1)
linkedList.append(atTail: 3)
linkedList.insert(2, at: 1)
assert(linkedList.contains(3))
assert(linkedList.contains(1))
assert(linkedList.contains(2))
}
}
複製代碼