概述
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的 “指针” 将一组零散的内存卡串联起来实现的。
单向链表
单向链表其中有两个结点是比较特殊的,它们分别是第一个结点和最后一个结点。我们习惯性地把第一个结点叫作头结点,把最后一个结点叫作尾结点。其中,头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。
插入节点
删除节点
代码实现
package main
import "fmt"
type Node struct {
id int
name string
next *Node
}
func InsertNode(headNode *Node, newNode *Node) {
temp := headNode
for {
if temp.next == nil {
break
}
temp = temp.next
}
temp.next = newNode
}
func ShowNode(headNode *Node) {
temp := headNode
if temp.next == nil {
fmt.Println("空空如也...")
return
}
for {
fmt.Println(temp.next.id, temp.next.name)
temp = temp.next
if temp.next == nil {
break
}
}
}
func DelNode(headNode *Node, id int) {
temp := headNode
flag := false
for {
if temp.next == nil {
break
} else if temp.next.id == id {
flag = true
break
}
temp = temp.next
}
if flag {
temp.next = temp.next.next
} else {
fmt.Println("ID不存在")
}
}
func main() {
headNode := &Node{}
node1 := &Node{
id: 1,
name: "aa",
}
node2 := &Node{
id: 2,
name: "bb",
}
node3 := &Node{
id: 3,
name: "cc",
}
InsertNode(headNode, node1)
InsertNode(headNode, node2)
InsertNode(headNode, node3)
DelNode(headNode, 2)
ShowNode(headNode)
}
目前往链表中插入默认是不排序的,我们可以实现根据ID来排序,代码修改如下:
func InsertNodeOrderByID(headNode *Node, newNode *Node) {
temp := headNode
flag := true
for {
if temp.next == nil {
break
} else if temp.next.id > newNode.id {
break
} else if temp.next.id == newNode.id {
flag = false
break
}
temp = temp.next
}
if flag {
newNode.next = temp.next
temp.next = newNode
} else {
fmt.Println("ID已存在")
return
}
}
双向链表
双向链表需要额外的两个空间来存储后继结点和前驱结点的地址。所以,如果存储同样多的数据,双向链表要比单链表占用更多的内存空间。虽然两个指针比较浪费存储空间,但可以支持双向遍历,这样也带来了双向链表操作的灵活性。
插入节点
删除节点
代码实现
package main
import "fmt"
type Node struct {
id int
name string
pre *Node
next *Node
}
func InsertNode(headNode *Node, newHeroNode *Node) {
temp := headNode
for {
if temp.next == nil {
break
}
temp = temp.next
}
temp.next = newHeroNode
newHeroNode.pre = temp
}
func InsertNodeOrderByID(headNode *Node, newNode *Node) {
temp := headNode
flag := true
for {
if temp.next == nil {
break
} else if temp.next.id > newNode.id {
break
} else if temp.next.id == newNode.id {
flag = false
break
}
temp = temp.next
}
if flag {
newNode.next = temp.next
newNode.pre = temp
if temp.next != nil {
temp.next.pre = newNode
}
temp.next = newNode
} else {
fmt.Println("ID已存在")
return
}
}
func ShowNode(headNode *Node) {
temp := headNode
if temp.next == nil {
fmt.Println("空空如也...")
return
}
for {
fmt.Println(temp.next.id, temp.next.name)
temp = temp.next
if temp.next == nil {
break
}
}
}
func DelNode(headNode *Node, id int) {
temp := headNode
flag := false
for {
if temp.next == nil {
break
} else if temp.next.id == id {
flag = true
break
}
temp = temp.next
}
if flag {
temp.next = temp.next.next
if temp.next != nil {
temp.next.pre = temp
}
} else {
fmt.Println("ID不存在")
}
}
func main() {
headNode := &Node{}
node1 := &Node{
id: 1,
name: "aa",
}
node2 := &Node{
id: 2,
name: "bb",
}
node3 := &Node{
id: 3,
name: "cc",
}
InsertNodeOrderByID(headNode, node3)
InsertNodeOrderByID(headNode, node1)
InsertNodeOrderByID(headNode, node2)
DelNode(headNode, 2)
ShowNode(headNode)
}
环形链表
环形链表是一种特殊的单链表。实际上,环形链表也很简单。它跟单链表唯一的区别就在尾结点。我们知道,单链表的尾结点指针指向空地址,表示这就是最后的结点了。而环形链表的尾结点指针是指向链表的头结点。
插入节点
删除节点
代码实现
package main
import "fmt"
type Node struct {
id int
name string
next *Node
}
func InsertNode(headNode *Node, newNode *Node) {
if headNode.next == nil {
headNode.id = newNode.id
headNode.name = newNode.name
headNode.next = headNode
return
}
temp := headNode
for {
if temp.next == headNode {
break
}
temp = temp.next
}
temp.next = newNode
newNode.next = headNode
}
func ShowNode(headNode *Node) {
temp := headNode
if temp.next == nil {
fmt.Println("空空如也...")
return
}
for {
fmt.Println(temp.id, temp.name)
if temp.next == headNode {
break
}
temp = temp.next
}
}
func DelNode(headNode *Node, id int) *Node {
temp := headNode
helper := headNode
if temp.next == nil {
fmt.Println("这是一个空的环形链表,不能删除")
return headNode
}
if temp.next == headNode {
temp.next = nil
return headNode
}
for {
if helper.next == headNode {
break
}
helper = helper.next
}
flag := true
for {
if temp.next == headNode {
break
}
if temp.id == id {
helper.next = temp.next
flag = false
}
temp = temp.next
helper = helper.next
}
if flag && temp.id == id {
helper.next = temp.next
fmt.Println("删除ID:", id)
} else {
fmt.Println("ID不存在")
}
return headNode
}
func main() {
headNode := &Node{}
node1 := &Node{
id: 1,
name: "aa",
}
node2 := &Node{
id: 2,
name: "bb",
}
node3 := &Node{
id: 3,
name: "cc",
}
InsertNode(headNode, node3)
InsertNode(headNode, node1)
InsertNode(headNode, node2)
DelNode(headNode, 2)
ShowNode(headNode)
}
约瑟夫问题
Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 num(1<=num<=n)的人从 1 开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
实现思路
用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除算法结束。
代码实现
package main
import "fmt"
type Boy struct {
No int
Next *Boy
}
func AddBoy(num int) *Boy {
first := &Boy{}
curBoy := &Boy{}
if num < 1 {
fmt.Println("num 的值不对")
return first
}
for i := 0; i <= num; i++ {
boy := &Boy{
No: i,
}
if i == 1 {
first = boy
curBoy = boy
curBoy.Next = first
} else {
curBoy.Next = boy
curBoy = boy
curBoy.Next = first
}
}
return first
}
func ShowBoy(first *Boy) {
if first.Next == nil {
fmt.Println("链表为空")
return
}
curBoy := first
for {
fmt.Println("Boy No=", curBoy.No)
if curBoy.Next == first {
break
}
curBoy = curBoy.Next
}
}
func PlayGame(first *Boy, startNo int, countNum int) {
if first.Next == nil {
fmt.Println("链表为空")
return
}
tail := first
for {
if tail.Next == first {
break
}
tail = tail.Next
}
for i := 1; i <= startNo; i++ {
first = first.Next
tail = tail.Next
}
for {
for i := 1; i <= countNum-1; i++ {
first = first.Next
tail = tail.Next
}
fmt.Println("出列的 Boy No=", first.No)
first = first.Next
tail.Next = first
if tail == first {
break
}
}
fmt.Println("最后出列的 Boy No=", first.No)
}
func main() {
first := AddBoy(30)
PlayGame(first, 2, 3)
}
|