Githubnode
链表:链式结构,使用节点来存储内容,经过当前节点的 next 指针指向下一个节点。能够经过分散的内存来存储节点,所以内存利用率更高,占用内存空间更小。但由于只有经过 next 指针才能找到下一个节点,因此每次查找节点都须要从头结点开始。git
由于 next 指针的关系,它的查找效率和修改的效率不如数组,但添加和删除的效率要高于数组。github
结构图:
(Node) (Node) (Node)
----------- ----------- -----------
| | | | | |
head--> | value | ---> | value | ---> | value | --> nil
| | | | | |
----------- ----------- -----------
复制代码
class Node {
var val: Int
var next: Node?
init(val: Int, next: Node?) {
self.val = val
self.next = next
}
}
class LinkList {
var dummy: Node?
var size: Int
init() {
dummy = Node(val: 0, next: nil)
size = 0
}
private func node(_ index: Int) -> Node? {
var curNode = dummy?.next
var curIndex = index
while curIndex > 0 {
curNode = curNode?.next
curIndex -= 1
}
return curNode
}
}
复制代码
首先声明一个包含 val 和 next 的 Node 类。val 用来存储值,而 next 用来指向下一个节点。数组
而后再声明一个名 LinkList 类来表明链表。dummy 的 next 用来指向头结点,size 用来存储链表的当前长度。markdown
这里须要说明的是,为何要设置 dummy 节点。由于这样作是能够避免在添加第一个节点时的不少逻辑判断。app
最后,建立一个 node(_:) 的私有函数,用来获取当前 index 的节点。函数
public func append(val: Int) {
let newNode = Node(val: val, next: nil)
if size == 0 {
dummy?.next = newNode
} else {
var curSize = size
var curNode = dummy
while curSize > 0 {
curNode = curNode?.next
curSize -= 1
}
curNode?.next = newNode
}
size += 1
}
复制代码
在尾部添加节点有如下两种状况:oop
在 condition 1 的状况下,将 dummy 的 next 指向 newNode 便可,而在 condition 2 的状况下,寻找到尾结点,将尾结点的 next 指向 newNode。spa
最后,不要忘记将 size + 1。
指针
public func remove(_ index: Int) -> Int? {
guard index > -1 && index < size else { return nil }
let val: Int
if index == 0 {
val = dummy?.next?.val ?? 0
dummy?.next = dummy?.next?.next
} else {
let prev = node(index - 1)
val = prev?.next?.val ?? 0
prev?.next = prev?.next?.next
}
size -= 1
return val
}
复制代码
进行移除操做时,须要对 index 的合法性
进行校验。
移除操做同添加操做也是有如下两种状况:
移除操做的核心就是找到须要移除节点的前一个节点 prev(经过 node(_:) 能够得到),将 prev 的 next 指针指向它 next 的 next 便可。如:prev?.next = prev?.next?.next
。
最后不要忘记 size - 1 。
public func insert(_ val: Int, atIndex index: Int) {
guard index > -1 && index <= size else { return }
let newNode = Node(val: val, next: nil)
if index == 0 {
newNode.next = dummy?.next
dummy?.next = newNode
} else {
let pre = node(index - 1)
newNode.next = pre?.next
pre?.next = newNode
}
size += 1
}
复制代码
插入操做相对来讲是比较复杂的一个操做,要考虑好 index == 0,index == size 等条件的状况。
插入操做的核心操做分两步:
以上两步的顺序很重要,切记不要搞错。
经过上面的步骤来分析插入操做的代码。当 index == 0 时,说明要在头结点的位置进行插入,首先进行第一步:将 newNode 的 next 指向当前 index 的节点 - newNode.next = dummy?.next
;接着进行第二步:将当前节点的 prev 节点的 next 指向新建节点 newNode - dummy?.next = newNode
。
当 index > 0 时同上。最后不要忘记 size + 1 。
public func get(_ index: Int) -> Int? {
guard index > -1 && index < size - 1 else { return nil }
return node(index)?.val
}
复制代码
查询操做是最简单的一个。只须要判断下 index 的有效性,而后经过 node(_:) 获取相应的节点便可。
以上,就是链表基本操做的实现。
上面的示例代码只支持存储 Int 类型的数据,而在项目中可能须要支持多种类型,因此将其改为支持泛型仍是颇有必要的。
class Node<Element> {
var val: Element?
var next: Node?
init(val: Element?, next: Node?) {
self.val = val
self.next = next
}
}
class LinkList<Element> {
var dummy: Node<Element>?
var size: Int
public init() {
dummy = Node(val: nil, next: nil)
size = 0
}
public func append(val: Element) {
let newNode = Node(val: val, next: nil)
if size == 0 {
dummy?.next = newNode
} else {
// 查找尾结点
var curSize = size
var curNode = dummy
while curSize > 0 {
curNode = curNode?.next
curSize -= 1
}
curNode?.next = newNode
}
size += 1
}
public func remove(_ index: Int) -> Element? {
guard index > -1 && index < size else { return nil }
let val: Element?
if index == 0 {
val = dummy?.next?.val
dummy?.next = dummy?.next?.next
} else {
let prev = node(index - 1)
val = prev?.next?.val
prev?.next = prev?.next?.next
}
size -= 1
return val
}
public func get(_ index: Int) -> Element? {
guard index > -1 && index < size - 1 else { return nil }
return node(index)?.val
}
public func insert(_ val: Element, atIndex index: Int) {
guard index > -1 && index <= size else { return }
let newNode = Node(val: val, next: nil)
if index == 0 {
newNode.next = dummy?.next
dummy?.next = newNode
} else {
let pre = node(index - 1)
newNode.next = pre?.next
pre?.next = newNode
}
size += 1
}
private func node(_ index: Int) -> Node<Element>? {
var curNode = dummy?.next
var curIndex = index
while curIndex > 0 {
curNode = curNode?.next
curIndex -= 1
}
return curNode
}
}
复制代码
经过重写 subscript 也能够支持索引操做。
extension LinkList {
public subscript(index: Int) -> Element? {
return node(index)?.val
}
}
复制代码