Java使用双向循环队列实现约瑟夫环
问题描述
分析
如果使用使用链表的方式来实现的话,由于循环的顺序是先顺时针再逆时针,因此考虑使用双向循环队列来实现。
需要注意的是 我们还需要使用一个头结点来指向第一个节点,用头结点来作为这个双向循环链表的一个入口,因此整个链表看起来的样子大概是这样
因此对于上面的节点形式我们可以定义出改节点类
//创建一个双向链表节点类
class DoubleLinkedNode{
public DoubleLinkedNode preNode;
public DoubleLinkedNode nextNode;
public int password;
public int number;
public DoubleLinkedNode(int password,int number){
preNode = null;
nextNode = null;
this.password = password;
this.number = number;
}
}
至此为止,节点就已经创建好了,现在就该开始创建链表了
由于链表的长度是我们自己指定的,并且创建每个节点的时候还应该告诉每个节点他们所携带的密码应该是多少,因此在链表类的构造函数里面创建链表
int len = 0; // 列表的长度
// 创建头结点和尾结点
DoubleLinkedNode head = new DoubleLinkedNode(0,0);
DoubleLinkedNode r = head;
// 初始化列表的长度
public DoubleLinked(int k,int pwd[]){
this.len = k;
int i;
for (i = 1;i <= k;i++){
DoubleLinkedNode LNode = new DoubleLinkedNode(pwd[i-1],i);
r.nextNode = LNode;
LNode.preNode = r;
r = LNode;
}
// 最后一个节点需要特殊的处理
r.nextNode = head.nextNode;
head.nextNode.preNode = r;
}
public void popList(int rest,int m){
DoubleLinkedNode currentNode = head.nextNode;
while (rest != len){
int k = 1;
while (k < m){
currentNode = currentNode.nextNode;
k++;
}
System.out.println(currentNode.number);
// 对头结点下一个节点的删除需要特别处理
if (head.nextNode.number == currentNode.number){
head.nextNode = currentNode.preNode;
}
m = currentNode.password;
currentNode.nextNode.preNode = currentNode.preNode;
currentNode.preNode.nextNode = currentNode.nextNode;
currentNode = currentNode.preNode;
len = len - 1;
if (rest == len) break;
k = 1;
while (k < m){
currentNode = currentNode.preNode;
k++;
}
System.out.println(currentNode.number);
// 对头结点下一个节点的删除需要特别处理
if (head.nextNode.number == currentNode.number){
head.nextNode = currentNode.nextNode;
}
m = currentNode.password;
currentNode.nextNode.preNode = currentNode.preNode;
currentNode.preNode.nextNode = currentNode.nextNode;
currentNode = currentNode.nextNode;
len = len - 1;
}
}
在删除节点的时候需要注意的是,如果删除的节点是头结点所指向的节点,那么这个节点的删除就需要额外的小心,不能像普通的节点一样直接删除,而还需要改变头结点所指的节点,否则当完成删除进行遍历的时候可能1号节点在循环的过程中已经被删除了但是输出的时候还是会从1号节点开始遍历。
同时,由于每个人都携带一个密码,当把出队的节点的密码作为下一次循环截至的序号的时候可以做一个小的优化(密码%链表的长度),防止密码过大而在链表中多次的兜圈子
最后是出队
public void popRest(){
int i = 0;
DoubleLinkedNode currentNode = head;
while (i < len){
currentNode = currentNode.nextNode;
System.out.println(currentNode.number);
i++;
}
}
最后附上完整代码和测试代码
//创建一个双向链表节点类
class DoubleLinkedNode{
public DoubleLinkedNode preNode;
public DoubleLinkedNode nextNode;
public int password;
public int number;
public DoubleLinkedNode(int password,int number){
preNode = null;
nextNode = null;
this.password = password;
this.number = number;
}
}
// 创建一个双向循环列表
public class DoubleLinked{
int len = 0; // 列表的长度
// 创建头结点和尾结点
DoubleLinkedNode head = new DoubleLinkedNode(0,0);
DoubleLinkedNode r = head;
// 初始化列表的长度
public DoubleLinked(int k,int pwd[]){
this.len = k;
int i;
for (i = 1;i <= k;i++){
DoubleLinkedNode LNode = new DoubleLinkedNode(pwd[i-1],i);
r.nextNode = LNode;
LNode.preNode = r;
r = LNode;
}
// 最后一个节点需要特殊的处理
r.nextNode = head.nextNode;
head.nextNode.preNode = r;
}
public void popList(int rest,int m){
DoubleLinkedNode currentNode = head.nextNode;
while (rest != len){
int k = 1;
while (k < m){
currentNode = currentNode.nextNode;
k++;
}
System.out.println(currentNode.number);
// 对头结点下一个节点的删除需要特别处理
if (head.nextNode.number == currentNode.number){
head.nextNode = currentNode.preNode;
}
m = currentNode.password;
currentNode.nextNode.preNode = currentNode.preNode;
currentNode.preNode.nextNode = currentNode.nextNode;
currentNode = currentNode.preNode;
len = len - 1;
if (rest == len) break;
k = 1;
while (k < m){
currentNode = currentNode.preNode;
k++;
}
System.out.println(currentNode.number);
// 对头结点下一个节点的删除需要特别处理
if (head.nextNode.number == currentNode.number){
head.nextNode = currentNode.nextNode;
}
m = currentNode.password;
currentNode.nextNode.preNode = currentNode.preNode;
currentNode.preNode.nextNode = currentNode.nextNode;
currentNode = currentNode.nextNode;
len = len - 1;
}
}
public void popRest(){
int i = 0;
DoubleLinkedNode currentNode = head;
while (i < len){
currentNode = currentNode.nextNode;
System.out.println(currentNode.number);
i++;
}
}
}
测试代码
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
System.out.println("请输入总人数: ");
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
System.out.println("请输入报数上限m: ");
int m = sc.nextInt();
int pwd[] = new int[n];
System.out.println("请输入每个人的密码: ");
for (int i=0;i<n;i++){
pwd[i] = sc.nextInt();
}
System.out.println("请输入最终要留下的人数: ");
int rest = sc.nextInt();
DoubleLinked list = new DoubleLinked(n,pwd);
if (rest > n){
System.out.println("留下人数大于实际的人数,输入错误");
}else {
System.out.println("依次出队的编号为:");
list.popList(rest,m);
}
System.out.println("留下的人的编号为: ");
list.popRest();
}
}