zoukankan      html  css  js  c++  java
  • 第六节:手撸“循环链表”及约瑟夫问题的解决

    1. 约瑟夫问题

      加斯帕·蒙日是法国数学家,他在《数学的游戏问题》中讲了一个故事:15个教徒和15个非教徒在深海上遇险,必须将一半的人投入海中,其余人才能幸免于难,于是想了一个方法,30个人 围城一圈,从第一个人开始依次报数,每数到第九个的人就将他扔向大海,如此循环进行直到仅剩余15个人为止。问怎样的排法,才能使每次投入大海的都是非教徒?这就是著名的约瑟夫问题,又称约瑟夫环。 

      我们将问题进行一下抽象:N个人进行手尾排列,从第一个开始数数,数到第X个停止,然后把第X个剔除;然后接着数,数到第X个停止,并将其移除,以此类推,直到达到要剔除的个数数目,把这些剔除位置的记录下来,就是我们所需要的位置。很显然,循环链表非常适合解决这个问题。

    2. 解决思路

    (1). 先抽象出一个单向的循环链表,要有尾节点,尾节点指向头节点;可以用当前节点的前驱节点来表示当前节点。

     代码如下:

     节点

     1     /// <summary>
     2     /// 循环链表节点
     3     /// </summary>
     4     public class CircleNode<T>
     5     {
     6         /// <summary>
     7         /// 存放数据
     8         /// </summary>
     9         public T Data { get; set; }
    10 
    11         /// <summary>
    12         /// 下一个节点
    13         /// </summary>
    14         public CircleNode<T> Next { get; set; }
    15 
    16         public CircleNode()
    17         {
    18                 
    19         }
    20 
    21         public CircleNode(T data)
    22         {
    23             Data = data;
    24         }
    25     }
    CircleNode

    循环链表

      1  /// <summary>
      2     /// 手写单链表
      3     /// </summary>
      4     public class MySingleLinkedList<T>
      5     {
      6         private Node<T> _header;  //头结点
      7         private int _count;  //元素个数
      8         public MySingleLinkedList()
      9         {
     10 
     11         }
     12         public MySingleLinkedList(T data)
     13         {
     14             _header = new Node<T>(data);
     15             _count++;
     16         }
     17         //只能读
     18         public int Count
     19         {
     20             get
     21             {
     22                 return _count;
     23             }       
     24         }
     25         /// <summary>
     26         /// 根据索引获取元素
     27         /// </summary>
     28         /// <param name="index"></param>
     29         /// <returns></returns>
     30         public Node<T> GetNodeByIndex(int index)
     31         {
     32             if (index < 0 || index>=_count)
     33             {
     34                 throw new ArgumentOutOfRangeException("索引越界");
     35             }
     36             Node<T> tempNode = _header;
     37             //当index为0的时候,不进入for循环
     38             for (int i = 0; i < index; i++)
     39             {
     40                 tempNode = tempNode.Next;
     41             }
     42             return tempNode;
     43         }
     44 
     45         //索引器获取和设置数据
     46         public T this[int index]
     47         {
     48             get
     49             {
     50                 return GetNodeByIndex(index).Data;
     51             }
     52             set
     53             {
     54                 GetNodeByIndex(index).Data = value;
     55             }
     56         }
     57 
     58         /// <summary>
     59         /// 添加元素(最后)
     60         /// </summary>
     61         /// <param name="data"></param>
     62         public void Add(T data)
     63         {
     64             Node<T> newNode = new Node<T>(data);
     65             if (_header==null)  //表示添加的是第一个节点
     66             {
     67                 _header = newNode;
     68             }
     69             else
     70             {
     71                 var lastNode = GetNodeByIndex(_count - 1);
     72                 lastNode.Next = newNode;
     73             }       
     74             _count++;
     75         }
     76 
     77         /// <summary>
     78         /// 插入元素
     79         /// </summary>
     80         /// <param name="index">索引</param>
     81         /// <param name="data">数据</param>
     82         public void Insert(int index,T data)
     83         {
     84             if (index < 0||index>_count)
     85             {
     86                 throw new ArgumentOutOfRangeException("索引越界");
     87             }
     88             var newNode = new Node<T>(data); //新节点
     89             if (index==0)
     90             {
     91                 //头结点为空,直接插入头结点即可
     92                 if (_header==null)
     93                 {
     94                     _header = newNode;
     95                 }
     96                 //头结点有元素,需要把头结点的位置让出来
     97                 else
     98                 {
     99                     newNode.Next = _header;
    100                     _header = newNode;
    101                 }
    102             }
    103             else
    104             {
    105                 var preNode = GetNodeByIndex(index-1);  //查找插入位置的前驱节点
    106                 var nextNode = preNode.Next;            //插入位置的后继节点
    107                 preNode.Next = newNode;        //前驱结点的后继节点为新节点
    108                 newNode.Next = nextNode;       //新节点的后继节点执行原来前驱的后继
    109             }
    110             _count++;             
    111         }
    112 
    113         /// <summary>
    114         /// 根据索引删除元素
    115         /// </summary>
    116         /// <param name="index">索引</param>
    117         public void RemoveAt(int index)
    118         {
    119             if (index < 0 || index >= _count)
    120             {
    121                 throw new ArgumentOutOfRangeException("索引越界");
    122             }
    123             if (index==0)  //删除头结点
    124             {
    125                 if (_header==null)
    126                 {
    127                     throw new ArgumentOutOfRangeException("索引越界");
    128                 }
    129                 _header = _header.Next;
    130             }
    131             else
    132             {
    133                 var preNode = GetNodeByIndex(index - 1); //删除节点的前驱节点
    134                 if (preNode.Next==null)
    135                 {
    136                     throw new ArgumentOutOfRangeException("索引越界");
    137                 }
    138                 preNode.Next = preNode.Next.Next;     //如果删除的是最后一个节点,那么它的前一个节点指向null
    139             }
    140             _count--;
    141         }
    142         /// <summary>
    143         /// 元素输出
    144         /// </summary>
    145         /// <returns></returns>
    146         public override string ToString()
    147         {
    148             string s = "";
    149             Node<T> temp = _header;
    150             while (temp != null)
    151             {
    152                 s += temp.ToString() + " ";
    153                 temp = temp.Next;
    154             }
    155             return s;
    156         }
    157 
    158     }
    MySingleLinkedList

    测试链表

     1  {
     2                 MyCircleLinkedList<int> cList = new MyCircleLinkedList<int>();
     3                 cList.Add(1);
     4                 cList.Add(2);
     5                 cList.Add(3);
     6                 cList.Add(4);
     7                 cList.Add(5);
     8                 cList.Add(6);
     9                 cList.Add(7);
    10                 Console.WriteLine($"当前节点值为:{cList.currentNodeValue}");
    11                 cList.Move(2);
    12                 Console.WriteLine($"当前节点值为:{cList.currentNodeValue}");
    13                 cList.RemoveCurrentNode();
    14                 cList.Move(3);
    15                 Console.WriteLine($"当前节点值为:{cList.currentNodeValue}");
    16                 var str1 = cList.ToString();
    17                 Console.WriteLine(str1);
    18                 Console.ReadKey();
    19             }

    运行结果:

    (2). 利用循环链表的环解决该问题, 需要记录当前节点,默认当前节点为第一个,所以它的前驱节点为尾节点,然后设计移动算法,将当前节点移动n步,然后删除当前节点,那么下一个节点就变为当前节点了,然后接着移动,直到人数符合要求为止。

     1  {
     2                 MyCircleLinkedList<int> cList = new MyCircleLinkedList<int>();
     3                 string result = string.Empty;
     4                 Console.WriteLine("请输入总人数:");
     5                 int count = int.Parse(Console.ReadLine());
     6                 Console.WriteLine("请输入出队的位置:");
     7                 int outPosition = int.Parse(Console.ReadLine());
     8                 Console.WriteLine("请输入出队的个数:");
     9                 int outPerson = int.Parse(Console.ReadLine());
    10 
    11                 Console.WriteLine("游戏开始");
    12                 for (int i = 1; i <= count; i++)
    13                 {
    14                     cList.Add(i);
    15                 }
    16                 Console.WriteLine($"所有人的人:{cList.ToString()} ");
    17                 //此处的业务用来表示剩下多少个人
    18                 while (cList.Count > (count - outPerson))
    19                 {
    20                     //移动的步数=位置数-1
    21                     cList.Move(outPosition-1);
    22                     result += cList.currentNodeValue.ToString() + " ";
    23                     cList.RemoveCurrentNode();      //删掉当前节点出队
    24                     if (cList.Count == (count - outPerson))
    25                     {
    26                         Console.WriteLine($"剩余的人为:{cList.ToString()} ");
    27                     }
    28                     else
    29                     {
    30                         Console.WriteLine($"剩余的人为:{cList.ToString()} 开始报数的人为:{cList.currentNodeValue.ToString()} ");
    31                     }
    32 
    33                 }
    34                 Console.WriteLine("出队位置的顺序: " + result);
    35             } 

    运行结果:

     

    !

    • 作       者 : Yaopengfei(姚鹏飞)
    • 博客地址 : http://www.cnblogs.com/yaopengfei/
    • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
    • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    1,300萬像素Xperia TX K.O.相機 東方日報
    信息检索Reading List
    雷军:小米二为何不用1300万像素相机_TechWeb
    1300万像素高清双核旗舰 索尼LT30p评测_手机_科技时代_新浪网
    Darts: DoubleARray Trie System海 的 声音我的搜狐
    说说底层架构之实体类的设计
    不忘本~枚举
    两种底层数据层操作时的架构方式,你喜欢那种?
    说说C#中的global
    JS对外部文件的加载及对IFRMAME的加载的实现,当加载完成后,指定指向方法(方法回调)
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/12720965.html
Copyright © 2011-2022 走看看