1 单循环链表
单向循环链表就是链表的尾节点的next指向链表的头结点,这样整个链表就形成了一个环形结构.
单向循环链表的著名的应用场景就是解决约瑟夫问题(Josephu)
2 约瑟夫问题
osephu(约瑟夫、约瑟夫环) 问题
Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
思路:
用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束
3 代码实现
package mylist;
import java.util.LinkedList;
/**
* @author Andy
* @email andy.gsq@qq.com
* @date 2023/2/9 21:04:06
* @desc 约瑟夫问题
* <p>
* Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到
* m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此
* 产生一个出队编号的序列。
*/
public class Josepfu {
public static void main(String[] args) {
josepfuFunc1(5, 1, 2);
josepfuFunc2(5, 1, 2);
josepfuFunc1(10, 2, 3);
josepfuFunc2(10, 2, 3);
}
/**
* 使用java集合中双向链表解决约瑟夫问题
* @param n 链表节点的数量
* @param k 从哪个位置开始
* @param m 隔几个节点抽一个
*/
public static void josepfuFunc1(int n, int k, int m) {
//定义一个链表用来存放1,2,… n 的 n
LinkedList list = new LinkedList();
for (int i = 1; i <= n; i++) {
list.add(i);
}
/**
* 开始报数,从约定编号为 k(1<=k<=n)的人从 1 开始报数,数到m 的那个人出列
* 利用求余来模拟循环报数,比如有三个人1,2,3,从1号位置(在list中的位置实际为0,所以位置数需要减1)报数,
* 假设我们的初始报数位置为index = 1 - 1 = 0
* 报到5,实际从1到5,但我们的list的index是从0到4,所以报数到(5 - 1) = 4
* 就是 (list中开始报数的index(1-1) + (5 - 1)) % (指得是list的长度)3 = 1,
* 在list中index=1的数为2
*
**/
//这里来存储开始报数的位置,因为数组是从0开始的,比如1是存储在0号位置上,所有需要减1
System.out.println("退出的编号为: ");
System.out.print("[ ");
int i = k - 1;
while (list.size() > 0) {
i = (i + (m - 1)) % list.size();
System.out.print(list.remove(i) + " ");
}
System.out.println("]");
}
/**
* 自己定义循环链表解决约瑟夫问题
* @param n 链表节点的数量
* @param k 从哪个位置开始
* @param m 隔几个节点抽一个
*/
public static void josepfuFunc2(int n, int k,int m){
CircleSingleLinkedList list = new CircleSingleLinkedList();
list.addNode(n);
list.show();
list.setFirst(k);
Node node = null;
System.out.println("退出的编号为: ");
//开始抽取数据,直到链表为空为止
while (!list.isEmpty()){
node = list.del(m);
System.out.print(node.getNo() + " ");
}
System.out.println();
}
}
class CircleSingleLinkedList {
private Node first = null;
/**
* 判断数组是否为空
* @return
*/
public boolean isEmpty(){
return first == null;
}
/**
* 设置first 的位置
* @param index
*/
public void setFirst(int index){
if(index < 0 || index > getCount() || index == 1){
return ;
}
for (int i = 1; i < index; i++) {
first = first.getNext();
}
}
/**
* 增加num个node
* 编号从1 到 num
*
* @param num
*/
public void addNode(int num) {
if (num < 1) {
System.out.println("num 的值不正确");
return;
}
Node temp = null;
for (int i = 1;i <= num;i++){
Node node = new Node(i);
if (i == 1){ //处理第一个节点
first = node;
node.setNext(node); //节点自己指向自己
temp = node;
}else{
/**
* node节点的下一个节点指向first,因为插入之后node节点讲师最后的
* 原来链表的next指向node
* temp指向node
*/
node.setNext(first);
temp.setNext(node);
temp = node;
}
}
}
/**
* 求链表节点的数量
* @return
*/
public int getCount(){
if (first == null){
return 0;
}
Node temp = first;
int count= 0;
while (true){
count++;
if (temp.getNext() == first){
break;
}
temp = temp.getNext();
}
return count;
}
/**
*
* 返回链表的尾结点
* @return
*/
public Node getTail(){
if (first == null){
System.out.println("没有数据");
return null;
}
Node temp = first;
while (true){
if (temp.getNext() == first){
break;
}
temp = temp.getNext();
}
return temp;
}
/**
* 删除指定位置的节点,并返回
* @param index
* @return
*/
public Node del(int index){
if(index < 1 || getCount() == 0){
System.out.println("index 的值不正确");
return null;
}
boolean flat = false;
//只有
if (getCount() == 1)
flat = true;
Node temp = first;
if (index == 1){ //删除第一个节点的情况
Node tail = getTail();
first = temp.getNext();
tail.setNext(first);
}else {//其它情况
for (int i = 1; i < index; i++) {
first = temp;
temp = temp.getNext();
}
first.setNext(temp.getNext());
first = first.getNext();
}
//当链表只有一个节点的情况,需要把frist指向null
if (flat)
first = null;
return temp;
}
/**
* 显示链表
*/
public void show(){
if (first == null){
System.out.println("没有数据");
return;
}
Node temp = first;
System.out.print("[ ");
while (true){
System.out.printf("%d ",temp.getNo());
if (temp.getNext() == first){
break;
}
temp = temp.getNext();
}
System.out.println(" ]");
}
}
class Node {
private int no;//人的编号
private Node next; //指向下一个节点
public Node(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}