C#数据结构与算法系列(七):约瑟夫问题(Josephu)

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);
        }
    }

7.效果演示图

原文地址:https://www.cnblogs.com/vic-tory/p/13152400.html