Golang-线性存储-链表-环形链表

  • 1.初始化
  • 2.定义环形链表结点结构体
  • 3.创建环形链表
  • 4.打印双向链表
    • 1.递归实现
    • 1.循环实现
  • 5.获取双向链表的长度[数据结点长度]

是单向链表的特例。将单向链表的尾结点,指向第一个数据结点(而不是指向头结点)

1.初始化

1
2
3
4
5
mkdir -p linkList
cd linkList
go mod init linkList
touch linkList.go
vim linkList.go

2.定义环形链表结点结构体

1
2
3
4
5
6
7
// 定义环形链表的结点结构体
type LinkNode struct {
    // 定义环形链表的数据域
    Data interface{}
    // 定义环形链表指针域
    Next *LinkNode
}

3.创建环形链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 创建环形链表
func (node *LinkNode) NewLinkList(data ...interface{}) {
    // 容错处理
    if node == nil || len(data) == 0 {
        return
    }
    // 保存head结点
    head := node
    // 遍历data数据创建新结点
    for _, v := range data {
        // 创建新结点,并初始化
        newNode := new(LinkNode)
        newNode.Data = v
        newNode.Next = nil

        // 将当前结点的指针域指向新结点
        node.Next = newNode
        // 更新当前结点
        node = node.Next
    } // 遍历结束,node在尾结点
    // 将尾结点的指针域指向第一个数据结点(head.Next)
    node.Next = head.Next
    // 将head结点赋值给node,以便node回到head结点位置
    node = head
}

4.打印双向链表

1.递归实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 正向打印环形链表--递归实现
var start interface{}

func (node *LinkNode) Print1() {
    // 容错处理
    if node == nil {
        return
    }
    // 如果是头结点就给第一个数据结点标记赋值[头结点的数据域为nil]
    if node.Data == nil {
        // 做第一个数据结点的标记
        start = node.Next
    }
    // 数据结点的特点:有数据数据域
    if node.Data != nil {
        fmt.Print(node.Data, " ")
        //  递归的出口: 此时应该在尾结点[数据域不为空,],其下一个结点(node.Next)即将回到第一个数据结点
        if node.Next == start {
            // 在递归调用出口处打印换行
            fmt.Println()
            return
        }
    }
    // 递归实现
    node.Next.Print1()
}

测试

1
2
3
4
5
func main() {
    list := new(LinkNode)
    list.NewLinkList(1, 2, 3, 4, 5, 6, 7)
    list.Print1()
}

1.循环实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 正向打印环形链表--循环实现
func (node *LinkNode) Print2() {
    // 容错处理
    if node == nil {
        return
    }
    // 定义第一个数据结点的位置标记
    start := node.Next
    for node.Next != nil {
        node = node.Next // 更新node结点位置,在初始时,可以跳过head指针[head结点:没有数据域(nil),只有指针域(指向第一个数据结点)]
        // 数据结点的特点:有数据数据域
        if node.Data != nil {
            fmt.Print(node.Data, " ")
        }
        // 判断是否在尾结点(即node.Next是否指向第一个数据结点位置)
        if start == node.Next {
            // 已经在尾结点,直接返回
            return
        }
    }
    // 打印换行
    fmt.Println()
}

测试

1
2
3
4
5
func main() {
    list := new(LinkNode)
    list.NewLinkList(1, 2, 3, 4, 5, 6, 7)
    list.Print2()
}

5.获取双向链表的长度[数据结点长度]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 获取数据结点长度
func (node *LinkNode) Length() (length int) {
    // 容错处理
    if node == nil {
        return
    }
    // 标记第一个数据结点的位置:head结点的下一个结点[node.Next]
    start := node.Next
    for {
        node = node.Next // 初始可以跳过head结点
        length++
        // 如果当前在尾结点,即指针域指向第一个数据结点时
        if start == node.Next {
            break
        }
    }
    /* // 也可以使用如下方法获取:
    for {
        node = node.Next // 初始跳过head 结点
        if node.Data != nil {
            length++
        }
        // 如果当前在尾结点,即指针域指向第一个数据结点时
        if start == node.Next {
            break
        }
    } */
    return
}

测试

1
2
3
4
5
6
7
func main() {
    list := new(LinkNode)
    list.NewLinkList(1, 2, 3, 4, 5, 6, 7)
    list.Print1()
    length := list.Length()
    fmt.Println("环形链表的数据结点长度为:", length)
}