8,约瑟夫斯(Josephus)问题
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。
然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
实现思路:
1)利用环形链表模拟,出列表示自杀。
2)First指正移到开始数数位置,辅助指针helpPointer移到First前一个位置,也就三环形链表末尾 helpPointer.Next=First
3)开始数数,First = First.Next helpPointer = helpPointer.Next; 两个指针一起移动,First指向哪个节点出列
4)First == First.Next 表示已有一个元素
C#代码实现:
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 5 namespace 数据结构 6 { 7 public class CircleLinked 8 { 9 //创建头指针 10 private Node First { get; set; } 11 //创建环形链表 12 public void Add(int nums) 13 { 14 if (nums < 1) 15 { 16 Console.WriteLine("至少需要一个节点"); 17 return; 18 } 19 //创建辅助指针 20 Node curNode = null; 21 for (int n = 1; n <= nums; n++) 22 { 23 Node node = new Node(n); 24 if (n == 1) 25 { 26 //First和curNode指针指向第一节点 27 First = node; 28 curNode = First; 29 //指向自己形成环路 30 curNode.Next = First; 31 continue; 32 } 33 //上一节点断开环路,Next指向下一节点 34 curNode.Next = node; 35 //curNode指针移指向下一节点 36 curNode = node; 37 //指向第一个节点,形成环路 38 curNode.Next = First; 39 } 40 } 41 42 //打印环形链表 43 public void Scan() 44 { 45 if (First == null) 46 { 47 Console.WriteLine("空链表"); 48 } 49 //创建辅助指针 50 var curNode = First; 51 while (true) 52 { 53 Console.Write($"{curNode.No}\t"); 54 //如果Next地址三First说明已经到最后一个 55 if (curNode.Next == First) 56 { 57 break; 58 } 59 curNode = curNode.Next; 60 } 61 } 62 63 /// <summary> 64 /// 65 /// </summary> 66 /// <param name="startNo">开始位置</param> 67 /// <param name="countNums">数多少次</param> 68 /// <param name="nums">数多少次</param> 69 public void Josephus(int startNo, int countNums, int nums) 70 { 71 if (First == null || startNo < 1 || startNo > nums) 72 { 73 Console.WriteLine("参数错误"); 74 return; 75 } 76 //只有一个节点,直接输出 77 if (First.Next == First) 78 { 79 Console.WriteLine($"{First.No}"); 80 return; 81 } 82 //辅助指针 83 Node helpPointer = First; 84 //First移到开始位置 85 while (true) 86 { 87 if (First.No == startNo) 88 { 89 break; 90 } 91 First = First.Next; 92 } 93 //辅助helpPointer指针移动到链表末尾 94 while (true) 95 { 96 if (helpPointer.Next == First) 97 { 98 break; 99 } 100 helpPointer = helpPointer.Next; 101 } 102 //出列 103 bool flag = true; 104 while (flag) 105 { 106 //只剩下一个节点 107 if (First.Next == First) 108 { 109 flag = false; 110 } 111 else 112 { 113 //数数 114 for (int n = 1; n < countNums; n++) 115 { 116 First = First.Next; 117 helpPointer = helpPointer.Next; 118 } 119 } 120 //First指向的节点出列 121 Console.Write($"{First.No}\t"); 122 if (flag) 123 { 124 First = First.Next; 125 helpPointer.Next = First; 126 } 127 } 128 } 129 130 131 public class Node 132 { 133 public int No { get; set; } 134 public Node Next { get; set; } 135 public Node(int no) 136 { 137 this.No = no; 138 } 139 } 140 } 141 142 public class Josephus 143 { 144 public static void Main(string[] args) 145 { 146 var circle = new CircleLinked(); 147 Console.WriteLine("加入41个人"); 148 circle.Add(41); 149 circle.Scan(); 150 Console.WriteLine("\n自杀顺序,最后两个个幸存"); 151 circle.Josephus(1, 3, 41); 152 } 153 } 154 }