问题 C: 【例2-3】围圈报数

题目描述

有n(n<100)个人依次围成一圈,从第1个人开始报数,数到第m个人出列,然后从出列的下一个人开始报数,数到第m个人又出列,…,如此反复到所有的人全部出列为止。设n个人的编号分别为1,2,…,n,打印出列的顺序。

输入

n和m。

输出

出列的顺序。

样例输入

4 17

样例输出

1 3 4 2

我们可以看到这个围圈报数(即约瑟夫问题),其实就是链表问题。我们通过这道题需要掌握的是循环单链表的写法(以c语言为例)

一、循环链表的写法

1.先定义链表的结构体

struct child{
	int data;
	struct child* next;
};

然后再全局变量定 “头节点”和“约瑟夫环的两个参数”

struct child* headnode;
int n,m;

下面,我们来定义两个函数,分别是“创建空节点”与“创建有数据的节点”

struct child* emptynode(){
	struct child* p = (struct child*)malloc(sizeof(struct child));
	p->data = NULL;
	p->next = NULL;
	return p;
}

struct child* createnode(int num){
	struct child* p = (struct child*)malloc(sizeof(struct child));
	p->data = num;
	p->next = NULL;
	return p;
}

我们可以看出这两段代码的主要差别,其实就是p->data=NULL和p->data=num

接下来定义一个函数“createlistBytail”,以尾接法,创建循环单链表

void createlistBytail(struct child* headnode,int num){
    struct child* r=headnode;
    for (int i=2; i<=num; i++ ){
       struct child* s = createnode(i);
       s->next = r->next;
       r->next = s;
       r=s;
    } 
    r->next = headnode;
}

其中需要注意的是,这段代码看似为单纯的创建单链表,其实我们在结尾处添加了一段代码

r->next = headnode;

这段代码保证了单链表的循环,也正因为这段代码,使得我们的单链表成功升级为循环单链表!

当然,为了检验我们的链表是否创建成功,我们可以写入这列代码,来遍历出链表的数据

void printlist(struct child* headnode){
	struct child* pMove = headnode;
	while(pMove) {
		printf("%d",pMove->data );
		pMove = pMove->next ;
	}
}

 接下来,我们开始创建solve函数,实现围圈报数功能

void solve(struct child* headnode,int n,int m){
	int i,j;
	struct child* p;
	struct child* q;
	for(i=1;i<=n;i++){
		p = headnode;
		j = 1;
		for(j=1;j<m-1;j++){
		    p = p->next ;
		}
		q = p->next ;
		printf("%d ",q->data );
		p->next = q->next ;
		headnode = p->next ;
	}
}

其中,我们需要注意的是 我们在进行第二个for循环时,j<m-1这个数值不要搞错。我们为什么要j<m-1呢?因为我们要保证p是在第m个人的前一个人,这样我们就可以使q指向p->next,也就是我们想要的那个第m个人,实现精准查找,然后精准“删除”。但是问题来了,我们为什么不直接将p指向第m个人呢,这样我们直接输出p不就好了么:(。非也非也!你只是想到了输出数值的事,但是后面的事你却没能想到。

我们在这段函数的主要任务是什么呢?1.输出第m个人2.删除m,让m前一个人接上m后一个人3.将领头人headnode变成m的下一个人,也就是说从m的下一个人开始报数。这样我们不难发现我们将p设为m的前一个人,是要将p-next指向q->next,实现删除m的目的,因为我们节点的指针能指向某节点的后面,却指向不了节点的前面,所以哦我们需要这样的一个p指向m前一个点。

最后,我们编写主函数,将上面的函数包装进去

其中我们要注意的是,创建空节点与数据节点不在主函数里。

int main(){
	struct child* headnode = createnode(1);
	scanf("%d %d",&n,&m);
    createlistBytail(headnode,n);
	//printlist(headnode);
    solve(headnode,n,m);
    return 0;
}

这样,我们的代码就算结束了。最后,给大家奉上“围圈报数”(即约瑟夫问题)的完整版代码

#include<stdio.h>
#include<stdlib.h>
struct child{
	int data;
	struct child* next;
};

struct child* headnode;
int n,m;

struct child* emptynode(){
	struct child* p = (struct child*)malloc(sizeof(struct child));
	p->data = NULL;
	p->next = NULL;
	return p;
}

struct child* createnode(int num){
	struct child* p = (struct child*)malloc(sizeof(struct child));
	p->data = num;
	p->next = NULL;
	return p;
}

void createlistBytail(struct child* headnode,int num){
    struct child* r=headnode;
    for (int i=2; i<=num; i++ ){
       struct child* s = createnode(i);
       s->next = r->next;
       r->next = s;
       r=s;
    } 
    r->next = headnode;
}

void printlist(struct child* headnode){
	struct child* pMove = headnode;
	while(pMove) {
		printf("%d",pMove->data );
		pMove = pMove->next ;
	}
}

void solve(struct child* headnode,int n,int m){
	int i,j;
	struct child* p;
	struct child* q;
	for(i=1;i<=n;i++){
		p = headnode;
		j = 1;
		for(j=1;j<m-1;j++){
		    p = p->next ;
		}
		q = p->next ;
		printf("%d ",q->data );
		p->next = q->next ;
		headnode = p->next ;
	}
}

int main(){
	struct child* headnode = createnode(1);
	scanf("%d %d",&n,&m);
    createlistBytail(headnode,n);
	//printlist(headnode);
    solve(headnode,n,m);
    return 0;
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值