一、链表节点
class ListNode {
var val: Int
var node: ListNode
init(_ val: Int){
self.val = val
self.node = nil
}
}
二、通过节点实现链表
class List {
var head: ListNode?
var tail: ListNode?
// 尾插法
func appendToTail(_ val: Int) {
if tail == nil { // 确定头部节点
tail = ListNode(val)
head = tail
} else {
// 如下方式保证了尾部节点始终为传入的最新节点
tail?.next = ListNode(val)
tail = tail?.next
}
}
// 头插法
/*
头插法就是从后往前寻找节点,当前传入的节点在链表中位于head节点之前
*/
func appendToHead(_ val: Int) {
if head == nil {
head = ListNode(val)
tail = head
} else {
let temp = ListNode(val)
temp.next = head
head = temp
}
}
}
三、Dummy节点和尾插法
Dummy节点的作用是作为一个虚拟的头前节点。在不知道要返回的新链表的头结点是哪一个,它可能是原链表的第一个节点,可能在原链表的中间,也可能在最后,甚至不存在(nil)。引入Dummy节点可以涵盖所有情况,并且可以使用dummy.next返回最终需要的头结点。
四、算法实例
给出一个链表和一个值x,要求将链表中所有小于x得值放在左边,左右大于或者等于x的值放到右边,并且原链表的节点顺序不能变。例如:1 -> 5 -> 3 -> 2 -> 4 -> 2,给定x = 3,则要返回 1 -> 2 -> 2 -> 5 -> 3 -> 4。
思路:将链表分为小于x节点与大于x节点进行处理,最后进行左右连接。同时采用尾插法,遍历链表。
func partiton(_ head: ListNode?, _ target: Int) {
// 引入Dummy节点
var preDummy = ListNode(0), postDummy = ListNode(0)
var pre = preDummy, post = postDummy, node = head
while node != nil {
if node!.val < target {
pre.next = node
pre = node!
} else {
post.next = node
post = node!
}
node = node!.next
}
preDummy.next = postDummy.next
post.next = nil // 防止构成环
return preDummy.next
}
测试代码:
let firstNode = ListNode(1)
let secondNode = ListNode(5)
let thirdNode = ListNode(3)
let fourthNode = ListNode(2)
let fifthNode = ListNode(4)
let sixthNode = ListNode(2)
firstNode.next = secondNode
secondNode.next = thirdNode
thirdNode.next = fourthNode
fourthNode.next = fifthNode
fifthNode.next = sixthNode
var result = self.getLeftList(firstNode, 3)
while result != nil {
print("node val: \(String(describing: result?.val))")
result = result?.next
}
注意点:post.next = nil,是为了防止链表循环指向构成环,这是必需的但是容易忽略的一步。