如何用Swift 实现一个单链表?

11,099 阅读4分钟

单链表

为什么需要单链表

单链表和数组同属于线性表结构,为什么有了数组还需要设计单链表这种结构呢?

1、从时间复杂度的角度思考

答:当连续插入、或者连续删除操作较多时,链表比数组更加合适。因为当连续插入或者删除时,链表的第一次的时间复杂度为O(n),后续操作的时间复杂度均为O(1)。而对于数组来说,多次操作的时间复杂度均为O(n)。

数组和链表对于查找、更新、插入、删除四种操作的时间复杂度对比如下:

  • 数组:查找、更新的时间复杂度为O(1);插入和删除的时间复杂度为O(n)
  • 单链表:查找、更新、插入、删除的时间复杂度均为O(n)

所以,如果我们的需求是查找、更新的操作较多的话,推荐使用数组;而如果是连续插入、删除的操作较多的话,推荐使用链表。

2、从内存空间的角度思考

数组需占用一整块连续的内存,而链表不需要,它可以分散存储于内存上。可以对内存进行更加高效的使用。

代码实现

完整代码地址

首先对于链表来说,要先定义它的节点:

/// 单链表的节点
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 实现单链表的全部代码,下面的是测试代码用来保证代码的正确性。

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))
    }
}