循环链表的实例——约瑟夫环

循环链表的实例——约瑟夫环

约瑟夫环(Josephus)问题是由古罗马的历史学家约瑟夫(Josephus)提出的,该问题具体描述是:设有编号为1,2,……,n的n个人围成一个圈,从第1个人开始报数,报m(m为报数上限)的人将出列被杀掉,再从他的下一个人起重新报数,如此下去,知道所有人全部出列为止。此时,最后一个出列的将是幸存者。当任意给定n和m后,设计算法求n个人出列的次序。

由于约瑟夫环问题中要求n个人围成一圈,按照一定的方向依次报数,很自然地会想到用单循环链表来表示。下面将介绍用单循环链表解决约瑟夫环问题的有关思路。

1.数据结构

对于约瑟夫环问题,可以采用长度为n的单循环链表来表示n个人围成一圈,每当某人报数为m时,就从循环链表中将该结点删除,并从下一个结点继续开始报数。循环链表的结点中应包含一个用于存放开始时每个人在圈中排列序号(即初始循环链表中的排列位置)的id域。具体的结点结构描述如下:

typedef int ElemType;
typedef struct node{
    ElemType id;
    struct node *next;
}ListNode,*LinkList;

2.程序实现

程序中主要用到创建链表和约瑟夫环算法两个函数,具体代码如下:

#include "stdio.h"
#include "stdlib.h"
/* 常量定义 */
#define OK 0
#define Err_Momery -1
#define Err_InvalidParam -2
/* 结点结构定义*/
typedef int ElemType;
typedef struct node{
    ElemType id;
    struct node *next;
}ListNode,*LinkList;

typedef int Status;

Status CreateList(LinkList Head, int n){    // 创建具有n个结点的单循环链表
    if(!Head)
        return Err_InvalidParam;
    ListNode *p,*s;
    int i;
    Head->next = Head;      // 初始化头结点
    p = Head;               // p指向头结点Head
    for(i=1; i<=n; i++){
        s = (ListNode*)malloc(sizeof(ListNode));
        s->id = i;
        s->next = p->next;  // 将结点s插入到Head的末尾
        p->next = s;
        p = p->next;
    }
    return OK;
}

int Josephus(LinkList Head, int m){    // 约瑟夫环问题算法
    if(!Head)
        return Err_InvalidParam;
    int iCount = 1, iOrder = 0;         // 分别保存当前报数和当前出列次序
    ListNode *pcur,*pprev,*pdel;        // 分别用于指向当前结点你、前驱结点和要删除结点
    pprev = Head;
    pcur = Head->next;
    while(pprev != pcur->next){      // 当链表剩余结点大于1
        if(pcur != Head && iCount==m){  // 找到报数为m的当前结点
            pdel = pcur;                // 删除当前结点
            pprev->next = pcur->next;
            pcur = pcur->next;
            iOrder++;                   // 出列次序加1
            printf("第%d个出列序号为:%d\n", iOrder,pdel->id);
            free(pdel);
            iCount=1;                   // 重新报数,从1开始
        }
        else{
            if(pcur != Head)            // 头结点不报数
                iCount++;
            pprev = pcur;               // pprev指向当前结点
            pcur = pcur->next;          // pcur指向下一结点
        }
    }
    printf("第%d个出列序号为:%d\n", ++iOrder,Head->next->id);
    free(pcur);
    free(pprev);
    return OK;
}

int main(){
    int n,m;
    LinkList Joseph;
    printf("请输入参与人数n:");
    scanf("%d",&n);
    printf("请输入报数上限m:");
    scanf("%d",&m);
    Joseph = (ListNode*)malloc(sizeof(ListNode));
    if(CreateList(Joseph,n) != OK){
        printf("创建链表错误!\n");
        return -1;
    }
    if(Joseph->next==Joseph){
        printf("参与人数错误!\n");
        return -1;
    }
    printf("\n");
    Josephus(Joseph,m);
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值