当前位置:   article > 正文

循环链表:约瑟夫问题(非常详细易理解)_约瑟夫循环

约瑟夫循环

约瑟夫问题来源

据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

如何使用循环链表进行模拟

因为约瑟夫问题时一个环状,又被称为约瑟夫环,故用循环链表非常好进行模拟

每数2个人后,将第3个人干掉,在循环链表中就是把这个节点del掉,然后指针指向后面一个人,数2个人后,又将第3个人干掉,一直这样,直到最后不足3个人,依次自杀后退出循环

代码中有详细说明

如果愿意,你可以跑一下代码。其实就是找规律,方法有很多种,不过循环链表这种方式很贴近约瑟夫问题也更好理解。

详细代码:

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #define OK 1
  5. #define ERROR 0
  6. typedef struct Node
  7. {
  8. int data;
  9. struct Node* next;
  10. } Node;
  11. /*
  12. 创建循环链表
  13. 在上篇数据结构—循环链表的实现博客中循环链表中有头结点,在我们的单链表中头结点是非必须,必须要有头指针,有头结点头指针指向头结点
  14. 无头结点,头指针指向头结点。
  15. 在循环链表中,如果有头结点 那么尾结点的指针域指向头结点形成环,如果没有头结点 那么尾结点的指针域指向第一个结点形成环。
  16. 有无头结点都不重要,重要的是这种思想,那种更加方便解决我们的问题,视情况是否需要头结点。
  17. num 为约瑟夫环的结点个数
  18. 创建约瑟夫环的结点,构成约瑟夫环,使用循环链表模拟
  19. */
  20. int Create(Node** joseph,int num)
  21. {
  22. if (joseph == NULL || num < 1)
  23. {
  24. return ERROR;
  25. }
  26. //创建头结点
  27. Node* head = (Node*)malloc(sizeof(Node));
  28. head->next = NULL;
  29. //移动的指针,开始指向头结点
  30. Node* p = head;
  31. //创建约瑟夫结点,采用尾插法,每次创建的结点放到链表尾部
  32. for (size_t i = 1; i <= num; i++)
  33. {
  34. //创建新结点,并且赋值
  35. Node* node = (Node*)malloc(sizeof(Node));
  36. node->data = i;
  37. node->next = head;
  38. //下面2步干啥?
  39. //p指向链表尾结点,p->next=node 将新建结点添加到链表尾部。
  40. //p = node 移动p指针让其始终指向链表尾结点
  41. p->next = node;
  42. p = node;
  43. }
  44. //循环结束 p 指向最后一个结点,此时约瑟夫环包含头结点
  45. //方便解决问题,我们把头结点去掉,整个环剩下约瑟夫结点。
  46. p->next = head->next;
  47. free(head);
  48. //约瑟夫环,循环链表中头指针指向环第一个结点
  49. *joseph = p->next;
  50. return OK;
  51. }
  52. int Length(Node* joseph)
  53. {
  54. if (joseph == NULL)
  55. {
  56. return 0;
  57. }
  58. Node* target = joseph;//开始target指向第一个结点
  59. int length = 1;
  60. for (; target->next!=joseph; target = target->next)
  61. {
  62. length++;
  63. }
  64. return length;
  65. }
  66. /*
  67. 模拟约瑟夫自杀问题
  68. 每数到第3个人进行自杀,然后从下一个人重新开始数,数到第3个人进行自杀...
  69. 如果是4人个进行自杀
  70. 1 2 3 4
  71. 死亡顺序:->3->2->4->1
  72. n 为 数到第 n个人自杀,约瑟夫 问题 是 每数到第3个人自杀。
  73. 这里可以进行设置,也可以每数到 第4个人进行自杀。
  74. */
  75. int ShowKill(Node* joseph,int n)
  76. {
  77. if (joseph == NULL)
  78. {
  79. return ERROR;
  80. }
  81. int num = Length(joseph);
  82. //skip为杀第n个人要跨越的人数
  83. int skip = n-1;
  84. Node* p = joseph;
  85. Node* begin = p;//begin指向开始数的第1个人
  86. while (1)
  87. {
  88. begin = p;
  89. //找到自杀人的前一个位置
  90. for (size_t i = 0; i < skip-1; i++)
  91. {
  92. p = p->next;
  93. }
  94. //最后剩2个人时,依次自杀
  95. //temp == p->next 时剩下最后2个人!
  96. if (begin == p->next)
  97. {
  98. printf("->%d", begin->data);
  99. printf("->%d", begin->next->data);
  100. free(begin);
  101. free(p);
  102. begin = NULL;
  103. p = NULL;
  104. break;
  105. }
  106. //循环完,p 指向自杀前面一个人
  107. //p->next 进行自杀,实质上就是将p->next结点从循环链表中干掉
  108. Node* kill = p->next;
  109. printf("-->%d", kill->data);
  110. //将kill移除结点
  111. p->next = kill->next;
  112. free(kill);
  113. kill = NULL;
  114. //p移动到后面3个人的第1个人的位置
  115. p = p->next;
  116. }
  117. printf("\n");
  118. return OK;
  119. }
  120. int main(int argc, char *argv[])
  121. {
  122. Node* joseph = NULL;
  123. int num = 1;
  124. while (num)
  125. {
  126. printf("请输入约瑟夫环人数(输入0退出):");
  127. scanf("%d", &num);
  128. if (!num)
  129. {
  130. break;
  131. }
  132. printf("约瑟夫环死亡顺序:\n");
  133. joseph = NULL;
  134. Create(&joseph,num);
  135. ShowKill(joseph, 3);
  136. }
  137. return 0;
  138. }

验证结果截图:





声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/551223
推荐阅读
相关标签
  

闽ICP备14008679号