利用循环链表解决约瑟夫问题

据说著名的犹太历史学家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报数,如此下去,直到所有人出列为止。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是牛大春呀

老板糊涂啊

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值