1.介绍
Josephu问题为:设编号为1、2、...n的n个人围坐在一圈,约定编号为k(1<=k<=n) 的人从1开始报数,
数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人都出列为止,由此产生出一个出列编号的序列。
2.提示
用一个不带头节点的循环链表来处理Josephu问题:先构成一个有n节点的单向循环链表,
然后由k节点起从1开始计数,计到m时,对应节点从链表中删除,然后再重被删除节点的下一个节点又从1开始计数,直到最后一个节点从链表中删除算法结束
3.示意图
4.环形链表示意图
5.思路分析图
6.代码实现
public class Boy { public Boy(int id) { this.Id = id; } public int Id { get; set; } public Boy Next { get; set; } }
public class CircleSingleLinkList { private Boy _first = null; public void Add(int nums) { if (nums < 1) Console.WriteLine("传入的值必须大于1"); else { Boy curBoy = null; for (int i = 1; i <= nums; i++) { Boy boy = new Boy(i); if (i == 1) { _first = boy; _first.Next = _first; //构成环形 curBoy = _first; //让当前boy等于第一个小孩 } else { curBoy.Next = boy; //指向当前小孩的下一个 boy.Next = _first; //待添加的小孩的下一个节点指向第一个 curBoy = boy;//再把当前小孩指向待添加节点 } } } } /// <summary> /// 根据用户的输入,计算出小孩出圈的顺序 /// </summary> /// <param name="startId">表示从第几个小孩开始数数</param> /// <param name="countNum">表示数几下</param> /// <param name="nums">表示最初由几个小孩在圈中</param> public void CountBoy(int startId,int countNum,int nums) { if (startId < 1 || startId > nums || _first == null) { Console.WriteLine("参数错误请重新输入"); return; } Boy helper = _first; //创建一个辅助节点 //将辅助节点指向最后一个节点 while (true) { if (helper.Next == _first) break; //当辅助节点的下一个节点等于第一个节点 说明是最后一个节点 helper = helper.Next; } //将开始节点和辅助节点指向开始数数的位置和结尾 由于索引是从0开始所以是startId-1 for (int j = 0; j < startId-1 ; j++) { _first = _first.Next; helper = helper.Next; } //当小孩报数时,让first和helper同时移动 countNum-1次,然后出圈 while (true) { if (helper == _first) break; //当尾结点等于头节点时 说明只有一个节点 for (int i = 0; i < countNum-1; i++) { _first = _first.Next; helper = helper.Next; } //first就代表出圈的小孩 Console.WriteLine($"出圈的小孩:{_first.Id}"); _first = _first.Next; //将first指向小孩节点出圈 helper.Next = _first; } Console.WriteLine("最后留下的小孩:"+_first.Id); }
public class Josephu { public static void Test() { CircleSingleLinkList singleLinkList = new CircleSingleLinkList(); singleLinkList.Add(5); singleLinkList.ShowBoy(); singleLinkList.CountBoy(1, 2, 5); } }