约瑟夫环问题(C语言版)——双向循环链表和静态链表实现

约瑟夫环问题,也称圆桌问题,解决的方案很多,在此,给出三种解决方案,双向循环链表,静态链表和公式法

题目描述

编号为1,2,…,n的n个人按顺时针方向围坐在一张圆桌周围,每人持有一个密码(正整数)。一
开始任选一个正整数m作为报数上限值,从第一个人开始按顺时针方向自1开始报数,报到m时停止报数,报m的那个人出列,将他的密码作为新的m值,从他顺时针方向的下一个人开始重新从1报数,数到m的那个人又出列;如此下去,直至圆桌周围的人全部出列为止。要求按出列顺序输出n个人的编号。
输入
第一行输入两个整数,依次表示人数n和初始化密码m,以空格间隔。
第二行依次输入n个整数,分别表示n个人的密码,以空格间隔。
输出
按出列次序输出每个人的编号,以空格间隔。
输入
7 20
3 1 7 2 4 8 4
输出
6 1 4 7 2 3 5

双向循环链表

思想十分简单,就是建立一个双向链表来存储数据结构体,然后一直遍历,当遍历到指定位置,改变密码值,然后删除该结点,如此循环,直到仅剩一个结点时,退出循环

下面是主要函数的流程图:
约瑟夫环双向链表

源码如下(注释已加)

//双向循环链表解决约瑟夫环问题
#include<stdio.h>
#include<stdlib.h>

typedef struct node{
    int data;   //手持密码值
    int num;    //编号
    struct node *prev;
    struct node *next;
}Jose_node;

//debug打印链表
void print_list(Jose_node *head,int n){
        Jose_node *p = head;
        while(n--){
            printf("data = %d,num = %d \n",p->data,p->num);
            p = p->next;
            getchar(); 
        }
}
//创建链表
Jose_node *Creatlist(){
    Jose_node *head = (Jose_node*)malloc(sizeof(Jose_node));
    head->data = 0;
    head->next = head;
    head->prev = head;
    return head;
}
//链表输入
int Insertlist(Jose_node *head){
    int sum,pwd;
    Jose_node *p = head;
    
    scanf("%d %d",&sum,&pwd);
    
    int n = sum;
    
    //在头后开始插入结点,所以结点 编号 为 2 
    int num = 2;

    //头的编号为1
    head->num = 1;
    //插入结点
    while(sum--){
        Jose_node *q = (Jose_node*)malloc(sizeof(Jose_node));
        scanf("%d",&p->data);
        q->num = num;
        num++;

        q->next = head;
        head->prev = q;
        p->next = q;
        q->prev = p;
        
        p = p->next;
    }

    //释放空结点
    p = head->prev;
    p->prev->next = p->next;
    p->next->prev = p->prev;
    free(p);
    
    //打印链表
    /* print_list(head,n); */
    
    //返回密码值
    return pwd;
}
//约瑟夫环
void Josephus(Jose_node *head,int pwd){
    int n = 1;
    Jose_node *p = NULL;
    
    while(head){
        //与密码相同
        if(n == pwd){
            printf("%d ",head->num);
            
            //结束
            if(head->next == head){
                printf("\n");
                free(head);
                break;
            }

            //重置密码
            pwd = head->data;
            
            //释放结点
            p = head; 
            head = head->next;
            p->prev->next = p->next;
            p->next->prev = p->prev;
            free(p);
            
            //从 1 开始
            n = 1;
        }else{
            //继续游戏
            head = head->next;
            n++;
        }
    }
}

int main()
{
    //创建链表
    Jose_node *head = Creatlist();
    //添加数据
    int pwd = Insertlist(head);
    //约瑟夫环
    Josephus(head,pwd);
    return 0;
}

运行结果:
约瑟夫环样例一

静态链表法

这个就更加简单了,只需要将数据存入链表中,然后每当一个人出局时,对其进行标识,初始化步数,继续进行遍历,快越界时,初始化下标即可

//静态链表解决约瑟夫环问题
#include<stdio.h>
#include<stdlib.h>
typedef struct node{
    int data;
    int num;
}Jose_node;

int main()
{
    int sum,pwd;
    scanf("%d %d",&sum,&pwd);
    Jose_node *head = (Jose_node *)malloc(sizeof(Jose_node)*(sum+1));
    
    //循环输入数据
    for(int i = 1;i <= sum;i++){
        scanf("%d",&head[i].data);
        head[i].num = i;
    }

    //下标
    int h = 1;
    //步数
    int steps = 1;
    int flag = 0;

    while(1){
        
        //越界
        if(h > sum){
            h = 1;
            continue;
        }
        
        //已被删除
        if(head[h].num == -1){
            h++;
            continue;
        }
        
        //当到达这个点
        if(steps == pwd){
            printf("%d ",head[h].num);
            
            //人已经全部离开
            flag++;
            if(flag == sum){
                break;
            }
            
            //标识已离开
            pwd = head[h].data;
            head[h].num = -1;
            
            //步数初始化
            steps = 1;

            //下标继续向后
            h++;
            continue;
        }
        
        h++;
        steps++;
    }

    printf("\n");
    return 0;
}

具体运行结果同上面的运行结果

公式法

这个就是一个奥数推导题目,推导过程 没有细看 看了没懂,以后有机会学习一下,最终的公式十分简单
以下转载于这个博客
题目:
n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。求胜利者的编号。

#include <stdio.h>
int main()
{
    int n, m, i, s = 0;
    printf ("N M = ");
    scanf("%d%d", &n, &m);
    for (i = 2; i <= n; i++)
    {
        s = (s + m) % i;
    }
    printf ("\nThe winner is %d\n", s+1);
}
  • 9
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值