用循环单链表求解约瑟夫环(基于c语言)

问题背景:据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。

示例代码:

头文件(list.h)

#pragma once
#ifndef LIST_H
#define LIST_H
#include<stdlib.h>

typedef int ElemType;
typedef struct List_ {
    ElemType data;
    struct List_* next;
}LNode,*List;

int Locate(List L, int i);
#endif

源文件(list.c)

#include<string.h>
#include<stdio.h>
#include"list.h"
int main() {
    LNode G;
    int i;
    printf("请输入表中元素序号的最大值:");
    scanf_s("%d", &i);
    Locate(&G, i);

}

int Locate(List L, int i) {
    L = (List)malloc(sizeof(LNode));
    L->next = L;
    int j,w,k=0,d=0;
    List q=L;
    for (j = 1;j <= i;j++) {
        List p;
        p = (List)malloc(sizeof(LNode));
        p->data = j;
        q->next = p;
        p->next = L;
        q = p;
    }
    List r,x;
    r = L;
    while (d < i-2) {
        x = r->next;
        if(x!=L&&x->data!=0){
            k++;//记录次数,看其是否是3的倍数
        }
        
        if (k % 3 == 0&&k!=w) {
            
            d++;
            x->data = 0;
        }
        r = x;
        w = k;//由于后面空结点较多,k在循环几次后很可能不会改变,故启用另一个变量记录k在上一个循环的值,看两个是否相等;
    }
    while (i > 0) {
        if (L->data > 0) {
            printf("%d\n", L->data);
            
        }
        L = L->next;
        i--;
    }
    return 0;
}

写该程序中应注意的问题:

代码思路:首先创建一个链表,并根据总人数依次创建结点,每个结点按顺序给予结点的数据域(从1开始);在“自杀”部分,把死去的人的数据域设为0;

链表创建部分比较简单,重要的是后面的每报一个“3”就杀死那个人,这里采用一个循环(while(d<i-2)),i是总人数,d是要杀死的总人数;

此处注意:(1)遇到头结点时,k这个计数器不能+1;

(2)由于后续会有很多data域等于0,所以要注意循环时上一次跟这一次k的计数不能相同(因为如果相同的话,k可能连续几次循环都是3的倍数,从而导致循环提前退出);

(3)为了解决(2)的问题,可声明另一个变量w记录下前一次的报数k,然后在下一次循环中判断k是否等于w;

最后,输出不会被杀的位置序号:采用了while循环,以i(链表里元素总个数)大于0为循环条件,依次将data域大于0的数字输出。如下图所示:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值