数据结构实验课(1):7-1 约瑟夫环

f8246673403ca250a7644f4b47e442b7.png

题目:N个人围成一圈顺序编号,从1号开始按1、2、3......顺序报数,报p者退出圈外,其余的人再从1、2、3开始报数,报p的人再退出圈外,以此类推。请按退出顺序输出每个退出人的原序号。

输入格式:

输入只有一行,包括一个整数N(1<=N<=3000)及一个整数p(1<=p<=5000)。

输出格式:

按退出顺序输出每个退出人的原序号,数据间以一个空格分隔,但行尾无空格。

输入样例:

7 3

输出样例:

3 6 2 7 5 1 4


这题在去年,我尝试使用数组进行解决最后惨得TLE,在今年,首先尝试第一种容易想到的办法,用单向循环列表进行解决。

单向循环链表解法

创建一个单向循环链表,每当遇到一个输出,将链表位置弹出,直到最后全部输出。

代码如下:

//使用单向循环链表
#include <iostream>
#include <malloc.h>
using namespace std;
typedef struct players
{
    int n;//用于记录玩家的编号
    players *next;
} players;




players *creatlist(int m)//功能是创建一个链表。并返回头结点的位置
{
    players *p1, *p2, *head;
    int i = 1;
    head = p1 = (players *)malloc(sizeof(players));
    p1->n = i++;
    m--;//考虑到m=1的特殊情况
    while (m--)
    {
        p2 = (players *)malloc(sizeof(players));
        p2->n = i++;
        p1->next = p2;
        p1 = p2;
    }
    p1->next = head;
    return head;
}




void deleteandprint(players *p, int n)//功能是删除和打印
{
    int m = 1, flag = 0;//使p2的延迟只发生一次否则会出问题
    players *tem1 = p, *tem2 = p;
    while (tem1->next != tem1)
    {
        for (m = 1; m < n; m++)//使用m为退出判定条件
        {
            tem1 = tem1->next;
            if (flag || m != n - 1)
                tem2 = tem2->next;
            else
                flag = 1;
        }
        cout << tem1->n << ' ';
        tem2->next = tem1->next;
        free(tem1);
        tem1 = tem2->next;
    }
    cout << tem1->n;//注意,由于判定条件,我们需要输出最后一个元素
}
int main()
{
    players *p;
    int m, n;
    cin >> m >> n;//录入m,n
    p = creatlist(m);//创建链表
    deleteandprint(p, n);//完成输出
    return 0;
}

顺序表解法:by 俊杰锅

算法思想:设置一个包含数组及数组长度的结构体,手动写一个动态数组,完成数组的删除元素等操作,其中,定位时采用公式:

tmp=(tmp+p-1)%La->length;

因为每次正确操作后,数组原指针实际上已经往前一步,所以用p-1,p为输入的第二位数字,表示每次退出的步长。每次使迭代器向后移动步长单位,输出后将该元素从数组中删去。


#include <stdio.h>
#include <stdlib.h>
typedef struct list
{
    int num[10000];
    int length;
} List;//结构体定义




void InitList(List *&L)
{
    L = (List *)malloc(sizeof(List));
    L->length = 0;
}//初始化链表
void CreateList(List *&L, int m)//L为指针类型,以引用的方式传入
{
    int i;
    for (i = 0; i < m; i++)
        L->num[i] = i + 1;
    L->length = m;
}//创建链表
int List_Delete(List *La, int x)
{
    int tmp = La->num[x];
    for (; x < La->length - 1; x++)
    {
        La->num[x] = La->num[x + 1];
    }
    La->length--;
    return tmp;
}




void Quit(List *La, int p)
{
    int i = 0;
    int first = 1;
    int tmp=0;
    while (La->length > 0)
    {
        tmp=(tmp+p-1)%La->length;
        if (first)
        {
            printf("%d", List_Delete(La, tmp));
            first=0;
        }
        else
        {
            printf(" %d", List_Delete(La, tmp));
        }
        
    }
}
int main()
{
    List *L;
    int n,p;
    scanf("%d %d",&n,&p);
    InitList(L);
    CreateList(L,n);
    Quit(L,p);
    return 0;
}

最简单的写法

使用STL完成顺序表操作

算法思想与顺序表类似,不过选择了已有的vector动态数组代替手写代码,大大提高效率

//使用STL解决该问题
#include <iostream>
#include <vector>
#include <iterator>
using namespace std;
vector<int> players;
int main()
{
    int m, n;
    cin >> m >> n;
    for(int i=1;i<=m;i++)
    players.push_back(i);//创建动态数组
    vector<int>::iterator it;//创建迭代器
    for(it=players.begin();players.size()!=1;)
    {
        for(int i=0;i<n-1;i++){it++;if(it==players.end())it=players.begin();}
        //it的越界判断非常重要
        cout<<*it<<' ';//输出
        players.erase(it);//擦去it迭代器位置,该函数会重新对齐
    }
    if(it==players.end())it=players.begin();//it的越界判断
    cout<<*it;
    return 0;
}

以上算法时间复杂度均为O(M*N)

f05bd6b0321ad16086c9ce1d4ada8f9f.png

AC!1fd4b1d4c17b08562e6d8dad227b6775.png

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目: 每个人手里有个密码。开始给定一个正数m,做为报数上限,从某个人开始循环报数,报到m的人出列;再以该出列的人手中的密码为报数上限,依次报数。打印出列的人的序号的先后顺序。 一. 需求分析 1. 本演示程序中,人数n应为任意的,首先应输入一个值赋给初始报数上限m,程序应能自动保存出列人的序号和将出列的人所持的密码赋给m,再次作为报数上限,如此循环,直至所有人都出列为止。 2. 演示程序以用户和计算机的对话方式执行,即在计算机终端上显示“提示信息”之后,由用户在键盘上输入相应数据(即每个人所持的密码),每个人的序号由程序自动分配。 3. 程序执行的命令包括: (1)构造链表;(2)输入数据;(3)执行报数,储存出列人的序号,删除出列人的信息以及把出列人的密码赋给m;(4)结束。 4. 测试数据 (1)m=20,n=7,7个人的密码依次为:3,1,7,2,4,8,4,首先m值为6,则这正确的出列顺序为6,1,4,7,2,3,5。 确的出列顺序为6,1,4,7,2,3,5。 二. 概要设计 为了实现上述操作,应以单向循环链表为存储结构。 1. 基本操作: code( ) 操作结果:构造空链表,若成功就初始化每个人的相关信息 code( ) 初始条件:线性链表存在 操作结果:释放指向出列的人的结点,并重新报数 2. 本程序包含三个模块: (1) 主程序模块; (2) 构造链表并输入每个人信息模块; (3) 释放结点模块;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值