【数据结构】约瑟夫链表-环形链表
2022-12-09 13:49:08 TwelveT 215
一、双向链表双向链表的定义:在单链表的每个结点里再增加一个指向其直接前驱的指针域prior,这样链表中就形成了有两个方向不同的链,故称为双向链表。优点:双向链表可以克服单链表查找链表中某结点不方便的缺点。双向循环链表:和单链的循环表类似,双向链表也可以有循环表。让头结点的前驱指针指向链表的最后一个结点,让最后一个结点的...
一、约瑟夫问题
- 约瑟夫问题是个著名的问题:N个人围成一圈,第一个人从1开始报数,报M的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的胜利者。
例如只有三个人,把他们叫做A、B、C,他们围成一圈,从A开始报数,假设报2的人被杀掉。 - 首先A开始报数,他报1。侥幸逃过一劫。
- 然后轮到B报数,他报2。非常惨,他被杀了
- C接着从1开始报数
- 接着轮到A报数,他报2。也被杀死了。
- 最终胜利者是C
二、基本原理
- 约瑟夫环算法是:n个人围成一圈,每个人都有一个互不相同的密码,该密码是一个整数值,选择一个人作为起点,然后顺时针从1到k(k为起点人手中的密码值)数数,数到k的人退出圈子,然后从下一个人开始,继续从1到j(j为刚退出圈子的人的密码)数数,数到j的人退出圈子。重复上面的过程,直到剩下最后一个人。

三、代码实现
1.创建链表节点
class Boy {
/**
* 编号
*/
private int no;
/**
* 指向下一个节点,默认null
*/
private Boy next;
public Boy(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
}
2.链表实现
/**
* 创建一个环形的单向链表
*/
class CircleSingleLinkedList {
private final static Logger log = LoggerFactory.getLogger(CircleSingleLinkedList.class);
/**
* 创建一个first节点,当前没有编号
*/
private Boy first = new Boy(-1);
/**
* 添加小孩节点,构成一个环形链表
*
* @param nums 数量
*/
public void addBoy(int nums) {
if (nums < 1) {
log.error("nums的值不正确");
return;
}
// 辅助指针,帮助构建环形链表
Boy curBoy = null;
// 循环创建环形链表
for (int i = 1; i <= nums; i++) {
// 根据编号创建小孩节点
Boy boy = new Boy(i);
if (i == 1) {
first = boy;
// 构成环
first.setNext(first);
// 指向第一个小孩
curBoy = first;
} else {
curBoy.setNext(boy);
boy.setNext(first);
curBoy = boy;
}
}
}
/**
* 遍历环形链表
*/
public void showBoys() {
// 判断是否为空数据
if (first == null) {
log.error("数据为空");
return;
}
// first不能移动,需要使用辅助指针
Boy curBoy = first;
while (true) {
log.info("小孩编号为:{}", curBoy.getNo());
// 判断是否循环完毕
if (curBoy.getNext() == first) {
break;
}
// curBoy后移
curBoy = curBoy.getNext();
}
}
/**
* 根据用户的输入,计算小孩的出拳顺序
*
* @param k 表示从第几个小孩开始
* @param m 表示数几下
* @param nums 表示最初有多少小孩在圈中
*/
public void countBoy(int k, int m, int nums) {
// 校验数据
if (first == null || k < 1 || k > nums) {
log.error("参数有误,请重新输入");
return;
}
// 创建辅助指针,帮助小孩出圈
Boy helper = first;
// 指针指向链表最后一个节点,让其以最后一个开始数 1->5
while (helper.getNext() != first) {
// curBoy后移
helper = helper.getNext();
}
// 小孩报数前,先让first和helper移动k-1次,移动到需要开始的位置前一个
for (int i = 0; i < k - 1; i++) {
// 1 -> 2
first = first.getNext();
// 5 -> 1
helper = helper.getNext();
}
// 当小孩报数时,让first和helper指针同时的移动m-1次(移动到需要出圈的小孩),然后出圈
// 这里是一个循环操作,知道圈中只有一个节点
while (helper != first) {
// 说明圈中只有一个节点
// 让first和helper指针同时移动m-1
for (int i = 0; i < m - 1; i++) {
first = first.getNext();
helper = helper.getNext();
}
// 这时first指向的节点就是要出圈的小孩节点
log.info("小孩{}出圈", first.getNo());
// 这时将first指向小孩节点出圈
first = first.getNext();
helper.setNext(first);
}
log.info("最后留在圈中的小孩编号:{}", helper.getNo());
}
}
免责声明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络收集整理,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。如果您喜欢该程序和内容,请支持正版,购买注册,得到更好的正版服务。我们非常重视版权问题,如有侵权请邮件与我们联系处理。敬请谅解!
![]() |
相关文章
-
12-09【数据结构】java实现双向链表
-
12-06【数据结构】java实现单向链表