缓存:
缓存的大小有限,当缓存被用满时,哪些数据应该被清理出去,哪些数据应该被保留?
缓存淘汰策略:
链表不需要一块连续的内存空间,它通过"指针"将一组零散的内存块
串联起来使用.
结点:
存储数据. data
记录链上的下一个结点的地址. 后继指针 next
–> (data, next) --> (data, next) --> (data, next) --> NULL
头结点
: 第一个结点, 记录链表的基地址
尾结点
: next 指向一个空地址 NULL, 表示这是链表上最后一个结点
链表的插入或删除时间复杂度是 O(1)
.
随机访问第 k 个元素, 需要从第一个一个向下找, 时间复杂度是 O(n)
尾结点
: next 指向头结点
从链尾到链头比较方便
记录链上的上一个结点的地址. 前驱指针 prev
–> (prev, data, next) <–> (prevdata, next) <–> (prevdata, next) <–> (prevdata, next) <–> (prevdata, next) --> NULL
链表的删除操作时间复杂度是 O(1)
, 但是在实际的软件开发中, 删除往往需要先查找, 查找的时间复杂度是 O(n)
, 总的时间复杂度就是 O(n)
了
对于 删除给定指针指向的结点
, 单链表要从头查询一遍, 找到上一个结点, 把其 next 指向删除节点的下一个结点, 时间复杂度是 O(n)
. 双向链表直接就可以获取上一个结点, 时间复杂度是 O(1)
.
Java 的 LinkedHashMap 就是一个双向链表.
双向链表是一个空间换时间的设计思想.
缓存就是利用了空间换时间的设计思想。
ArrayList 也可以动态扩容, 但是需要数据拷贝, 非常耗时.
链表进行频繁的插入/删除操作, 会导致频繁的内存申请和释放, 容易造成内存碎片. 如果是 Java, 就有可能会导致频繁的 GC (Garbage Collection, 垃圾回收).
维护一个有序单链表,越靠近链表尾部的结点是越早之前访问的。当有一个新的数据被访问时,我们从链表头开始顺序遍历链表。
不管缓存有没有满,都需要遍历一遍链表,所以这种基于链表的实现思路,缓存访问的时间复杂度为 O(n)。
可以继续优化, 例如收入散列表 (Hash table) 记录每个数据的位置, 时间复杂度降至 O(1)
.
如果字符串是通过单链表来存储的,那该如何来判断是一个回文串呢
时间复杂度 O(n)
, 空间复杂度 O(1)
.
将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。
针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理。
如果引入哨兵结点,在任何时候,不管链表是不是空,head 指针都会一直指向这个哨兵结点。把这种有哨兵结点的链表叫带头链表。相反,没有哨兵结点的链表就叫作不带头链表。
经常用来检查链表代码是否正确的边界条件有这样几个:
type Link interface {
IsEmpty() bool
Length() int
Contain(data interface{}) bool
Add(data interface{})
Append(data interface{})
Insert(index int, data interface{})
Delete(data interface{}) bool
DeleteAt(index int) bool
Reverse()
IsHasRing() bool
String() string
Print()
}
import (
"bytes"
"fmt"
)
type SingleNode struct {
Data interface{}
Next *SingleNode
}
type SingleLink struct {
header *SingleNode
}
// IsEmpty 是否为空链表
func (self *SingleLink) IsEmpty() bool {
return self.header == nil
}
// Length 长度
func (self *SingleLink) Length() int {
count, cur := 0, self.header
for cur != nil {
count++
cur = cur.Next
}
return count
}
// Contain 是否包含
func (self *SingleLink) Contain(data interface{}) bool {
cur := self.header
for cur != nil {
if cur.Data == data {
return true
}
cur = cur.Next
}
return false
}
func (self *SingleLink) String() string {
buf := bytes.NewBufferString("")
cur := self.header
for cur != nil {
// TODO 优化
buf.WriteString(cur.Data.(string))
cur = cur.Next
if cur != nil {
buf.WriteString("-")
}
}
return buf.String()
}
func (self *SingleLink) Print() {
if self.IsHasRing() {
fmt.Println("链表存在环, 无法打印")
} else {
fmt.Println(self.String())
}
}
// Add 头部添加
func (self *SingleLink) Add(data interface{}) {
node := &SingleNode{
Data: data,
Next: self.header,
}
self.header = node
}
// Append 尾部添加
func (self *SingleLink) Append(data interface{}) {
node := &SingleNode{
Data: data,
}
if self.IsEmpty() {
self.header = node
} else {
cur := self.header
for cur.Next != nil {
cur = cur.Next
}
cur.Next = node
}
}
// Insert 指定位置添加
func (self *SingleLink) Insert(index int, data interface{}) {
if index == 0 {
self.Add(data)
} else {
node := &SingleNode{
Data: data,
}
cur := self.header
if cur == nil {
cur = &SingleNode{}
}
index--
for index > 0 {
if cur.Next == nil {
cur.Next = &SingleNode{}
cur = cur.Next
}
index--
}
node.Next = cur.Next
cur.Next = node
}
}
// Delete 删除数据
func (self *SingleLink) Delete(data interface{}) bool {
pre := self.header
if pre == nil {
return false
}
if pre.Data == data {
self.header = pre.Next
return true
}
cur := pre.Next
for cur != nil {
if cur.Data == data {
pre.Next = cur.Next
return true
}
cur = cur.Next
}
return false
}
// DeleteAt 删除指定位置数据
func (self *SingleLink) DeleteAt(index int) bool {
pre := self.header
if pre == nil {
return false
}
if index == 0 {
self.header = pre.Next
return true
}
cur := pre.Next
index--
for cur != nil && index > 0 {
pre = cur
cur = cur.Next
index--
}
if cur != nil {
pre.Next = cur.Next
return true
}
return false
}
func (self *SingleLink) Reverse() {
pre := self.header
if pre == nil || pre.Next == nil {
return
}
cur := pre.Next
pre.Next = nil
for cur != nil {
tmp := cur.Next
cur.Next = pre
pre = cur
cur = tmp
}
self.header = pre
}
// IsHasRing 是否存在环
func (self *SingleLink) IsHasRing() bool {
slow := self.header
fast := self.header
for fast != nil && fast.Next != nil && fast.Next.Next != nil {
slow = slow.Next
fast = fast.Next.Next
if slow == fast {
return true
}
}
return false
}
// GetAt 根据索引获取指定位置的节点
func (self *SingleLink) GetNodeAt(index int) *SingleNode {
cur := self.header
for cur != nil && index > 0 {
cur = cur.Next
index--
}
return cur
}
// Merge 两个有序的链表合并
func (self *SingleLink) Merge(list2 *SingleLink) *SingleLink {
header := &SingleNode{}
link := &SingleLink{header: header}
node1 := self.header
node2 := list2.header
for node1 != nil || node2 != nil {
// TODO 优化, 暂时以字符串处理
if node1 != nil && (node2 == nil || node1.Data.(string) < node2.Data.(string)) {
header.Next = node1
header = header.Next
node1 = node1.Next
} else if node2 != nil && (node1 == nil || node2.Data.(string) < node1.Data.(string)) {
header.Next = node2
header = header.Next
node2 = node2.Next
}
}
link.DeleteAt(0)
return link
}
func Test(link Link) {
link.Print()
}