A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© xiaochongzi 中级黑马   /  2015-7-28 20:48  /  3989 人查看  /  15 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

3黑马币
耶稣15个门徒,围成一圈,从1开始报数,报数编号1-3,当谁报到3,谁就退出,叛徒就在继续报数的人里面,找出最后一个退出的人,他就是叛徒。
求注释一定要详细详细详细在详细

最佳答案

查看完整内容

兄弟,上边内容纯手打,有任何疑问可以随时交流,最好把分也分享给我。。。。哈哈哈哈哈

15 个回复

倒序浏览
  1. /*
  2. 对于这个题目,大家既然都可以用数组循环的方式来解决,那么对基本的解决思想是有的,而用数组和链表的区别是:使用数组最大的障碍是下标,因为下标的连续性,导致我们无法判定活着的下一个人在什么位置,而需要遍历当前的人员后边的每一个位置,直到这个人是活的,才参与报数。而使用双向链表的优势在于,每个节点都记录的自己的前一个人的地址是什么,后一个人的地址是什么,便于直接访问。
  3. */
  4. #include <stdio.h>
  5. #include <stdlib.h>

  6. int main(int argc, const char * argv[]) {
  7.    
  8.     struct people{
  9.         int num; //当前人员的号码
  10.         struct people *top;//当前人员的前一个人员
  11.         struct people *next;//当前人员的后一个人员
  12.     };//声明一个结构体
  13.     typedef struct people Note;
  14.     int N=13,q=3;//N是总人数q是喊到q的人被杀
  15.    
  16.     Note *head,*tail,*pnew,*topNote,*nextNote;
  17.     //head 链表的头
  18.     //head 链表的尾
  19.     //pnew 新添加的元素
  20.     //topNote 上一个元素
  21.     //nextNote 下一个元素
  22.     head=(Note *)malloc(sizeof(Note));//创建头指针
  23.     head->next=NULL;//头指针的下一个地址是空的,可以相当于一个人的时候
  24.     head->num=1;//当前的人的编号
  25.     tail=head;//尾指针指向头指针
  26.     for (int i=2; i<=N; i++) { //初始化链表。为了便于人员编号循环从2开始
  27.         pnew=(Note *)malloc(sizeof(Note));//创建当前人员
  28.         pnew->num=i; //人员的编号
  29.         pnew->top=tail;//新的人员进入链表之后,这个人员的前一个人就是原来链表的最后一个人
  30.         pnew->next=NULL;//新的人员的后边没有人,复制NULL
  31.         tail->next=pnew;//原链表中的后边一个人成了新添加的人员
  32.         tail=pnew;//新的人员进入链表之后,链表的尾指针指向新添加的人员,形成新的链表
  33.     }
  34.     tail->next=head; //链表尾部的下一个节点指向头指针
  35.     head->top=tail;//链表头部的上一个节点指向尾指针 完成环形链表
  36.     //如果这里有疑问,可以打印出连表中每个节点Top next num 和地址,看于想象的是否相同
  37.    
  38.     pnew=tail;//杀人游戏开始,当前节点指向尾指针,因为从头指针还是报数,我们认为尾指针是0
  39.     for (int i=1; i<N; i++) {//循环N-1次,循环一次杀一人
  40.         for (int j=0; j<q; j++) {//循环q次,找到被杀人员pnew
  41.             pnew=pnew->next;
  42.         }
  43.         topNote=pnew->top;//找出被杀人员的上一个人员
  44.         topNote->next=pnew->next;//上一个人员的后边一个人,变成当前人员的后一个人。用数字来表示1 2 3,假设被杀的人员是2号,那么2号的上一个人员(也就是1号)他的右边的人员变成了2号右边的人员。下边相同
  45.         nextNote=pnew->next;//找出被杀的下一个人员
  46.         nextNote->top=pnew->top;//注释同上
  47.         free(pnew);//释放当前空间
  48.         pnew=topNote;//当前人员的上一个人员变成上一个活着的人
  49.     }
  50.     printf("存活的人员编号是:%d\n",pnew->num);//最后链表中只剩一个节点,为了验证,我们做以下操作
  51.     printf("下一个活着的人员的编号是:%d\n",pnew->next->num);
  52.     printf("上一个活着的人员的编号是:%d\n",pnew->top->num);
  53.     return 0;
  54. }
复制代码
兄弟,上边内容纯手打,有任何疑问可以随时交流,最好把分也分享给我。。。。哈哈哈哈哈
回复 使用道具 举报
本帖最后由 lizhichao 于 2015-7-28 21:28 编辑
  1. public static void yuesefu(int totalNum, int countNum) {  
  2. 18         // 初始化人数  
  3. 19         List<Integer> start = new ArrayList<Integer>();  
  4. 20         for (int i = 1; i <= totalNum; i++) {  
  5. 21             start.add(i);  
  6. 22         }  
  7. 23         //从第K个开始计数  
  8. 24         int k = 0;  
  9. 25         while (start.size() >0) {  
  10. 26             k = k + countNum;  
  11. 27             //第m人的索引位置  
  12. 28             k = k % (start.size()) - 1;  
  13. 29            // 判断是否到队尾  
  14. 30             if (k < 0) {  
  15. 31                 System.out.println(start.get(start.size()-1));  
  16. 32                 start.remove(start.size() - 1);  
  17. 33                 k = 0;  
  18. 34             } else {  
  19. 35                 System.out.println(start.get(k));  
  20. 36                 start.remove(k);  
  21. 37             }  
  22. 38         }  
复制代码

回复 使用道具 举报
拿去研究吧,请采纳,谢谢

W1`%A}IVG6X$L_{D%`)]ZWX.jpg (118.83 KB, 下载次数: 17)

W1`%A}IVG6X$L_{D%`)]ZWX.jpg
回复 使用道具 举报

希望对你有帮助

本帖最后由 刘唐飞 于 2015-7-29 02:13 编辑

13个人按顺序报数,报道3的人进行标记,下次继续进行13次循环,报数之前判断标记为,如果标记为为真就继续执行,否则就跳过,下一个人接着报数。

定义一个13个大小的数组,用来标记,看那一个是最后标记的。


代码实现:
  • void jesusApostles()  
  • {  
  •    
  •     int length = 13;  
  •     int j = 0;  
  •     int data[13] = {0};  
  •     int traitor = 0;  
  •     while ((isFinished(data,length)) == 0) {  
  •         for (int i = 0; i < length; i ++) {  
  •             if (data == 100) {  
  •                 continue;  
  •             }  
  •               
  •             data = (j % 3) + 1;  
  •             if (data == 3) {  
  •                 data = 100;  
  •                 traitor = i;  
  •                   
  •             }  
  •               
  •             j ++;  
  •         }  
  •     }  
  •       
  •     printf("traitor: %d\n",traitor + 1);  
  • }  
  •   
  • int isFinished(int *data,int length)  
  • {  
  •     int flag = 1;  
  •     for (int i = 0; i < length; i++) {  
  •         if (data != 100) {  
  •             flag = 0;  
  •         }  
  •     }  
  •       
  •     return flag;  

回复 使用道具 举报

说了链表,循环的不需要
回复 使用道具 举报
刘唐飞 发表于 2015-7-29 02:10
13个人按顺序报数,报道3的人进行标记,下次继续进行13次循环,报数之前判断标记为,如果标记为为真就继续 ...

抱歉,我需要的是链表的实现方法
回复 使用道具 举报
小小歪 发表于 2015-7-28 22:14
拿去研究吧,请采纳,谢谢

要链表,循环不要再拿来了好吧
回复 使用道具 举报
xiaochongzi 发表于 2015-7-29 08:11
要链表,循环不要再拿来了好吧

在IOS交流群里不是贴给你了么,需要每句代码的解释么?
回复 使用道具 举报
Eil.tea 发表于 2015-7-29 10:19
在IOS交流群里不是贴给你了么,需要每句代码的解释么?

恩,不太明白,需要的,最后用c语言写
回复 使用道具 举报
Eil.tea 发表于 2015-7-28 20:48
兄弟,上边内容纯手打,有任何疑问可以随时交流,最好把分也分享给我。。。。哈哈哈哈哈
...

谢啦,终于觉得自己看懂了,我都想在给你加分,数组和递归函数的算法我都会,就想整明白把约瑟夫这个写篇blog
回复 使用道具 举报
这几种算法的时间复杂度都是比较高的,就算是链表时间复杂度也是O(N*q)的时间复杂度,还有种动态规划的思想的时间复杂度为O(n)。你有兴趣可以查一下,代码就几行
int solve(int n, int q){
    int jn=0;
    for(int i=1; i<=n; i++)
        jn=(jn+q)%i;
    return jn;
}
因为解释起来比较麻烦我就不赘述了。我曾经有个同学说,人的思想复杂度和代码的复杂度成反比,也就是说,思想越NB了,往往写出来的代码就几行。
回复 使用道具 举报
Eil.tea 发表于 2015-7-29 14:45
这几种算法的时间复杂度都是比较高的,就算是链表时间复杂度也是O(N*q)的时间复杂度,还有种动态规划的思想 ...

恩,兄台,你流程走多少了啊,看你是学过的啊
回复 使用道具 举报
xiaochongzi 发表于 2015-7-29 20:42
恩,兄台,你流程走多少了啊,看你是学过的啊

视频还没看完,我以前学过几年算法
回复 使用道具 举报
双向循环链表是一个最容易想到的方法.不过单向循环链表也能实现,用一个指针记录当前人的上一个位置即可
回复 使用道具 举报
我也用数组做过这个题目,是我的基础测试里面的:

  1. #include <stdio.h>
  2. /*
  3. 程序总体思路:
  4.     用一个数组存储每个人的编号,然后按顺序报数,每数到3该人的编号变为0,并且好人的数量加1.
  5. 当到达数组尾部时再从数组头部开始报数,这个时候数组中有些人的编号为0,当到达编号为0的人时,
  6. 此人不报数,直接跳到下一个人,以此类推。当好人数量达到14时,数组就只有一个非零数了,这个编号
  7. 的人就是叛徒。
  8. */
  9. int main()
  10. {
  11.     //变量good_man用来存储好人个数
  12.     int good_man = 0;
  13.     //创建一个容量为15的数组
  14.     int arr[15];
  15.     //变量flag用来记录
  16.     int flag = 0;
  17.     //对数组中的每个人编号,
  18.     for (int i = 0; i < 15; i++) {
  19.         //由于数组是从0开始的,而此题编号从1开始,所以编号是i+1
  20.         arr[i] = i+1;
  21.     }
  22.    
  23.     //定义一个遍历数组的循环变量j
  24.     int j = 0;
  25.     //当好人数量不是14的时候进行循环
  26.     while (good_man != 14)
  27.     {
  28.         //如果某人的编号不为零,则flag加1
  29.         if (arr[j] != 0) {
  30.             flag++;
  31.         }
  32.         /*如果flag的值为3,则此人是好人,将此人的编号设为0,
  33.          并且将好人加1。最后把flag重置为0.
  34.          */
  35.         if (flag == 3) {
  36.             arr[j] = 0;
  37.             flag = 0;
  38.             good_man++;
  39.         }
  40.         //循环变量自增
  41.         j++;
  42.         //如果j到达数组尾部,则让j回到数组头部去
  43.         if (j == 15) {
  44.             j = 0;
  45.         }
  46.         
  47.     }
  48.    
  49.     //打印数组中唯一一个编号非零的人,这个人就是叛徒
  50.     for (int k = 0; k < 15; k++)
  51.     {
  52.         if (arr[k] != 0)
  53.         {
  54.             printf("出卖耶稣是第%d个人\n",arr[k]);
  55.         }
  56.     }
  57.    
  58.     return 0;
  59. }

  60. /*这个程序也可以用单向循环链表实现:每数到3将相应的元素从链表中移除*/
复制代码
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马