C#线性表约瑟夫环(Joseph Ring)

约瑟夫环

已知N个人围成一个圆圈,每隔M个元素,就清除该元素,剩下的继续:每隔M个元素,就清除该元素,直到元素只有(M-1)个为止。

n个人排成一圈。从某个人开始,按顺时针方向依次编号。
从编号为1的人开始顺时针“一二一”报数,报到2的人退出圈子。这样不断循环下去,圈子里的人将不断减少。由于人的个数是有限的,因此最终会剩下一个人。

试问最后剩下的人最开始的编号。

约瑟夫环是一个循环列表问题。我们用三种线性数据结构来分别实现,同时打印算法用时。

第一种:移除指定位置的元素,每隔N个元素就清除,使用List<T>的RemoveAt(index)

第二种:使用T[] array,bool[] flagArray,第一个数组代表集合数据,第二个数组代表是否已删除标记

第三种:我们使用元组列表来实现Tuple<T,bool> 第一项代表集合数据,第二项代表是否已删除标记

新建.net 5.0控制台应用程序JosephRingDemo,输入测试程序如下:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;

namespace JosephRingDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            int divide = 3;

            //列表
            List<string> stringlist = new List<string>() 
            { 
                "大魔法师", "山丘之王", "圣骑士", "血魔法师",
                "剑圣", "先知", "牛头人酋长", "暗影猎手",
                "死亡骑士", "巫妖", "地穴领主", "恐惧魔王",
                "恶魔猎手", "丛林守护者", "守望者", "月之女祭司",
                "驯兽师", "黑暗游侠", "深渊魔王", "娜迦女海巫",
                "火焰领主", "炼金术士", "地精修补匠", "熊猫酒仙",
                "AA","BB","CC","DD","EE","FF","GG","HH","II","JJ","KK","LL","MM",
                "NN","OO","PP","QQ","RR","SS","TT","UU","VV","WW","XX","YY","ZZ"
            };

            //数组
            string[] array = stringlist.ToArray();
            bool[] flagArray = new bool[array.Length];

            //元组的第一项代表姓名[string],第二项代表是否已删除标志[bool](默认都未删除)
            List<Tuple<string, bool>> tupleList = new List<Tuple<string, bool>>();
            for (int i = 0; i < stringlist.Count; i++)
            {
                tupleList.Add(Tuple.Create(stringlist[i], false));
            }

            JosephRing(stringlist, divide);
            Console.WriteLine($"【使用List<string>集合】当前集合剩余元素【{string.Join(",", stringlist)}】");

            JosephRing(array, flagArray, divide);
            List<string> tempList = new List<string>();
            for (int i = 0; i < array.Length; i++)
            {
                if (!flagArray[i]) 
                {
                    tempList.Add(array[i]);
                }
            }
            Console.WriteLine($"【使用string[]和bool[]集合】当前集合剩余元素【{string.Join(",", tempList)}】");

            JosephRing(tupleList, divide);
            Console.WriteLine($"【使用List<Tuple<string, bool>>集合】当前集合剩余元素【{string.Join(",", tupleList.FindAll(tuple => !tuple.Item2).Select(tuple => tuple.Item1))}】");
            Console.ReadLine();
        }

        /// <summary>
        /// 约瑟夫环一:【使用列表集合】每隔divide个元素就从集合中删除
        /// </summary>
        /// <param name="list">集合</param>
        /// <param name="divide">分割计数,每隔几个移除</param>
        static void JosephRing(List<string> list, int divide)
        {
            if (list == null) 
            {
                throw new ArgumentNullException("集合list不能为空");
            }
            if (divide <= 0) 
            {
                throw new ArgumentOutOfRangeException("分割计数必须为正整数");
            }
            Contract.EndContractBlock();
            Stopwatch stopwatch = Stopwatch.StartNew();
            int cnt = 0;
            //记录移除的元素集合
            List<string> removeList = new List<string>();
            //如果集合的个数 大于 (分割数-1)
            while (list.Count > divide - 1)
            {
                for (int index = 0; index < list.Count; index++)
                {
                    cnt++;
                    if (cnt >= divide)
                    {
                        //Console.WriteLine($"【{list[index]}】被移除...");
                        removeList.Add(list[index]);
                        list.RemoveAt(index);
                        index--;
                        cnt = 0;
                    }
                }
            }
            Console.WriteLine($"---约瑟夫环一:依次移除的元素集合【{string.Join(",", removeList)}】");
            Console.WriteLine($"---约瑟夫环一:【使用列表集合】,算法用时【{stopwatch.Elapsed.TotalMilliseconds}】ms");
        }

        /// <summary>
        /// 约瑟夫环二:【使用数组】隔divide个元素就将元素状态设置为已删除
        /// </summary>
        /// <param name="array">姓名数组,必须与是否删除标记数组flagArray长度一致</param>
        /// <param name="flagArray">标记数组,必须与姓名数组array长度一致</param>
        /// <param name="divide"></param>
        static void JosephRing(string[] array, bool[] flagArray, int divide) 
        {
            if (array == null)
            {
                throw new ArgumentNullException("数组array不能为空");
            }
            if (flagArray == null)
            {
                throw new ArgumentNullException("数组flagArray不能为空");
            }
            if (array.Length != flagArray.Length) 
            {
                throw new ArgumentOutOfRangeException($"对应数组的元素个数不一致:【{array.Length}】vs【{flagArray.Length}】");
            }
            if (divide <= 0)
            {
                throw new ArgumentOutOfRangeException("分割计数必须为正整数");
            }
            Contract.EndContractBlock();
            Stopwatch stopwatch = Stopwatch.StartNew();
            int cnt = 0;
            //记录移除的元素集合
            List<string> removeList = new List<string>();
            //如果存在未删除状态的集合个数 大于 (分割数-1)
            while (flagArray.ToList().FindAll(element => !element).Count > divide - 1)
            {
                for (int index = 0; index < array.Length; index++)
                {
                    //如果某个元素没有被删除
                    if (!flagArray[index])
                    {
                        cnt++;
                    }
                    if (cnt >= divide)
                    {
                        //Console.WriteLine($"【{array[index]}】被标记为已删除状态...");
                        removeList.Add(array[index]);
                        flagArray[index] = true;
                        cnt = 0;
                    }
                }
            }
            Console.WriteLine($"---约瑟夫环二:依次移除的元素集合【{string.Join(",", removeList)}】");
            Console.WriteLine($"---约瑟夫环二:【使用数组】,算法用时【{stopwatch.Elapsed.TotalMilliseconds}】ms");
        }

        /// <summary>
        /// 约瑟夫环三:【使用元组集合】每隔divide个元素就将元素状态设置为已删除
        /// </summary>
        /// <param name="tupleList"></param>
        /// <param name="divide"></param>
        static void JosephRing(List<Tuple<string, bool>> tupleList, int divide)
        {
            if (tupleList == null)
            {
                throw new ArgumentNullException("集合tupleList不能为空");
            }
            if (divide <= 0)
            {
                throw new ArgumentOutOfRangeException("分割计数必须为正整数");
            }
            Contract.EndContractBlock();
            Stopwatch stopwatch = Stopwatch.StartNew();
            int cnt = 0;
            //记录移除的元素集合
            List<string> removeList = new List<string>();
            //如果存在未删除状态的集合个数 大于 (分割数-1)
            while (tupleList.FindAll(tuple => !tuple.Item2).Count > divide - 1)
            {
                for (int index = 0; index < tupleList.Count; index++)
                {
                    //如果某个元素没有被删除
                    if (!tupleList[index].Item2)
                    {
                        cnt++;
                    }
                    if (cnt >= divide)
                    {
                        //Console.WriteLine($"【{tupleList[index].Item1}】被标记为已删除状态...");
                        removeList.Add(tupleList[index].Item1);
                        //注意 不能使用 tupleList[index].Item2 = true; 元组的第几项是只读的
                        tupleList[index] = new Tuple<string, bool>(tupleList[index].Item1, true);
                        cnt = 0;
                    }
                }
            }
            Console.WriteLine($"---约瑟夫环三:依次移除的元素集合【{string.Join(",", removeList)}】");
            Console.WriteLine($"---约瑟夫环三:【使用元组集合】,算法用时【{stopwatch.Elapsed.TotalMilliseconds}】ms");
        }
    }
}

运行结果如图:

算法性能:元组----->数组----->列表

          

猜你喜欢

转载自blog.csdn.net/ylq1045/article/details/112797430