约瑟夫环专题
前言
上次作业:
判断一个非空单向链表是否带环
分析:(3)和(4)都是在带环结点处会有两个后继,导致前驱结点存后继地址空间不够,所以(3)、(4)不存在,反观(1)、(2)是可以的。
(1)、(2)的链表结构如下:
如何检测带环:我们可以想到,如果链表带环,遍历链表则势必会对带环结构重复遍历,因此我们可以想到用大小指针来检测链表是否带环。
1.如果带环:大指针转n圈和小指针相遇;
2.如果不带环:大指针率先next->NULL。
代码实现:
int huan(ElemSN *h){
int flag=0;//设置一个岗哨,如果带环,flag设置成1,不带环则为0
ElemSN *p,*q;
p=q=h;
do{//当型结构用do while循环实现遍历
p=p->next->next;//设置p为大指针
q=q->next://设置q为小指针
if(p==q){//如果带环大小指针总能相遇
flag=1;//岗哨设置成1
break;
}
}while(p&&p->next);
return flag;
}
一、约瑟夫环是什么?
示例:有n个小孩围成一个圈,制定从每个小孩开始,每s个小孩,该小孩出圈,问所有小孩出圈次序。
二、步骤
1.创建一个循环链表
设n=10,s=3;
2.新链表——小孩出圈顺序(尾插)
(1)找到需要出圈的小孩
(2)尾插到新链表
结束条件:t==t->next
算法实现如下:
while(t->next){
//找出圈小孩
//t跑s-1下
for(int i=0;i<s*&&t=t->next;==i);
//删
p=t->next;
t->next=p->next;
//尾插
if(!hn){
hn=tn=p;
}else{
tn=tn->next=p;
}
tn=tn->next=t;
tn->next=NULL;
}
代码实现:
//创建小孩图
ElemSN *GreateJosephusRing(int a[]){
ElemSN *t=NULL;
for(int i =0;i<n;++i){
ElemSN *p=(ElemSN*)malloc(sizeof(ElemSN));
p->data=a[i];
if(!t){
t=p->next=p;
}else{
p->next=t->next;
t=t->next=p;
}
}
return t;
}
//小孩出圈
//改进算法:while(t-t->next){
// 不够s-1次,跑t
// 够s-1次,删,插
// }
ElemSN *JosephusRing(ElemSN *t,int s){
ElemSN *hn,*tn,*p;
int i=1;
while(t-t->next){
if(i%s){
t=t->next;
}else{
p=t->next;
t->next=p->next;
}
if(!hn){
hn=tn=p;
}else{
tn=tn->next=p;
}
i++;
}
return tn;
}
//输出
void print(ElemSN *hn){
for(ElemSN *h=*hn;hn;hn=hn->next){
printf("%5d",hn->data);
}
}