约瑟夫问题的链表解法(4种)

这是我的备赛用具,主要是用来熟悉链表的。

约瑟夫问题也是经典的C++问题,解法非常多,如果有机会的话,会再出更合集。

老规矩先上题目再上代码。

题目:洛谷P1996

题目描述

n 个人围成一圈,从第一个人开始报数,数到 m 的人出列,再由下一个人重新从 11 开始报数,数到 m 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。

注意:本题和《深入浅出-基础篇》上例题的表述稍有不同。书上表述是给出淘汰 �−1n−1 名小朋友,而该题是全部出圈。

输入格式

输入两个整数 n,m。

输出格式

输出一行 n 个整数,按顺序输出每个出圈人的编号。

输入输出样例

输入 #1复制

10 3

输出 #1复制

3 6 9 2 7 1 8 5 10 4

说明/提示

1≤m,n≤100

题目看着挺简单,实际上也还行。

上代码。

第一位选手是教科书式写法:动态链表,但是太烦,虽然省空间,但是竞赛里一般不用。

//动态链表解法
#include<bits/stdc++.h>
using namespace std;
struct node{//链表节点
    int data;//节点的值
    node *next;//单向链表只有一个next指针
};
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    node *head,*p,*now,*prev;//定义变量
    head = new node; head->data = 1; head->next = NULL;
    //分配第一个节点,值为一,指向空
    now = head;//当前指针就是头
    for(int i=2;i<=n;i++){
        p = new node; p->data = i; p->next = NULL;
        //建立新节点
        now->next = p;//新的指针连到前面的链表上
        now = p;//尾指针后移
    }
    now->next = head;//尾指针指向头
    //建立链表完成,接下来开始实现代码
    now = head, prev = head; //从第一个开始数数
    while(n > 1){
        n--;
        for(int i=1;i<m;i++){//数到m停止
            prev = now;//记录上一个位置
            now = now -> next;
        }
        printf("%d ",now->data);//记得输出空格,跳过m
        prev->next = now->next;//prev指向now指向的地方,即m
        delete now;//释放now
        now = prev->next;//now到prev后面去
    }
    printf("%d\n",now->data);//最后输出
    delete now;//释放节点
    return 0;
}

第二位选手是结构体数组单向静态链表。

//结构体数组单向静态链表解法
#include<bits/stdc++.h>
using namespace std;
const int N = 105;//静态链表的空间大小
struct node{
    int id,nextid;//当前编号,下一个的编号
}nodes[N];
int main(){
    int n,m;
    cin>>n>>m;//输入
    nodes[0].nextid = 1;//0的下一个是1
    for(int i=1;i<=n;i++){
        nodes[i].id = i;
        nodes[i].nextid = i + 1;//建立链表
    }
    nodes[n].nextid = 1;//形成闭环
    int now = 1,prev = 1;
    while((n--) > 1){
        for(int i=1;i<m;i++){
            prev = now;//保存prev
            now = nodes[now].nextid;//now后移
        }
        cout<<nodes[now].id<<' ';//输出
        nodes[prev].nextid = nodes[now].nextid;//删除now,建立链接
        now = nodes[prev].nextid;//now重建
    }
    cout<<nodes[now].nextid;//最后一个人
    return 0;
}

第三位选手是结构体数组双向静态链表。

 

//结构体数组双向静态列表解法
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
struct node{
    int id,preid,nextid;//当前,前,后
}nodes[N];
int main(){
    int n,m;
    cin>>n>>m;
    nodes[0].nextid = 1;//这行代码是增加辨识度的(跟前面差不多)
    for(int i=1;i<=n;i++){
        nodes[i].id = i;//id
        nodes[i].preid = i - 1;//前节点
        nodes[i].nextid = i + 1;//后节点
    }
    nodes[n].nextid = 1;//n的后面一个是1
    nodes[1].preid = n;//1的前面一个是n
    int now = 1;
    while((n--) > 1){
        for(int i=1;i<m;i++){
            now = nodes[now].nextid;
        }//now后移,跳m
        cout<<now<<' ';//输出
        //下面三行有点绕,但是忍一忍,懒了没用变量
        nodes[nodes[now].preid].nextid = nodes[now].nextid;
        //now前节点与now后节点建立链接
        nodes[nodes[now].nextid].preid = nodes[now].preid;
        //now后节点与now前节点建立链接
        now = nodes[now].nextid;//now后移
        //相当于把now释放之后重新建立
    }
    cout<<nodes[now].nextid;//输出最后一个被淘汰的人的编号
    return 0;
}

最后是最简单但是使用环境最有限的一维数组单向静态链表解法。

//普通一维数组单向静态链表解法,里面最简单的,但是使用环境有限
//i就是当前节点的值,nodes[i]是下一个节点
#include<bits/stdc++.h>
using namespace std;
const int N = 105;//常量
int nodes[N];//节点数组
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<n;i++) nodes[i] = i + 1;//初始化
    nodes[n] = 1;//首尾相连
    int now = 1,prev = 1;
    while((n--) > 1){
        for(int i=1;i<m;i++){
            prev = now;
            now = nodes[now];//老规矩,prev先保存,now后移
        }
        cout<<now<<' ';
        nodes[prev] = nodes[now];//建立链接
        now = nodes[prev];//now重建
    }
    cout<<now;//最后一个被淘汰的人的编号
    return 0;
}

结束。

再见。

本文内容基本来自于《算法竞赛 上册》

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
约瑟夫问题是一个经典的算问题,假设有n个人围成一圈,编号从1到n,从第1个人开始报数,报到第m个人出列,然后从出列的下一个人开始重新报数,直到所有人都出列为止。请使用循环链表实现这个算。 首先,我们需要定义一个节点类,表示每个人的信息和位置。节点类包含两个属性,一个是编号,另一个是指向下一个节点的指针。 ```python class Node: def __init__(self, data): self.data = data self.next = None ``` 然后,我们需要构造循环链表,将所有人的信息存储在链表中。为了方便起见,我们可以先构造一个单向链表,然后将链表的尾节点指向头节点,形成一个循环链表。 ```python def construct_circle_list(n): head = Node(1) cur = head for i in range(2, n+1): new_node = Node(i) cur.next = new_node cur = new_node cur.next = head return head ``` 接下来,我们需要实现约瑟夫问题解法。我们可以使用一个计数器count,表示当前报数的人的位置,当count等于m时,将该节点从链表中删除。具体实现如下: ```python def josephus(head, m): cur = head while cur.next != cur: count = 1 while count < m-1: cur = cur.next count += 1 print("出列的人的编号为:", cur.next.data) cur.next = cur.next.next cur = cur.next print("出列的人的编号为:", cur.data) ``` 最后,我们可以在主函数中调用上述函数,解决约瑟夫问题。 ```python if __name__ == '__main__': n = 10 m = 3 head = construct_circle_list(n) josephus(head, m) ``` 上述代码的输出结果为: ``` 出列的人的编号为: 3 出列的人的编号为: 6 出列的人的编号为: 9 出列的人的编号为: 2 出列的人的编号为: 7 出列的人的编号为: 1 出列的人的编号为: 8 出列的人的编号为: 5 出列的人的编号为: 10 出列的人的编号为: 4 ``` 这个结果符合我们的预期,表示最后出列的顺序为3、6、9、2、7、1、8、5、10、4。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值