蓝桥杯练习题 <约瑟夫环> 循环链表

题目描述

设有 n 个人围坐在圆桌周围,现从某个位置 k 上的人开始报数,报数到 mm的人就站出来。下一个人,即原来的第 m+1个位置上的人,又从 1 开始报数,再报数到 m 的人站出来。依次重复下去,直到全部的人都站出来为止。试设计一个程序求出这 n 个人的出列顺序。

要求一:采用循环链表解决。

要求二:可以使用模拟法,模拟循环链表。

要求三:可以不使用循环链表类的定义使用方式。

输入描述

输入只有一行且为用空格隔开的三个正整数 n,k,m,其含义如上所述。

输出描述

共 n行,表示这 n 个人的出列顺序。

输入输出样例

输入

3 5 8

输出

3
2
1

这道题的话用c写一个双链表实现就可以,思路是几个循环,第一个循环找到初始报数的位置,第二个循环里进行报数,出人,结束条件是链表中只剩下最后一个元素,最后循环结束再将最后一个元素输出,注意控制好循环的量即可。

#include <iostream>
using namespace std;

struct Node
{
    int data;
    Node *pNext;
};

int main()
{
    int n, k, m, i;    //n个人从k位置开始报数,数到m出列
     Node *p, *q, *head;
    cin >> n >> k >> m;

    //初始化链表
    Node * first = (Node *)new Node;
    p = first;
    first->data = 1;
    for (i = 2; i <= n; i++)
    {
        q = new Node;
        q->data = i;
        p->pNext = q;
        p = p->pNext;
    }
    p->pNext = first;    
    p = first;            //形成循环链表
    for (i = 1; i <= k - 1; i++)     //找到第k个位置的那个人,即初始位
        p = p->pNext;
    while (p != p->pNext)   //只剩下一个结点的时候停止(剩下一个结点时头尾一致)
    {
        for (i = 1; i < m - 1; i++)    //开始报数,循环结束时 p为第m-1个
        {
            p = p->pNext;
        }    
                                      //所以q为第m个,出列      
        q = p->pNext; //q为要出队的元素
        cout << q->data << endl;
        p->pNext = q->pNext;         
        delete q;
        p = p->pNext;                //从第m+1个开始重新报数                  
    }
    cout << p->data << endl;        //循环结束,只剩最后一个元素,输出
    return 0;
}

起初我想用list容器去写,也很方便,但是思路没有这么简单明了,下面贴出我用list容器实现的算法,需要注意的点是利用迭代器在遍历的时候,要注意界限,包括最后那里要记得多用一个迭代器来判断下位置是否是最后一个,否则会乱了。

#include <iostream>
using namespace std;
#include <list>

list<int> l1;

int main()
{
 int n,k,m;
 int i=1;
 cin >> n >> k >> m;
//循环链表初始化 
 for(i=1;i<=n;i++)
 {
   l1.push_back(i);
 }
 //找到起始位置k的那个人
 list<int>::iterator it=l1.begin();
 for(int j=1;j<k;j++)
 {  
   it++;
   if(it==l1.end())
   {
     it=l1.begin();
   }
 }
 //开始报数  用两个循环  外层循环为总共链表中的个数,内层循环为每一次报数淘汰过程
 for(int l=1;l<n;l++)
 {
    for(int j=1;j<m;j++)
    {
      it++;
      if(it==l1.end())
      {
        it=l1.begin();
      }    
    }
    cout<< (*it)<<endl;
    //多加一个判断,防止这个数是在末尾 
    list<int>::iterator it_1 = it;
    it_1++;
    if(it_1==l1.end())
	 {
	 	l1.erase(it);
	 	it=l1.begin();
    }else{
    	   it=l1.erase(it);      //erase返回值是删除位置的下一个位置,刚好就是从这里继续开始报数,不过要多加一个判断,万一这个位置在末尾 
 			}
}
 
  cout<< l1.back() <<endl;
 
  // 请在此输入您的代码
  return 0;
}

好像遍历还可以用其他的函数来实现,但是自己还不是非常熟悉,待我去网上再学习一番,有后续再更更,也欢迎大家评论指点

猜你喜欢

转载自blog.csdn.net/mc10141222/article/details/123683738