且将新火试新茶

诗酒趁年华

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

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

约瑟夫环(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);
}
阅读更多
上一篇数据结构之循环链表和双向链表
下一篇动态数组实现Point对象栈类
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭