使用java的循环单向链表解决约瑟夫问题

什么是约瑟夫问题

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

约瑟夫的故事比较残忍,我们把约瑟夫问题改为一个小游戏,游戏规则如下:

  假设编号为1,2,3...n个人手拉手围坐在一圈,约定编号为k(1<= k <= n)的人开始报数,数到m的人出圈,他的下一位又从1开始报数,数到m的人又出圈,依次类推,直到剩余一个人即为胜出者,由此产生一个出圈编号的序列,

使用循环链表模拟n个人围坐一圈

首先我们定义一个循环链表,由此来模拟n个人围坐在一圈。下面先看下单链表的实现代码

 1 /**
 2  * 单向循环链表
 3  */
 4 class CycleSingleLinkedList{
 5     PersonalNode first = null;
 6     /**
 7      * 向循环链表中添加num个节点
 8      * @param num
 9      */
10     public void add(int num){
11         if(num < 1){
12             System.out.printf("参数不合法");
13             return;
14         }
15         PersonalNode curr = null;
16         for (int i = 1; i <=num; i++) {
17             PersonalNode personalNode = new PersonalNode(i);
18             if(i == 1){
19                 //添加第一个节点
20                 first = personalNode;
21                 curr = first;
22                 curr.setNext(first);
23             }else {
24                 curr.setNext(personalNode);
25                 personalNode.setNext(first);
26                 curr = personalNode;
27             }
28         }
29
30     }
31
32 }
33
34 /**
35  * 人物节点
36  */
37 class PersonalNode {
38     private int no;
39     private PersonalNode next;
40
41     public PersonalNode(int no){
42         this.no = no;
43     }
44
45     public int getNo() {
46         return no;
47     }
48
49     public void setNo(int no) {
50         this.no = no;
51     }
52
53     public PersonalNode getNext() {
54         return next;
55     }
56
57     public void setNext(PersonalNode next) {
58         this.next = next;
59     }
60 }

下面我们用图例的方式讲解一下添加n个节点的循环链表的流程,当创建第一个节点时,我们让first和current指向第一个节点,并将current的next指向first节点,以此来形成一个循环。当添加第二个节点时,我们将current的next指针指向第二个节点,第二个节点的next指向first,然后将current后移,即current指向第二个节点,以此类推。

现在我们问题的第一步即n个人围坐在一起解决了,下面来实现第二步从第k个人开始数数。第二步比较简单,即让first指针后移k-1次,由此让first指针指向第一个数数的人。但由于first指向的节点需要出圈,这里我们再定义一个helper指针,来指向first指针的前一个节点,即让helper指针首先指向first,依此循环直到helper的next指针等于first(因为是循环链表)。并保持helper指针始终在first指针的前一个节点(只有一个节点时与first指针指向同一个节点)。假设k=2即从第二个人开始数,m=3即数到3的人出圈,如下图所示

找到node4的人出圈,这时让helper的next等于first的next,然后让first等于helper的next,如下图所示

继续从node1开始数,数到3,即node3出圈,此时指针指向情况如下图

node3出圈后,下一个要出圈的为node1,依此循环,直到helper与first指针指向相同节点,即node2为最后一个要出圈的人。代码如下

 1 /**
 2      * 约瑟夫问题
 3      * @param n n个人围成圈
 4      * @param k 第k个人开始报数
 5      * @param m 数到m的人出圈
 6      */
 7     public void joseph(int n,int k,int m){
 8         //添加n个人
 9         add(n);
10         //helper指针指向first的前一个节点
11         PersonalNode helper = first;
12         while (helper.getNext() != first){
13             helper = helper.getNext();
14         }
15         //找到第k个人
16         for (int i = 0; i < (k-1); i++) {
17             first = first.getNext();
18             helper = helper.getNext();
19         }
20         //数到m的人出圈
21         while (true){
22             if(helper == first){
23                 break;
24             }
25             for (int i = 0; i < (m-1); i++) {
26                 first = first.getNext();
27                 helper = helper.getNext();
28             }
29             System.out.printf("编号为%d的人出圈\n",first.getNo());
30             first = first.getNext();
31             helper.setNext(first);
32         }
33         System.out.println("最后出圈的人是:"+first.getNo());
34
35     }

测试代码

1 public static void main(String []args){
2         CycleSingleLinkedList linkedList = new CycleSingleLinkedList();
3         linkedList.joseph(4,2,3);
4 }

测试结果:

总结

以上就是实现约瑟夫问题的循环链表解决思路,当然肯定还有很多别的解决思路,欢迎留言探讨,谢谢。

原文地址:https://www.cnblogs.com/menglong1108/p/11617542.html

时间: 2024-11-10 14:11:03

使用java的循环单向链表解决约瑟夫问题的相关文章

循环单向链表(约瑟夫环)

#include <stdio.h> #include <stdlib.h> typedef struct List { int data; struct List *next; }List; //创建循环单向链表n为长度 List *list_create(int n) { List *head, *p; int i; head = (List *)malloc(sizeof(List)); p = head; p->data = 1; //创建第一个结点 for (i =

一步一步写算法(之循环单向链表)

原文:一步一步写算法(之循环单向链表) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 前面的博客中,我们曾经有一篇专门讲到单向链表的内容.那么今天讨论的链表和上次讨论的链表有什么不同呢?重点就在这个"循环"上面.有了循环,意味着我们可以从任何一个链表节点开始工作,可以把root定在任何链表节点上面,可以从任意一个链表节点访问数据,这就是循环的优势. 那么在实现过程中,循环单向链表有什么不同? 1)打印链表数据 void pri

01-(2)数据结构- 一步一步写算法(之循环单向链表)

前面的博客中,我们曾经有一篇专门讲到单向链表的内容.那么今天讨论的链表和上次讨论的链表有什么不同呢?重点就在这个"循环"上面.有了循环,意味着我们可以从任何一个链表节点开始工作,可以把root定在任何链表节点上面,可以从任意一个链表节点访问数据,这就是循环的优势. 那么在实现过程中,循环单向链表有什么不同? 1)打印链表数据 [cpp] view plain copy void print_data(const LINK_NODE* pLinkNode) { LINK_NODE* pI

php实现单,双向链表,环形链表解决约瑟夫问题

传智播客PHP学院 韩顺平 PHP程序员玩转算法第一季  http://php.itcast.cn 聊天篇: 数学对我们编程来说,重不重要? 看你站在什么样的层次来说. 如果你应用程序开发,对数学要求不高 但是,如果你开发系统软件,比如(搜索/识别软件[图像,语言识别]/操作系统...)对数学高 建模.大量数学模型. 老师啊啊.我是学C++的.麻烦,谈哈对QT和MFC的看法嘛.前景什么的, 记住 : 打好基础,大有可为! 初中毕业能去传智学习吗? 学习It, 不管是java ,php ,c#,对

数据结构和算法--3链表(单向链表、双向链表、环形单向链表和约瑟夫问题)

链表 链表是以节点的方式来存储 每个节点包含data域和next域,指向下一个节点 链表的各个节点不一定是连续存储 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定 单向列表 最大特点是可以将物理地址上不连续的数据连接起来,通过指针来对物理地址进行操作,实现增删改查等功能. 单链表分为两种:有头链表和无头链表. 有头节点的增删改查 定义一个单链表的类: //定义一个SingleLinkedList,单链表,管理HeroNode class SingleLinkedList{ //初始

java版的单向链表的逆序输出

将单向链表逆序输出,方法有三种: a.遍历链表,将每个节点的内容存入一个数组中,然后逆序输出数组(最简单的做法) b.使用栈来逆序输出 c.直接将链表逆序然后输出(本文采用的方法) 先介绍算法: 1). 若链表为空或只有一个元素,则直接返回: 2). 设置两个前后相邻的指针p,q. 将p所指向的节点作为q指向节点的后继: 3). 重复2),直到q为空 4). 调整链表头和链表尾 示例:以逆序A->B->C->D为例,图示如下 package com.moluo.shujujiegou;

java版的单向链表的基本操作

首先定义链表节点的结构: package com.moluo.linklist; /** * * @author 行者摩罗 * */ public class Node { private int data; private Node next; public int getData() { return data; } public void setData(int data) { this.data = data; } public Node getNext() { return next;

I学霸官方免费教程三十七:Java数据结构之单向链表结构

数据结构之单向链表 例如:现有双向链表OneWayLinked中存储着1,2,3,4四个元素,那么集合对象中会有4个节点A.B.C.D,由上述结构可以知道,节点A中存储着元素1和节点B:节点B中存储着元素2和节点C,节点C中存储着元素3和节点D,节点D中存储着元素4和null.如果现在要在元素2和3中间插入一个元素5:过程如下:1.创建节点E,E中存储元素52.将B中的下一个节点修改为节点E3.将E中的下一个节点赋值为节点C从上述过程看,插入时没有节点位置移动的操作,所以效率比较高:删除的过程和

java模拟实现单向链表

直接上代码 1 package com.bjpowernode.test.datastructure; 2 3 public class Mylinked { 4 5 Node first;//头结点 6 7 int size;//链表的长度 8 9 public void add(Object value) { 10 Node newNode = new Node(value); 11 if (first == null) { 12 first = newNode; 13 } else { 1