单链表
为什么需要单链表
单链表和数组同属于线性表结构,为什么有了数组还需要设计单链表这种结构呢?
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))
}
}
复制代码