示意图

image.png

注意

  1. 头节点有数据
  2. 最后一个节点的 next 指向 头节点 head, 构成一个环
  3. 删除节点
    1. 双指针,一前一后,前面的用来比较判断,后一个用来操作删除(单向的链表无法自删除,需要借助辅助节点)
    2. 只有一个节点,next 指向 nil
    3. 删除的是第一个节点,则需要重新修改 head 的指向

image.png

代码

  1. // 环形单向链表
  2. package main
  3. import "fmt"
  4. type CatNode struct {
  5. no int
  6. name string
  7. next *CatNode
  8. }
  9. func insertNode(head *CatNode, newCatNode *CatNode) {
  10. // 1. 处理第一个节点(头节点有值)
  11. if head.next == nil {
  12. head.no = newCatNode.no
  13. head.name = newCatNode.name
  14. head.next = head // 构成环形
  15. fmt.Printf("第一个节点: 猫: %v 加入环形链表 \n", newCatNode.name)
  16. return
  17. }
  18. // 2. 处理别的节点
  19. // 找到最后的节点
  20. // 定义临时变量
  21. temp := head
  22. for {
  23. if temp.next == head {
  24. break
  25. }
  26. temp = temp.next
  27. }
  28. // 加入到链表
  29. temp.next = newCatNode
  30. newCatNode.next = head
  31. }
  32. // 按顺序插入
  33. func insertNode02(head *CatNode, newCatNode *CatNode) {
  34. fmt.Println("insertNode02 - head: ", head)
  35. // 1. 处理第一个节点(头节点有值)
  36. if head.next == nil {
  37. head.no = newCatNode.no
  38. head.name = newCatNode.name
  39. head.next = head // 构成环形
  40. fmt.Printf("第一个节点: 猫: %v 加入环形链表 \n", newCatNode.name)
  41. return
  42. }
  43. // 2. 处理别的节点
  44. // 定义临时变量
  45. temp := head
  46. flag := true
  47. for {
  48. if temp.no == newCatNode.no {
  49. fmt.Printf("节点: %v , 已经存在,不允许加入", newCatNode.no)
  50. flag = false
  51. break
  52. } else if temp.next == head {
  53. break
  54. } else if temp.next.no > newCatNode.no {
  55. break
  56. } else if temp.next.no == newCatNode.no {
  57. fmt.Printf("节点: %v , 已经存在,不允许加入", newCatNode.no)
  58. flag = false
  59. break
  60. }
  61. temp = temp.next
  62. }
  63. // 加入到链表
  64. // temp.next = newCatNode
  65. // newCatNode.next = head
  66. if !flag {
  67. fmt.Println("不能插入节点")
  68. return
  69. } else {
  70. // 最后一个节点后插入
  71. if temp.next == head {
  72. temp.next = newCatNode
  73. newCatNode.next = head
  74. } else {
  75. // 中间节点插入
  76. newCatNode.next = temp.next
  77. temp.next = newCatNode
  78. }
  79. }
  80. }
  81. // 删除节点
  82. func delNode(head *CatNode, id int) *CatNode {
  83. // 这里的头节点是有值的
  84. fmt.Println("delNode - head: ", head)
  85. temp := head
  86. helper := head // helper 指向 head 的上一个节点, 即尾节点
  87. // 1. 空链表
  88. if temp.next == nil {
  89. fmt.Println("空链表")
  90. return head
  91. }
  92. // 2. 只有一个
  93. if temp.next == head {
  94. temp.next = nil // 没有任何指向就会被gc回收,即删除
  95. return head
  96. }
  97. // 3. 将 helper 指向最后
  98. for {
  99. if helper.next == head {
  100. break
  101. }
  102. helper = helper.next
  103. }
  104. // 4. 两个及以上节点
  105. flag := true
  106. for {
  107. if temp.next == head {
  108. // 只是比较,还没有执行删除操作
  109. break
  110. }
  111. if temp.no == id {
  112. if temp == head {
  113. // 删除的是头节点, 修改头节点的指向
  114. head = temp.next
  115. }
  116. helper.next = temp.next
  117. flag = false
  118. fmt.Printf("猫: no: %v 被移除链表了 \n", id)
  119. break
  120. }
  121. temp = temp.next // 移动 - 比较
  122. helper = helper.next // 移动 - 执行删除
  123. }
  124. // 这里要增加一次比较
  125. if flag {
  126. if temp.no == id {
  127. // temp 就是 要删除的节点
  128. helper.next = temp.next
  129. } else {
  130. fmt.Println("没有找到要删除的节点id: ", id)
  131. return head
  132. }
  133. }
  134. return head
  135. }
  136. // 显示所有节点
  137. func showNode(head *CatNode) {
  138. temp := head
  139. if temp.next == nil {
  140. fmt.Println("空链表")
  141. return
  142. }
  143. for {
  144. fmt.Printf("[猫: no: %v, name: %v] => ", temp.no, temp.name)
  145. if temp.next == head {
  146. break
  147. }
  148. temp = temp.next
  149. }
  150. fmt.Println()
  151. }
  152. func main() {
  153. // 初始化环形链表头节点
  154. head := &CatNode{}
  155. cat1 := &CatNode{
  156. no: 1,
  157. name: "tom1",
  158. }
  159. cat2 := &CatNode{
  160. no: 2,
  161. name: "tom2",
  162. }
  163. cat3 := &CatNode{
  164. no: 3,
  165. name: "tom3",
  166. }
  167. insertNode02(head, cat1)
  168. insertNode02(head, cat3)
  169. insertNode02(head, cat2)
  170. insertNode02(head, cat3)
  171. fmt.Println("显示")
  172. showNode(head)
  173. fmt.Println("删除 1")
  174. // 删除之后,重新制定 头节点 head
  175. head = delNode(head, 1)
  176. showNode(head)
  177. }
  178. /*
  179. insertNode02 - head: &{0 <nil>}
  180. 第一个节点: 猫: tom1 加入环形链表
  181. insertNode02 - head: &{1 tom1 0xc00004e3c0}
  182. insertNode02 - head: &{1 tom1 0xc00004e420}
  183. insertNode02 - head: &{1 tom1 0xc00004e400}
  184. 节点: 3 , 已经存在,不允许加入不能插入节点
  185. 显示
  186. [猫: no: 1, name: tom1] => [猫: no: 2, name: tom2] => [猫: no: 3, name: tom3] =>
  187. 删除 1
  188. delNode - head: &{1 tom1 0xc00004e400}
  189. 猫: no: 1 被移除链表了
  190. [猫: no: 2, name: tom2] => [猫: no: 3, name: tom3] =>
  191. */

👉约瑟夫问题

Josephu 问题
Josephu 问题为:设编号为 1,2,… n的n个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
提示
用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除算法结束。

解题示意图:

image.png
image.png
image.png
image.png
image.png

代码实现

  1. // 约瑟夫问题 (丢手绢), 单向环形链表
  2. package main
  3. import "fmt"
  4. type Boy struct {
  5. No int
  6. Next *Boy
  7. }
  8. // 编写函数 构成 单向环形链表
  9. // num 表示小孩的个数
  10. // *Boy 返回该
  11. func AddBoy(num int) *Boy {
  12. first := &Boy{}
  13. curBoy := &Boy{}
  14. if num < 1 {
  15. fmt.Println("num的值不对")
  16. return first
  17. }
  18. for i := 1; i <= num; i++ {
  19. // 生成 boy
  20. boy := &Boy{
  21. No: i,
  22. }
  23. if i == 1 {
  24. first = boy
  25. curBoy = boy
  26. curBoy.Next = first
  27. } else {
  28. curBoy.Next = boy
  29. curBoy = boy
  30. curBoy.Next = first
  31. }
  32. }
  33. return first
  34. }
  35. // 显示单向环形链表
  36. func show(first *Boy) {
  37. if first.Next == nil {
  38. fmt.Println("空链表")
  39. return
  40. }
  41. curBoy := first
  42. for {
  43. fmt.Printf("[编号: %v] => ", curBoy.No)
  44. if curBoy.Next == first {
  45. break
  46. }
  47. curBoy = curBoy.Next
  48. }
  49. fmt.Println()
  50. }
  51. // 游戏开始了, 结束后 first 重新赋值, 状态归位(否则会引发问题), 以便后续操作
  52. func PlayGame(first *Boy, startNo int, countNum int) *Boy {
  53. if first.Next == nil {
  54. fmt.Println("空链表")
  55. return first
  56. }
  57. // 定义辅助指针, 指向最后一个节点
  58. tail := first
  59. for {
  60. if tail.Next == first {
  61. break
  62. }
  63. tail = tail.Next
  64. }
  65. // 定位到初始节点 赋给first; tail 跟着移动到 first 后面
  66. for i := 1; i <= startNo-1; i++ {
  67. first = first.Next
  68. tail = tail.Next
  69. }
  70. // for循环处理方式
  71. // for {
  72. // // 找到要删除的节点,
  73. // for i := 1; i <= countNum-1; i++ {
  74. // first = first.Next
  75. // tail = tail.Next
  76. // }
  77. // // 执行删除
  78. // fmt.Printf("编号: %v 出列\n", first.No)
  79. // first = first.Next
  80. // tail.Next = first
  81. // // 可以编写回调函数
  82. // if first == tail {
  83. // break
  84. // }
  85. // }
  86. // 以下是递归调用的方式
  87. first = delNode(first, tail, countNum)
  88. fmt.Printf("编号: %v 是最后一个\n", first.No)
  89. return first
  90. }
  91. // 递归调用
  92. func delNode(first *Boy, tail *Boy, countNum int) *Boy {
  93. // 1. 找到要删除的节点,
  94. for i := 1; i <= countNum-1; i++ {
  95. first = first.Next
  96. tail = tail.Next
  97. }
  98. // 2. 执行删除
  99. fmt.Printf("编号: %v 出列\n", first.No)
  100. first = first.Next
  101. tail.Next = first
  102. // 向临界条件逼近 first.Next == first, 或者 first == tail
  103. if first.Next == first {
  104. // 表示已经剩最后一个了,
  105. return first
  106. }
  107. boy := delNode(first, tail, countNum)
  108. // 返回最后剩下的
  109. return boy
  110. }
  111. func main() {
  112. first := AddBoy(5)
  113. show(first)
  114. fmt.Println("开始...")
  115. first = PlayGame(first, 2, 3)
  116. fmt.Println("------ 结束了, 显示剩余的:------")
  117. show(first)
  118. }
  119. /*
  120. [编号: 1] => [编号: 2] => [编号: 3] => [编号: 4] => [编号: 5] =>
  121. 开始...
  122. 编号: 4 出列
  123. 编号: 2 出列
  124. 编号: 1 出列
  125. 编号: 3 出列
  126. 编号: 5 是最后一个
  127. ------ 结束了, 显示剩余的:------
  128. [编号: 5] =>
  129. */