「数据结Go」循环链表

209 阅读4分钟

什么是循环链表

循环链表-基础定义

结构特性

  • 一种链式存储结构;
  • 表中有且只有一个头节点;
  • 表中有且只有一个尾节点;
  • 尾节点的Next是头节点;
  • 头节点和尾节点可以是同一个节点,当表中总共只有一个节点时。

优势

  • 添加或删除节点时,不用移动表中的其它节点,修改直接相关的节点的前后节点即可。

缺点

  • 对随机索引查询不友好,与所有的链表结构一样。

实用场景

  • 构建时间轮调度算法。

上手搓

已支持的功能

  1. 添加节点到头部;
  2. 添加节点到尾部;
  3. 删除头部节点;
  4. 删除尾部节点;
  5. 添加节点到任意位置;
  6. 删除任意位置节点。

核心源码

CircleLinkedList.go

定义节点和循环链表结构体

type Node struct {
    /**
    * 数字类型的id,每个节点的id唯一
     */
    ID int
    /**
    * 节点上可承载任意类型的业务数据
     */
    Data interface{}
    /**
    * 下一节点
     */
    Next *Node
}
​
type CircleLinkedList struct {
    /**
    * 头节点
     */
    Head *Node
    /**
    * 尾节点
     */
    Tail *Node
    /**
    * 链表中的节点数量
     */
    Size int
}

添加新节点到头部

  1. 当添加第一个节点到链表中时,头部和尾部节点都是它,它的Next也是它本身;

    循环链表-添加第一个节点到头部

  2. 当添加新节点到非空链表头部时,更改新节点的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++
}

添加新节点到尾部

  1. 当添加第一个节点到链表中时,头部和尾部节点都是它,它的Next也是它本身;

循环链表-添加第一个节点到尾部

  1. 当添加新节点到已有其它节点的链表时,把头部节点设置为新节点的下一节点。

    循环链表-添加第二个节点到尾部

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++
}

添加新节点到指定节点后方

  1. 如果指定位置实际上是尾部,直接当作添加到尾部处理。

  2. 如果指定位置不是尾部,先把新节点的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++
}

删除头部节点

  1. 当删除的是最后一个节点时,把头部和尾部节点设置为空。

    循环链表-删除头部节点

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
}

删除尾部节点

  1. 当删除的是最后一个节点时,把头部和尾部节点设置为空;

  2. 否则找到尾部节点之前的节点,设置为新的尾部节点。

    循环链表-删除尾部节点

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
}

删除任意指定的节点

  1. 当删除的是头节点时,当作删除头部处理;

  2. 当删除的是尾节点时,当作删除尾部处理;

  3. 否则找到要删除节点的前面和后面节点,修改它们之间的关系。

    循环链表-删除指定位置的节点

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
添加新的尾节点21->2->1
添加新的尾节点41->2->4->1
添加新节点32之后:1->2->3->4->1
添加新的尾节点51->2->3->4->5->1
添加新节点65之后:1->2->3->4->5->6->1
添加新的尾节点81->2->3->4->5->6->8->1
添加新节点76之后:1->2->3->4->5->6->7->8->1
完成构建本测试用例的链表:1->2->3->4->5->6->7->8->1
删除链表头节点12->3->4->5->6->7->8->2
删除链表尾节点82->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("添加新节点32之后:")
	linkedList.Print()

	linkedList.AddToTail(node5)
	fmt.Print("添加新的尾节点5:")
	linkedList.Print()

	linkedList.Add(node6, node5)
	fmt.Print("添加新节点65之后:")
	linkedList.Print()

	linkedList.AddToTail(node8)
	fmt.Print("添加新的尾节点8:")
	linkedList.Print()

	linkedList.Add(node7, node6)
	fmt.Print("添加新节点76之后:")
	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()
}