数据结构与算法: 约瑟夫(丢手绢)问题(单向链表,双向链表解决)
Tips: 采用java语言, 关注博主,底部附有完整代码
采用到的知识点:
- 单向环形链表
- 双向环形链表
- 单向 / 双向 环形链表出圈
流程效果图:
单向环形链表 | 双向环形链表 |
---|---|
什么是约瑟夫问题
约瑟夫问题好像小时候玩的丢手绢一样
先来说一下规则, 默认从第一个小朋友开始数,在第n个小朋友过后开始寻手绢, 第n个小朋友每k个人就丢下手绢,然后这个小朋友出圈
直到圈中只剩一人才结束
假设5个小朋友围坐一圈来丢手绢
例如这样:
在第2个小朋友开始丢手绢
那么就是元素三出圈
接着每2个元素依次出圈
tips: 这里单向环形链表和双向环形链表思路有所不同
, 所以结果也有所不同!
- 单向环形链表 先创建好5个元素 然后从第n个孩子开始,每k次出圈 , (第一次从第n个孩子数也
会出圈
) - 双向环形链表 直接调用一个方法,并且传入需要“丢手绢”的元素,在开始出圈 (第一次从第n个孩子数也
不会出圈
)
单向环形链表
自定义链表 AnnularLinkedList.java:
public class AnnularLinkedList {
//头节点
private HeroNode3 head;
/**
* get头节点
* @return 头节点
*/
public HeroNode3 getHead() {
return head;
}
/**
* 创建元素
* @param sum 需要创建的个数
*/
public void createHeroNode(int sum) {
if (sum < 1) {
System.out.println("请正确输入创建个数");
return;
}
HeroNode3 temp = null;
for (int i = 1; i <= sum; i++) {
HeroNode3 node3 = new HeroNode3(i, "张三" + i);
if (i == 1) {
//记录第一个位置
head = node3;
//辅助变量,记录第一个位置
temp = node3;
//第一个元素指向自己,构建成环形
head.next = head;
}
//指向新的元素
temp.next = node3;
//新的元素指向第一个节点
node3.next = head;
//temp后移
temp = temp.next;
}
}
/**
* @param start 从元素几开始数
* @param number 每数几下出圈
*/
public void count(int start, int number) {
//获取链表长度
int sum = size();
System.out.println("sum为:" + start + "\t" + number + "\t" + sum);
//start < 1 不能从 < 1 开始的数 数
//sum < start 链表总数不能 < 开始的位置
if (start < 1 || sum < start) {
System.out.println("请正确输入");
return;
}
HeroNode3 help = head;
//将 help 移动到 head 后面
while (help.next != head) {
help = help.next;
}
//从第start位置开始循环
for (int i = 0; i < start - 1; i++) {
head = head.next;
help = help.next;
}
//如果heap != head 那么就继续循环
//当help == head 时,则退出循环
while (help != head) {
//每number下出圈
for (int i = 0; i < number - 1; i++) {
head = head.next;
help = help.next;
}
//此时head则为要出圈的元素
System.out.println("要出圈的勇士id为:" + head.id);
//出圈操作
head = head.next;
help.next = head;
}
//最终help = head 那么help/head就是最后一个在圈中的元素
System.out.println("留在圈中的勇士id为:" + help.id);
}
/**
* 计算链表长度
*/
public int size() {
int length = 1;
HeroNode3 temp = head;
while (temp.next != head) {
length++;
temp = temp.next;
}
return length;
}
/**
* 输出链表所有数据
*/
public void show() {
if (head == null) {
System.out.println("链表为null,不能打印");
}
HeroNode3 temp = head;
while (temp.next != null) {
System.out.println("show:" + temp);
//当最后一个元素 = 第一个元素时,表示链表当前在最后一个元素,停止循环
if (temp.next == head) {
break;
}
//temp后移
temp = temp.next;
}
}
}
HeroNode3.java:
public class HeroNode3 {
int id;
String name;
HeroNode3 next;
public HeroNode3(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "HeroNode3{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
调用:
AnnularLinkedList linkedList = new AnnularLinkedList();
linkedList.createHeroNode(5);
System.out.println("测试的值为:"+linkedList.getHead().next.next.next.next.next.next);
linkedList.show();
linkedList.count(2, 2);
来看看运行结果:
如果对单向链表或者约瑟夫问题陌生的,项目中还有我画的2张详细流程图 那就下载看看吧
双向环形链表
AnnularDoubleLinkedList.java
/**
* @author: android 超级兵
* @create: 2022-05-05 11:53
* TODO 约瑟夫问题 双向链表
**/
public class AnnularDoubleLinkedList {
public AnnularHeroNode headNode = new AnnularHeroNode();
/**
* TODO 约瑟夫问题 双向链表
*
* @param n 从第几个开始 (0 - list.size())
* @param k 每次走几个 (k < list.size())
* @param list 元素
*/
public void josephusProblem(int n, int k, List<AnnularHeroNode> list) {
if (list.size() < n) {
System.out.printf("当前元素一共%d个,无法从第%d个元素开始", list.size(), n);
return;
}
headNode = list.get(0);
// TODO 先将每个元素链接起来
for (int i = 0; i < list.size(); i++) {
AnnularHeroNode node = list.get(i);
// 下一个元素
AnnularHeroNode nextNode;
if (i != list.size() - 1) {
nextNode = list.get(i + 1);
} else {// 最后一个节点
node.setLastNode(true);
nextNode = list.get(0);
}
node.next = nextNode;
nextNode.pre = node;
}
System.out.println("测试是否成功:");
System.out.println(headNode.next.next.next.next);
System.out.println(headNode.pre.pre.pre.pre);
System.out.printf("最后一个节点为:%s\n", headNode.pre);
System.out.printf("第一个节点为:%s\n", headNode);
show();
// 从第几个元素开始
AnnularHeroNode tempNode = list.get(n);
System.out.printf("从%s开始\n", tempNode);
System.out.println();
AnnularHeroNode currentNode = null;
// 当前节点上一个节点
AnnularHeroNode currentPreNode = null;
while (true) {
// 每次过n个人
for (int i = 0; i < k; i++) {
currentNode = tempNode.next;
tempNode = tempNode.next;
}
System.out.printf("currentNode:%s\n", currentNode);
// 移除当前选中的
currentNode.pre.next = currentNode.next;
currentNode.next.pre = currentNode.pre;
// 当前位置的最后一个位置
currentPreNode = currentNode.pre;
// 如果当前位置后一个位置 = 当前位置 说明只有一个,则退出循环
if (currentPreNode == currentNode) {
break;
}
}
System.out.printf("最终留在圈中的是 %s\n", currentNode);
}
private void show() {
System.out.println("\n遍历: ======= start =======");
AnnularHeroNode tempHeadNode = headNode;
while (true) {
System.out.println(tempHeadNode);
if (tempHeadNode.isLastNode()) {
break;
}
tempHeadNode = tempHeadNode.next;
}
System.out.println("遍历: ======= end =======\n");
}
}
AnnularHeroNode.java
/**
* @author: android 超级兵
* @create: 2022-05-05 11:54
* TODO
**/
public class AnnularHeroNode {
private final int id;
private final String name;
private final String title;
// 是否是最后一个节点
private boolean isLastNode;
// 下一个节点
public AnnularHeroNode next;
// 上一个节点
public AnnularHeroNode pre;
public AnnularHeroNode() {
id = 0;
name = "";
title = "";
}
public int getId() {
return id;
}
public AnnularHeroNode(int id, String name, String title) {
this.id = id;
this.name = name;
this.title = title;
}
public boolean isLastNode() {
return isLastNode;
}
public void setLastNode(boolean lastNode) {
isLastNode = lastNode;
}
@Override
public String toString() {
return "AnnularHeroNode{" +
"id=" + id +
", name='" + name + '\'' +
", title='" + title + '\'' +
", isLastNode='" + isLastNode + '\'' +
'}';
}
}
使用:
package a20220505环形双向列表;
import java.util.ArrayList;
/**
* @author: android 超级兵
* @create: 2022-05-05 12:07
* TODO 约瑟夫问题 双向链表解决
**/
public class Client {
public static void main(String[] args) {
AnnularDoubleLinkedList annularDoubleLinkedList = new AnnularDoubleLinkedList();
AnnularHeroNode node0 = new AnnularHeroNode(0, "史进", "九纹龙");
AnnularHeroNode node1 = new AnnularHeroNode(1, "鲁智深", "花和尚");
AnnularHeroNode node2 = new AnnularHeroNode(2, "林冲", "豹子头");
AnnularHeroNode node3 = new AnnularHeroNode(3, "宋万", "云里金刚");
AnnularHeroNode node4 = new AnnularHeroNode(4, "吴用", "智多星");
ArrayList<AnnularHeroNode> nodeArrayList = new ArrayList<>();
nodeArrayList.add(node0);
nodeArrayList.add(node1);
nodeArrayList.add(node2);
nodeArrayList.add(node3);
nodeArrayList.add(node4);
// 23401
annularDoubleLinkedList.josephusProblem(1, 2, nodeArrayList);
}
}
原创不易,您的点赞与关注就是对我最大的支持!