我们上一章学习的,栈、队列,都是静态线性数据结构,底层依托于静态数组,依靠resize解决固定容量问题。
但是链表是真正的动态数据结构,也是最简单的动态数据结构,我们学习链表能够更加深入的理解指针,同时也能更加深入的理解递归函数。
链表的数组存储在节点(Node)中,节点通常有两部分组成,当节点数据为空的时候,就是最后一个节点
class Node {
E e; //节点数据
Node next; //指向下一个节点
}
- 优点:真正的动态,不需要处理固定容量问题
- 缺点:丧失了随机访问的能力
数组和链表
-
数组最好用户索引有语义的情况
-
最大的优点:支持快速查询
-
链表不适合用于索引有语义的情况
-
最大的优点:动态
我们在为链表头部添加元素的时候,我们发现我们不知道第一个元素以前的节点是什么,这个时候为了更好更方便的处理,在链表中添加第一个元素,我们就创建一个虚拟的节点
class LinkedList: NSObject {
private var dummyHead:Node = Node() //虚拟头节点
private var size:Int! = 0 //链表中的元素个数
override init() {
super.init()
dummyHead.E = ""
dummyHead.next = Node()
}
// 获取链表中的元素个数
func getSize() -> Int {
return size
}
//在链表中添加元素
func add(index:Int,E:String) {
var prev = dummyHead
for _ in 0..<index {
prev = prev.next
}
prev.next = Node(E: E, next: prev.next)
size += 1
}
//在链表头部添加元素
func addFirst(E:String) {
add(index: 0, E: E);
}
// 在链表末尾添加新的元素e
func addLast(E:String) {
add(index: size, E: E)
}
//获取链表元素
func get(index:Int) -> String {
var cur = dummyHead
for _ in 0..<index {
cur = cur.next
}
return cur.E
}
//修改链表元素
func set(index:Int,E:String) {
var cur = dummyHead
for _ in 0..<index {
cur = cur.next
}
cur.E = E
}
//删除链表元素
func delete(index:Int) {
var prev = dummyHead
for _ in 0..<index {
prev = prev.next
}
let retNode = prev.next
prev.next = retNode?.next
size -= 1
}
//打印
func toString() {
var res = ""
var cur = dummyHead
for _ in 0..<size {
cur = cur.next
res.append("->\(cur.E)")
}
print(res + "->NULL")
}
}
class Node: NSObject {
var E:String = "" //元素
var next:Node! //指向下一个节点
override init() {
super.init()
}
init(E:String,next:Node) {
self.E = E
self.next = next
}
}
删除链表中等于给定值 val 的所有节点。
示例:
输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
不使用虚拟头节点
func removeElements(_ head: ListNode?, _ val: Int) -> ListNode? {
var headNode = head
//判断第一个节点的val值是否是需要移除的那一个
while headNode != nil && headNode?.val == val {
headNode = headNode?.next
}
//第一个节点判断结束以后如果,链表为空,直接return
if headNode == nil {
return headNode
}
//判断剩余链表
var prev:ListNode = headNode!
while prev.next != nil {
if prev.next?.val == val{
//下一个节点如果是要去除的值,去掉下一个节点值
//把当前节点的下一个节点的值 修改为 当前节点下下一个节点的值,这个步骤是去掉下一个节点的值
prev.next = prev.next?.next
}else{
//遍历下一个节点
prev = prev.next!
}
}
return headNode
}
使用虚拟头节点
//如果要是使用虚拟头结点的话
func removeElements1(_ head: ListNode?, _ val: Int) -> ListNode? {
let dummyHead = ListNode(-1)
dummyHead.next = head
var prev:ListNode = dummyHead
while prev.next != nil {
if prev.next?.val == val{
//下一个节点如果是要去除的值,去掉下一个节点值
//把当前节点的下一个节点的值 修改为 当前节点下下一个节点的值,这个步骤是去掉下一个节点的值
prev.next = prev.next?.next
}else{
//遍历下一个节点
prev = prev.next!
}
}
return dummyHead.next
}