什么是循环链表
结构特性
- 一种链式存储结构;
- 表中有且只有一个头节点;
- 表中有且只有一个尾节点;
- 尾节点的Next是头节点;
- 头节点和尾节点可以是同一个节点,当表中总共只有一个节点时。
优势
- 添加或删除节点时,不用移动表中的其它节点,修改直接相关的节点的前后节点即可。
缺点
- 对随机索引查询不友好,与所有的链表结构一样。
实用场景
- 构建时间轮调度算法。
上手搓
已支持的功能
- 添加节点到头部;
- 添加节点到尾部;
- 删除头部节点;
- 删除尾部节点;
- 添加节点到任意位置;
- 删除任意位置节点。
核心源码
定义节点和循环链表结构体
type Node struct {
/**
* 数字类型的id,每个节点的id唯一
*/
ID int
/**
* 节点上可承载任意类型的业务数据
*/
Data interface{}
/**
* 下一节点
*/
Next *Node
}
type CircleLinkedList struct {
/**
* 头节点
*/
Head *Node
/**
* 尾节点
*/
Tail *Node
/**
* 链表中的节点数量
*/
Size int
}
添加新节点到头部
-
当添加第一个节点到链表中时,头部和尾部节点都是它,它的Next也是它本身;
-
当添加新节点到非空链表头部时,更改新节点的Next为原来的头节点,尾节点的Next为新节点。
func (c *CircleLinkedList) AddToHead(newNode *Node) {
if c.Size == 0 {
newNode.Next = newNode
c.Tail = newNode
} else {
c.Tail.Next = newNode
newNode.Next = c.Head
}
c.Head = newNode
c.Size++
}
添加新节点到尾部
- 当添加第一个节点到链表中时,头部和尾部节点都是它,它的Next也是它本身;
-
当添加新节点到已有其它节点的链表时,把头部节点设置为新节点的下一节点。
func (c *CircleLinkedList) AddToTail(newNode *Node) {
if c.Size == 0 {
newNode.Next = newNode
c.Head = newNode
} else {
c.Tail.Next = newNode
newNode.Next = c.Head
}
c.Tail = newNode
c.Size++
}
添加新节点到指定节点后方
-
如果指定位置实际上是尾部,直接当作添加到尾部处理。
-
如果指定位置不是尾部,先把新节点的Next变为前节点的Next,再把前节点的Next变为新节点
func (c *CircleLinkedList) Add(newNode *Node, previousNode *Node) {
if previousNode.ID == c.Tail.ID {
c.AddToTail(newNode)
return
}
newNode.Next = previousNode.Next
previousNode.Next = newNode
c.Size++
}
删除头部节点
-
当删除的是最后一个节点时,把头部和尾部节点设置为空。
func (c *CircleLinkedList) DeleteHead() (bool, error) {
if c.Size == 0 {
return false, errors.New("链表中已经没有元素")
}
if c.Size > 1 {
c.Head = c.Head.Next
c.Tail.Next = c.Head
} else {
c.Head = nil
c.Tail = nil
}
c.Size--
return true, nil
}
删除尾部节点
-
当删除的是最后一个节点时,把头部和尾部节点设置为空;
-
否则找到尾部节点之前的节点,设置为新的尾部节点。
func (c *CircleLinkedList) DeleteTail() (bool, error) {
if c.Size == 0 {
return false, errors.New("链表中已经没有元素")
}
if c.Size > 1 {
node := c.Head
for node.Next.ID != c.Tail.ID {
node = node.Next
}
node.Next = c.Head
} else if c.Size == 1 {
c.Head = nil
c.Tail = nil
}
c.Size--
return true, nil
}
删除任意指定的节点
-
当删除的是头节点时,当作删除头部处理;
-
当删除的是尾节点时,当作删除尾部处理;
-
否则找到要删除节点的前面和后面节点,修改它们之间的关系。
func (c *CircleLinkedList) Delete(node *Node) (bool, error) {
if c.Size == 0 {
return false, errors.New("链表中已经没有元素")
}
if node.ID == c.Head.ID {
c.DeleteHead()
return true, nil
}
if node.ID == c.Tail.ID {
c.DeleteTail()
return true, nil
}
var previousNode = *c.Head
var nextNode = *c.Head.Next
for nextNode.ID != c.Head.ID {
if nextNode.ID == node.ID {
break
}
previousNode = nextNode
nextNode = *nextNode.Next
}
*previousNode.Next = *nextNode.Next
c.Size--
return true, nil
}
自测验证
先看自测验证结果,为直观地表现循环链表,头节点在输出结果的末尾又打印了一次。
添加第一节点:1->1
添加新的尾节点2:1->2->1
添加新的尾节点4:1->2->4->1
添加新节点3到2之后:1->2->3->4->1
添加新的尾节点5:1->2->3->4->5->1
添加新节点6到5之后:1->2->3->4->5->6->1
添加新的尾节点8:1->2->3->4->5->6->8->1
添加新节点7到6之后:1->2->3->4->5->6->7->8->1
完成构建本测试用例的链表:1->2->3->4->5->6->7->8->1
删除链表头节点1:2->3->4->5->6->7->8->2
删除链表尾节点8:2->3->4->5->6->7->2
删除链表id=6的节点:2->3->4->5->7->2
以下是自测脚本 LinkedList_test.go。
func TestCircleLinkedList(t *testing.T) {
taskList := make([]string, 3)
taskList[0] = "任务0"
taskList[1] = "任务1"
taskList[2] = "任务2"
linkedList := linkedlist.CircleLinkedList{}
node2 := &linkedlist.Node{ID: 2}
node3 := &linkedlist.Node{ID: 3}
node4 := &linkedlist.Node{ID: 4}
node5 := &linkedlist.Node{ID: 5}
node6 := &linkedlist.Node{ID: 6}
node7 := &linkedlist.Node{ID: 7}
node8 := &linkedlist.Node{ID: 8}
linkedList.AddToHead(&linkedlist.Node{ID: 1, Data: taskList})
fmt.Print("添加第一节点:")
linkedList.Print()
linkedList.AddToTail(node2)
fmt.Print("添加新的尾节点2:")
linkedList.Print()
linkedList.AddToTail(node4)
fmt.Print("添加新的尾节点4:")
linkedList.Print()
linkedList.Add(node3, node2)
fmt.Print("添加新节点3到2之后:")
linkedList.Print()
linkedList.AddToTail(node5)
fmt.Print("添加新的尾节点5:")
linkedList.Print()
linkedList.Add(node6, node5)
fmt.Print("添加新节点6到5之后:")
linkedList.Print()
linkedList.AddToTail(node8)
fmt.Print("添加新的尾节点8:")
linkedList.Print()
linkedList.Add(node7, node6)
fmt.Print("添加新节点7到6之后:")
linkedList.Print()
fmt.Print("完成构建本测试用例的链表:")
linkedList.Print()
fmt.Print("删除链表头节点1:")
linkedList.DeleteHead()
linkedList.Print()
fmt.Print("删除链表尾节点8:")
linkedList.DeleteTail()
linkedList.Print()
fmt.Print("删除链表id=6的节点:")
linkedList.Delete(node6)
linkedList.Print()
}