约瑟夫环问题

问题描述:

      已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为1的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。---这是网上给的通俗说法。

      而我要说的这个约瑟夫环问题是这样的:在上述问题的基础上每个人持有一个int型的password,当链表建成后传过来初始密码m,从第一个人开始报数,报到m的人出列并打印,然后将此人的password作为新的密码,下一个人开始报数,如此往复,直到最后一个人也出列。

       好了,问题描述完了,接下来具体实现,先看结构体的定义。

#include <stdio.h>
#include <stdlib.h>
typedef struct node{
	int id;
	int password;
	struct node *next;
}node;

      接下来是链表的创建了,当然是单向循环链表。

node* creat(int n){
	node *head,*q,*tail;
	head = q = tail = NULL;   //初始化一下 
	for( int i = 1; i <= n; i++){
		q = (node *)malloc(sizeof(node));	//申请一段内存 
		q->id = i;							
		printf("请输入第%d个人的密码:",i); 
		scanf("%d",&q->password);
		if(i == 1){		//这里用的是尾插法,因为输入的数据和链表读出来的一致 
			head = q;
		}
		else{
			tail->next = q;
		}
		tail = q;
	}
	tail->next = head;
	return tail;		//我这里返回的是尾节点,即头节点的前驱,因为这样可以
} 			    // 在后续的操作中起到很大的作用 

      然后是出列的一系列操作了,此处用名为print的函数来实现。

void print(node *tail,int m){		//参数为尾节点和初始密码 
	node *q,*p;
	int n;
	q = tail;
	p = tail->next;    
	while(p != q){		                 //当剩余一个人时,p q相等,退出循环 
		for( n = 1; n < m; n++ ){        //每次循环遍历到出列的前一位 
				q = q->next;	//通过next->next操作来实现 
				p = q->next;	//这就是为什么要传入尾节点 
		}			        //因为如果传头节点,而初始密码为1,那将很难操作 
		m = p->password;
		printf("%d ",p->id);
		q->next = p->next;
		free(p);
		p = q->next;
	}
	printf("%d",q->id);	//输出最后一个人的id 
	free(q);		//释放节点 
}

      main方法

int main(){
	int n,Password;
	node *tail;
	printf("请输入人数:");
	scanf("%d",&n);
	printf("请输入初始密码:");
	scanf("%d",&Password);
	tail = creat(n);
	print(tail,Password); 
	return 0;
}

输入示例:

请输入人数:7
请输入初始密码:20
请输入第1个人的密码:3
请输入第2个人的密码:1
请输入第3个人的密码:7
请输入第4个人的密码:2
请输入第5个人的密码:4
请输入第6个人的密码:8
请输入第7个人的密码:4

执行结果:

6 1 4 7 2 3 5

      通过这道题我想表达的是,单向循环链表是非常灵活的,每一个节点既有一个前驱又有一个后继,因此在处理问题的时候可以多方面观察,比如这个传尾节点,而并非通常的头节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值