python3怎么创建一个链表_第二章:链表(1)

链表是按线性单向序列排列的值的集合。与SWIFT语言里Array等连续存储数据结构相比,链表在理论上具有两个优势:

  • 在链表头插入和删除元素的时间恒定
  • 可靠的性能特征

c42ec5014a901e5e543a5506c7ee8b09.png

如图所示,链表是一个节点链。节点有两个职责:

  • 持有一个元素
  • 持有下一个节点的引用。最后一个节点持有的引用为nil值

892ddc844532dbed9073eb6f54384846.png

节点(Node)

我们可以边动手边看文章,新建一个SOURCES目录,并在其中创建一个新的SWIFT文件,并将其命名为Node.swift。并将下列代码添加到文件里:

public class Node<Value> {
    public var value: Value
    public var next: Node?
    public init(value: Value, next: Node? = nil) {
        self.value = value
        self.next = next
    }
}

extension Node: CustomStringConvertible{
    public var description: String {
        guard let next = next else {
            return "(value)"
        }
    return "(value) -> " + String(describing: next) + " "
    }
}

导航到Playground页面并添加以下内容:

example(of: "creating and linking nodes") {
    let node1 = Node(value: 1)
    let node2 = Node(value: 2)
    let node3 = Node(value: 3)
    node1.next = node2
    node2.next = node3
    print(node1)
}

上边的代码其实就是让你创建了三个节点并将它们连接起来:

1a711ddcadb51e2412362bc18f458842.png

在控制台中,您应该看到以下输出:

---Example of creating and linking nodes---
1 -> 2 -> 3

就实用性而言,当前构建列表的方法还有很多不尽如人意的地方。您可以很容易地看到,以这种方式构建长列表是不切实际的。解决此问题的一种常见方法是构建管理Node对象的LinkedList。让我们继续。

链表(LinkedList)

在Sources目录中,创建一个新文件并将其命名为LinkedList.swift。将以下内容添加到文件中:

public struct LinkedList<Value> {
    public var head: Node<Value>?
    public var tail: Node<Value>?
    public init() {}
    public var isEmpty: Bool {
        return head == nil
    }
}
extension LinkedList: CustomStringConvertible {
    public var description: String {
        guard let head = head else {
            return "Empty list"
        }
        return String(describing: head)
    }
}

链表具有头部和尾部的概念,分别指链表的第一个和最后一个节点:

81ec0df1f0f3b3b312759c776246b747.png

添加数据到列表中

如前所述,我们可以提供一个接口来管理Node对象。你首先需要关心的是怎么添加值。这里有三种向链接列表添加值的方法,每种方法都具有各自独特的性能特征: 1.push:在列表前面增加一个值。 2.append:在列表末尾添加值。 3.insert(after:):在列表的特定节点后添加值。 你将在下一节中实现其中的每一个,并分析它们的性能特征。

push

在链表前面添加值称为push操作。这也称为头部优先插入。它的代码非常简单。 将以下方法添加到LinkedList:

public mutating func push(_ value: Value) {
    head = Node(value: value, next: head)
    if tail == nil {
        tail = head
    }
}

在push空链表的情况下,新节点既是链表的头部,也是链表的尾部。 返回playground页面并添加以下内容:

example(of: "push") {
    var list = LinkedList<Int>()
    list.push(3)
    list.push(2)
    list.push(1)
    print(list)
}

你的控制台输出应显示以下内容:

---Example of push---
1 -> 2 -> 3

append

你将看到的下一个操作是append。这意味着在列表的末尾添加值,称为尾端插入。 返回LinkedList.swift并在push方法下面添加以下代码:

public mutating func append(_ value: Value) {
    guard !isEmpty else {
    push(value)
    return
    }
    tail!.next = Node(value: value)
    tail = tail!.next
}

此代码相对简单: 1.和以前一样,如果链表为空,则需要将头部和尾部都更新为新节点。由于applend在空列表上增加一个元素与push功能相同,因此只需调用push即可为您完成这项工作。 2.在所有其他情况下,您只需在尾节点之后创建一个新节点。由于在isEmpty情况下使用上述GUARD语句声明,因此强制解包一定会成功。 3.由于这是尾端插入,因此你的新节点也是列表的尾部。

跳回Playground,在底部写下以下内容:

example(of: "append") {
    var list = LinkedList<Int>()
    list.append(1)
    list.append(2)
    list.append(3)
    print(list)
}

你在控制台中看到以下输出:

---Example of append---
1 -> 2 -> 3

insert(after:)

添加值的第三个也是最后一个操作是insert(after:)。

此操作在列表中的特定位置插入值,需要两个步骤: 1.在列表中查找特定节点。 2.插入新节点。 首先,我们将实现代码来查找要插入值的节点。 回到LinkedList.swift,在append方法下面添加以下代码:

public func node(at index: Int) -> Node<Value>? {
// 1
var currentNode = head
var currentIndex = 0
// 2
while currentNode != nil && currentIndex < index {
currentNode = currentNode!.next
currentIndex += 1
}
return currentNode
}

node(at:)将尝试根据给定索引检索列表中的节点。由于只能从Head节点访问列表的节点,因此必须进行迭代遍历。以下是步骤: 1. 创建一个新的Head引用,并跟踪当前遍历的数量。 2. 使用while循环,你可以在列表中向下移动引用,直到达到所需的索引。空列表或超出界限的索引将导致nil返回值。

现在需要插入新节点。 在node(at:)正下方添加以下方法:

// 1
@discardableResult
public mutating func insert(_ value: Value, after node: Node<Value>) -> Node<Value> {
// 2
    guard tail !== node else {
    append(value)
    return tail!
    }
// 3
    node.next = Node(value: value, next: node.next)
    return node.next!
}

以下是你所做的事情:

  1. @discardableResult允许调用者忽略此方法的返回值,而编译器不会上下跳警告你。
  2. 在使用尾节点调用此方法的情况下,你将调用功能等效的append方法。这将处理更新尾部。
  3. 否则,只需将新节点与列表的其余部分链接起来,然后返回新节点。

跳回playground页面测试上述。将以下内容添加到playground底部:

example(of: "inserting at a particular index") {
    var list = LinkedList<Int>()
    list.push(3)
    list.push(2)
    list.push(1)
    print("Before inserting: (list)")
    var middleNode = list.node(at: 1)!
    for _ in 1...4 {
        middleNode = list.insert(-1, after:     middleNode)
    }
    print("After inserting: (list)")
}

你在控制台中看到以下输出:

---Example of inserting at a particular index---
Before inserting: 1 -> 2 -> 3
After inserting: 1 -> 2 -> -1 -> -1 -> -1 -> -1 -> 3

性能分析

棒!到目前为止你已经取得了很好的进步。简单地说,你已经实现了向链接列表添加值的三个操作,以及在特定索引处查找节点的方法。

29ecc10cd886d87b60ed37f9867454cd.png

下一章节,我们将关注相反的操作:删除操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值