简介

Go的标准包Container中包含了常用的容器类型,包括conatiner/listcontainer/heapcontainer/ring,本篇讲解container/ring的使用。

ring包

ring包提供了环形链表的操作。它仅导出了一个类型,Ring:

// Ring表示环形链表中的元素。
type Ring struct {Value interface{} // Value类型为interface{},因此可以接受任意类型
}// 创建一个长度为n的环形链表
func New(n int) *Ring
// 针对环形链表中的每一个元素x进行f(x)操作
func (r *Ring) Do(f func(interface{}))
// 获取环形链表长度
func (r *Ring) Len() int
// 如果r和s在同一环形链表中,则删除r和s之间的元素,
// 被删除的元素组成一个新的环形链表,返回值为该环形链表的指针(即删除前,r->Next()表示的元素)
// 如果r和s不在同一个环形链表中,则将s插入到r后面,返回值为
// 插入s后,s最后一个元素的下一个元素(即插入前,r->Next()表示的元素)
func (r *Ring) Link(s *Ring) *Ring
// 移动 n % r.Len() 个位置,n正负均可
func (r *Ring) Move(n int) *Ring
// 返回下一个元素
func (r *Ring) Next() *Ring
// 返回前一个元素
func (r *Ring) Prev() *Ring
// 删除r后面的 n % r.Len() 个元素
func (r *Ring) Unlink(n int) *Ring

示例

Ring的用法

package mainimport ("container/ring""fmt"
)func main() {const rLen = 3// 创建新的Ringr := ring.New(rLen)for i := 0; i < rLen; i++ {r.Value = ir = r.Next()}fmt.Printf("Length of ring: %d\n", r.Len()) // Length of ring: 3// 该匿名函数用来打印Ring中的数据printRing := func(v interface{}) {fmt.Print(v, " ")}r.Do(printRing) // 0 1 2fmt.Println()// 将r之后的第二个元素的值乘以2r.Move(2).Value = r.Move(2).Value.(int) * 2r.Do(printRing) // 0 1 4fmt.Println()// 删除 r 与 r+2 之间的元素,即删除 r+1// 返回删除的元素组成的Ring的指针result := r.Link(r.Move(2))r.Do(printRing) // 0 4fmt.Println()result.Do(printRing) // 1fmt.Println()another := ring.New(rLen)another.Value = 7another.Next().Value = 8 // 给 another + 1 表示的元素赋值,即第二个元素another.Prev().Value = 9 // 给 another - 1 表示的元素赋值,即第三个元素another.Do(printRing) // 7 8 9fmt.Println()// 插入another到r后面,返回插入前r的下一个元素result = r.Link(another)r.Do(printRing) // 0 7 8 9 4fmt.Println()result.Do(printRing) // 4 0 7 8 9fmt.Println()// 删除r之后的三个元素,返回被删除元素组成的Ring的指针result = r.Unlink(3)r.Do(printRing) // 0 4fmt.Println()result.Do(printRing) // 7 8 9fmt.Println()
}

模拟约瑟夫问题

环形列表可以模拟约瑟夫问题。约瑟夫问题描述如下:

来自百度:
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

用代码模拟如下:

package mainimport ("container/ring""fmt"
)type Player struct {position int  // 位置alive    bool // 是否存活
}func main() {const (playerCount = 41  // 玩家人数startPos    = 1  // 开始报数位置)deadline := 3r := ring.New(playerCount)// 设置所有玩家初始值for i := 1; i <= playerCount; i++ {r.Value = &Player{i, true}r = r.Next()}// 如果开始报数的位置不为1,则设置开始位置if startPos > 1 {r = r.Move(startPos - 1)}counter := 1  // 报数从1开始,因为下面的循环从第二个开始计算deadCount := 0  // 死亡人数,初始值为0for deadCount < playerCount {  // 直到所有人都死亡,否则循环一直执行r = r.Next() // 跳到下一个人// 如果是活着的人,则报数if r.Value.(*Player).alive {counter++}// 如果报数为deadline,则此人淘汰出局if counter == deadline {r.Value.(*Player).alive = falsefmt.Printf("Player %d died!\n", r.Value.(*Player).position)deadCount++counter = 0  // 报数置成0}}
}

输出如下,可以看到16和31是最后两个出队列的,因此Josephus将他的朋友与自己安排在第16个与第31个位置是安全的。

Player 3 died!
Player 6 died!
Player 9 died!
Player 12 died!
Player 15 died!
Player 18 died!
Player 21 died!
Player 24 died!
Player 27 died!
Player 30 died!
Player 33 died!
Player 36 died!
Player 39 died!
Player 1 died!
Player 5 died!
Player 10 died!
Player 14 died!
Player 19 died!
Player 23 died!
Player 28 died!
Player 32 died!
Player 37 died!
Player 41 died!
Player 7 died!
Player 13 died!
Player 20 died!
Player 26 died!
Player 34 died!
Player 40 died!
Player 8 died!
Player 17 died!
Player 29 died!
Player 38 died!
Player 11 died!
Player 25 died!
Player 2 died!
Player 22 died!
Player 4 died!
Player 35 died!
Player 16 died!
Player 31 died!

Go标准容器之Ring相关推荐

  1. Docker容器之macvlan网络

    Docker容器之macvlan网络 一.查看当前dokcer的版本 二.创建macvlan网络 三.查看现有网络类型 四.运行macvlan网络类型的容器 一.查看当前dokcer的版本 [root ...

  2. Java并发编程:并发容器之CopyOnWriteArrayList(转载)

    Java并发编程:并发容器之CopyOnWriteArrayList(转载) 原文链接: http://ifeve.com/java-copy-on-write/ Copy-On-Write简称COW ...

  3. [知识点]C++中STL容器之set

    零.STL目录 1.容器之map 2.容器之vector 3.容器之set 一.前言 继上期的vector之后,我们又迎来了另一个类数组的STL容器--set. 二.用途与特性 set,顾名思义,集合 ...

  4. Docker容器之compose容器集群的快速编排

    Docker容器之compose容器集群的快速编排 前言 一.Docker-compose简介 二.YAML文件格式及编写注意事项 (1)YAML文件格式 (2)YAML格式的注意事项 (3)YAML ...

  5. Docker容器之harbor私有仓库部署与管理

    Docker容器之harbor私有仓库部署与管理 前言 一.Harbor概述 二.Harbor的特性 三.Harbor的构成 四.Harbor私有仓库搭建 (1)安装docker-compose (2 ...

  6. STL源码剖析(十三)关联式容器之rb_tree

    STL源码剖析(十三)关联式容器之rb_tree 文章目录 STL源码剖析(十三)关联式容器之rb_tree 一.rb_tree的数据结构 二.rb_tree的迭代器 三.rb_tree的操作 3.1 ...

  7. 【转】Java并发编程:并发容器之ConcurrentHashMap

    JDK5中添加了新的concurrent包,相对同步容器而言,并发容器通过一些机制改进了并发性能.因为同步容器将所有对容器状态的访问都串行化了,这样保证了线程的安全性,所以这种方法的代价就是严重降低了 ...

  8. 企业运维容器之 docker仓库

    企业运维容器之 docker 仓库 1. 什么是仓库? 2. Docker hub 3. Registry 工作原理 4. 配置镜像加速器 5. 搭建私有仓库 5. 总结 1. 什么是仓库? Dock ...

  9. PyQt5最全60 容器之QMdiArea和QMdiSubWindow容纳多文档的窗口

    PyQt5最全60 容器之QMdiArea和QMdiSubWindow容纳多文档的窗口 from PyQt5.QtWidgets import * import sysclass MultiWindo ...

  10. 【C++提高编程笔记】三.(一).STL常用容器之string容器

    文章目录 1.string基本概念 2.string构造函数 3.string赋值操作 4.string字符串拼接 5.string查找和替换 6.string字符串比较 7.string字符存取 8 ...

最新文章

  1. SLF4J: Failed to load class org.slf4j.impl.StaticLoggerBinder.
  2. 徐科:做IC不外乎PPA,但需要成百上千的专家合作 投入数千万
  3. linux下通用Makefile写法
  4. 线性代数应该这样讲(二)
  5. 如何实现Android平台GB28181前端设备接入
  6. Gaze Estimation笔记——data normalization
  7. 信息学奥赛C++语言:乘车费用
  8. mybatis中prefix,suffix,prefixOverrides,suffixOverrides用法解释
  9. TP5.x——update更新成功但是返回是0
  10. Catia抛物线建模_基于CATIA的方程曲线设计建模研究
  11. 已下载好的OpenCV4.5.5 CMake .cache文件夹
  12. 计算机键盘没有fn,键盘上没有fn键怎么办
  13. 企业邮箱注册—企业微信邮箱2.0时代正式来临!
  14. 华为「天才少年」自制硬萌机器人,开源5小时,GitHub收获317星!
  15. 注销在html中怎么,关于注销页面
  16. 智能秤方案设计——蓝牙体脂秤PCBA软硬件端功能说明
  17. 微信支付开发 认清微信支付v2和v3
  18. Python的全局安装源配置
  19. matlab数学建模-神经网络感知器函数
  20. 信息安全数学基础——模重复平方计算法(两种方法实现C+JAVA)

热门文章

  1. js、css基础总结
  2. 求一个图中的最大团(全连通分量) n = 40 (中途相遇法)
  3. android 照片 文件夹在哪里,Android手机照片文件夹在哪里?删除的手机照片怎么恢复...
  4. 疯狂springboot终极讲义笔记(一)
  5. 修改chrome默认背景颜色为浅绿色
  6. jpa Specification in 用法
  7. java栅栏_Java并发工具类(栅栏CyclicBarrier)
  8. java 腾讯微博模拟登陆_腾讯微博模拟登录
  9. “AI复活了我的妻子,但我决定跟她说再见了”
  10. 什么是二进制,为什么要用二进制