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