数据结构 约瑟夫环

Posted Ice丨shine

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 约瑟夫环相关的知识,希望对你有一定的参考价值。

目录

一、需求分析

功能需求:

设编号为1,2,…,n(n>0)个人按顺时针方向围坐一圈,每人持有一个正整数密码。开始时任意给出一个报数上限m,从第一个人开始顺时针方向自1起顺序报数,报到m时停止报数,报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人起重新自1报数;如此下去直到所有人全部出列为止。

界面需求:

输入人数n,每个人的密码和报数上限(初始密码)m,输出出列顺序

二、概要设计


建立线性表解决该问题:
(1) 建立表的储存结构,获取初始m的值
(2) 根据m的值查找出列数据元素的位置,删除该数据元素并获取新的m值
(3) 重复(2)直到表空

接口设计

void Ysfring(CycleLinkList L,int n,int m);//约瑟夫环
void InitList(CycleLinkList L);//初始化链表
bool ListInsert(CycleLinkList *L,int i,ElemType e); //在不带头结点的循环单链表中第i个数据元素之前插入新的数据元素e
void ListAppend(CycleLinkList *L,ElemType e); //在非空循环单链表表尾插入一个结点

数据结构设计

typedef struct ElemType//数据元素类型
        int location;//位置
        int password;//密码
ElemType;
//循环链表
typedef struct CycleLinkList
        struct LNode *head;
        struct LNode *tail;
CycleLinkList,*CL;
//结点结构
typedef struct LNode
        ElemType data;
        struct LNode *next;
LNode,*LinkList;

三、详细设计

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct ElemType//数据元素类型
        int location;//位置
        int password;//密码
ElemType;
//循环链表
typedef struct CycleLinkList
        struct LNode *head;
        struct LNode *tail;
CycleLinkList,*CL;
//结点结构
typedef struct LNode
        ElemType data;
        struct LNode *next;
LNode,*LinkList;

bool Equal(ElemType a,ElemType b)
        if(a.location == b.location && a.password == b.password)
                return true;
        else
                return false;


void Print(ElemType e)
     printf("位置:%d,密码:%d",e.location,e.password);



void Ysfring(CycleLinkList L,int n,int m)//约瑟夫环
     LNode *current = L.tail,*s;
     ElemType e;
     int k=n;
     while(n>0)
           m = m % n;
           if(m == 0) m = n; 
           int j;
           for(j=1;j<m;j++) current = current ->next;//current指向应该出列的结点的前一个结点
           s=current->next;//s指向出列结点
           current->next=s->next;//让s结点退出链表
           printf("第%d个出列的位序是:%d\\n",k-n+1,s->data.location);
           m=s->data.password;//得到当前结点的密码
           free(s);//结点出列
           n--;
     
     L.head=L.tail=NULL;



void InitList(CycleLinkList L)//初始化链表
        L.head=L.tail=NULL;

//在不带头结点的循环单链表中第i个数据元素之前插入新的数据元素e
bool ListInsert(CycleLinkList *L,int i,ElemType e)
        LNode *p,*s;
        if(i<1) return false;
        if(i==1)
                s = (LNode *)malloc(sizeof(LNode));
                s->data = e;
                if(!L->head==NULL)
                    L->head=L->tail=s;//将s插入空表中
                
                else
                    s->next=L->head;
                    L->head=s;//将s结点插在表首
                
                L->tail->next=L->head;//形成循环链表
        
        else
                p=L->head;
                int j=1;
                while(p!=L->head&&j<i-1)//找到需要插入结点的位置
                    p=p->next;
                    j++;
                
                if(p==L->head)//插入失败返回false
                    return false;
                else
                    s = (LNode *)malloc(sizeof(LNode));
                    s->data=e;
                    s->next=p->next;
                    p->next=s;

                
        
        return true;

//在非空循环单链表表尾插入一个结点
void ListAppend(CycleLinkList *L,ElemType e)
        LNode *s;
        s=(LNode *)malloc(sizeof(LNode));
        s->data=e;
        s->next=L->tail->next;
        L->tail->next=s;
        L->tail=s;



int main()

    CycleLinkList L;
    InitList(L);
    int n,m;
    ElemType e;
    printf("《约瑟夫环》\\n");
    printf("请输入人数:\\n");
    scanf("%d",&n);
    if(n<1) //判断输入是否合法
        printf("请输入正整数!");
        return 0;
    

    printf("请输入第1人的密码:\\n");
    scanf("%d",&e.password);
    if(e.password<1)//判断输入是否合法
        printf("请输入正整数!");
        return 0;
    
    e.location=1;
    ListInsert(&L,1,e);
    int i;
    for(i=2;i<=n;i++)
        printf("请输入第%d个人的密码:\\n",i);
        scanf("%d",&e.password);
        if(e.password<1)
                printf("请输入正整数!");
                return 0;
        
        e.location=i;//输入每个人的密码并插入表尾
        ListAppend(&L,e);
    

    printf("请输入初始密码:");
    scanf("%d",&m);
    Ysfring(L,n,m);
return 0;

四、调试分析

  1. 在建表过程中,插入的第一个结点与插入其他结点有所不同
  2. 删除结点时,为了明确删除结点的前驱,设置current指针记录,该指针初始指向表尾
  3. 为过滤不必要的循环次数,使用模运算,提高效率
  4. 由于使用了模运算,算法的复杂度为O(n²)。

五、用户手册

1、本程序经code::blocks编译,运行环境为win10
2、进入演示程序后,将显示如下的界面

3、输入人数和各个人所拥有的密码,再输入初始密码后输出出列顺序,如下图:

六、测试结果

编译Ysfring.c后执行,输入人数5,密码1-5号人分别为5,4,3,2,1,结果如下:

输入小于1的人数或密码输出提示信息并退出。

以上是关于数据结构 约瑟夫环的主要内容,如果未能解决你的问题,请参考以下文章

约瑟夫环_java_数组

数据结构 约瑟夫环

C-约瑟夫环

约瑟夫环1

约瑟夫环小结(线段树)

Java循环链表实现约瑟夫环(搬运)