据说著名的犹太历史学家JosephUS 有过以下的故事:
在罗马人占领乔塔帕特后,39个犹太人与JosephUS 以及他的朋友躲在一个山洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第一个人开始报数,每报数到第三个人,这个人就必须得自杀,然后由下一个人报数,直到所有人都自杀身亡为止,聪明的JosephUS 和选择站在 35位和16位 ,最终他们逃过一劫~
其实,这种题目可以用 数组来做,直接用循环,每次循环41次,符合条件的输出后 赋值为0 ,那么这样下一次循环就可以 根据这个数是否为0 判断 是否要输出了,不过,这样做的问题就是 ,他的时间复杂度o(n^2)就是很耗时间(其实我不感觉耗时间…可能41个人太少了把~)
但是,如果我们采取循环链表来做的话,时间可以大大缩短 ,下面我们直接在代码里一探究竟把!
#include<stdio.h>
#include<stdlib.h> //malloc 的头文件
#include<time.h> //这个可以不用,这个是我上次写代码留下的…
typedef struct node
{
int data ;
struct node * next ;
}node;
node *create (int n)
{
node *p =NULL;
node *head;
head =(node *)malloc(sizeof(node)); //创建一个头结点
p=head;//P指向头结点
node *s;
int i =1;
if(0!=n)
{
while(i<=n)
{ s=(node*) malloc (sizeof (node));
s->data=i++; //依次递增的加入数据
p->next =s; //P的下一个结点指向 新的结点; 这里也是head->next 为s的意思对吧?有助于下面更好理解哦
p=s;//再将下一个结点地址赋值给P
}
s->next=head->next ;//head的next不就是s吗? 这样就导致能够形成了一个循环的表
}
free(head);//因为头结点 并没有存放数据,这个环形状像个"6" head 就是多出来的那一点点东西 嘿嘿
return s->next ; //返回s 因为 s->next 就是 最初 s开始的结点
}
int main()
{
int n=41;//41个人
int m=3;//数字为3的循环周期
int i ;
node *p=create(n); //创建41个内存空间(能循环的,前面说过了),并返回 头指针
node *temp; //中间值
m%=n;//这行代码可以不用
while(p!=p->next)// 注释:我的屁股指向我自己,只剩下我一个了,是不是?
{for(i=1;i<m-1;i++)// 循环执行一次 就从第一次while 循环开始 ,p->data =1
{
p = p->next; //移动一位 p->data=2 这时候已经指向第二个了,不难理解 p->next->data =3
}
printf("%d->",p->next->data); //输出下一位的数据 3 所以,第一个是3
temp = p->next; //将删除数据的 地址传给temp
p->next =temp->next;//下一个就是有效的地址 例如第一次循环temp ->next 就是4 给了p->next (此时p为2) 那么 链表就和3断开了联系
free(temp);//释放 孤儿结点
p=p->next;//p从第2 变成了第4 (我说的是地址) 因为123为一个循环,那么下一个循环就应该是 456 ,4开始啊,
}//回到while 然后看while 注释 会好理解一点
printf("%d\n",p->data);//最后一个的data
return 0;
}
如果看完还有不懂的,可以去哔哩哔哩 看小甲鱼的数据结构和算法 这是第十八集的内容
如果看懂的话,可以尝试一下这道题目:
编号为1-N的N(相当上面的41)个人按照顺时针方向围坐一圈 ,每个人持有一个密码(正整数,可以只有输入) ,开始的那个人选择一个正整数作为报数上限的M (相当上面的3)然后呢从第一个人开始按照顺时针方向 从1 开始报数,报到 M的时候停止报数, 报到M的人出列,将他的密码作为新的M值从他顺时针方向的下一个人开始从1报数,如此下去,直到所有人出列为止。