著名的法国数学家加斯帕•蒙日,曾在《数学的游戏问题》中讲了一个故事:有15个教徒和15个非教徒在海上遇险,必须将一半的人投入海中,其他的人才能幸免于难,于是想了一个办法,30个人围成一个圆圈,从第一个人开始依次报数,每数到9的人,就被扔入大海,之后从被扔入海中的下一个开始数,依次循环,直到剩余15人,问 怎么排位置,才能让扔入大海的人全为非教徒。
这就是著名的约瑟夫问题。
这么一想,一圈人依次报数,有点和循环链表类似,而且还是单向的。
所以我们可以考虑采用一个单向的循环链表来解决这个问题。因为涉及到元素的移除,我们可以设置一个前驱结点,来减少链表的遍历。
private int count;
public int Count
{
get
{
return count;
}
}
//尾节点
private Node tailNode;
//当前结点的前驱结点
private Node currentPrev;
//当前结点的值
public object Current
{
get
{
return currentPrev.next.item;
}
}
添加元素首先:
public void Add(object value)
{
Node newNode = new Node(value);
if (tailNode==null)
{
tailNode = newNode;
tailNode.next = newNode;
currentPrev = newNode;
}
else
{
newNode.next = tailNode.next;
tailNode.next = newNode;
//如果只有一个元素的时候
if (currentPrev==tailNode)
{
currentPrev = newNode;
}
tailNode = newNode;
}
count++;
}
移除当前元素:
public void RemoveCurrentNode()
{
//空表
if (tailNode == null)
{
throw new NullReferenceException("Current List Null");
}
else if (count == 1)
{
tailNode = null;
currentPrev = null;
}
else
{
if (currentPrev.next==tailNode)
{
//删除的是尾结点
tailNode = currentPrev;
}
currentPrev.next = currentPrev.next.next;
}
count--;
}
一定要注意指针指向的设置顺序。
数数的方法:
public void Move(int step)
{
if (step < 0)
{
throw new ArgumentOutOfRangeException("step", "不能小于0");
}
if (tailNode==null)
{
throw new NullReferenceException("list is null");
}
//移动指针
for (int i = 0; i < step; i++)
{
currentPrev = currentPrev.next;
}
}
输出当前集合中的元素:
public string ConsoleContent()
{
if (tailNode==null)
{
return "list is null";
}
string str = "";
Node temp = tailNode.next;
for (int i = 0; i < count; i++)
{
str += temp.item.ToString() + " ";
temp = temp.next;
}
return str;
}
接下来来测试一下,假如有10个人,每当数到6的人扔进海中,那么我们来看一下最后的顺序:
SingleCircleLinkList list = new SingleCircleLinkList();
string ss = string.Empty;
Console.WriteLine("请输入总的人数");
int count = int.Parse(Console.ReadLine());
//假设当数到M的时候,出队
Console.WriteLine("请输入M的值");
int num = int.Parse(Console.ReadLine());
Console.WriteLine("现在开始");
for (int i = 1; i <= count; i++)
{
list.Add(i);
}
Console.WriteLine("一共有:" + list.ConsoleContent());
while (list.Count > 1)
{
list.Move(num - 1);
//记录出去的顺序
ss += list.Current.ToString() + " ";
list.RemoveCurrentNode();
Console.Write("\r\n剩余的人:"+list.ConsoleContent());
Console.Write(" 开始报数的是:"+list.Current);
}
Console.WriteLine("\n\r 顺序为:" + ss + list.Current);
Console.ReadKey();
最后的顺序为 6 2 9 7 5 8 1 10 4 3