如何用Swift 實現一個單鏈表?

單鏈表

爲何須要單鏈表

單鏈表和數組同屬於線性表結構,爲何有了數組還須要設計單鏈表這種結構呢?node

一、從時間複雜度的角度思考

答:當連續插入、或者連續刪除操做較多時,鏈表比數組更加合適。由於當連續插入或者刪除時,鏈表的第一次的時間複雜度爲O(n),後續操做的時間複雜度均爲O(1)。而對於數組來講,屢次操做的時間複雜度均爲O(n)。git

數組和鏈表對於查找、更新、插入、刪除四種操做的時間複雜度對好比下:github

  • 數組:查找、更新的時間複雜度爲O(1);插入和刪除的時間複雜度爲O(n)
  • 單鏈表:查找、更新、插入、刪除的時間複雜度均爲O(n)

因此,若是咱們的需求是查找、更新的操做較多的話,推薦使用數組;而若是是連續插入、刪除的操做較多的話,推薦使用鏈表。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))
    }
}
複製代碼
相關文章
相關標籤/搜索