Java使用双向循环队列实现约瑟夫环

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();
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
/* * 基于双向链表实现双端队列结构 */ package dsa; public class Deque_DLNode implements Deque { protected DLNode header;//指向头节点(哨兵) protected DLNode trailer;//指向尾节点(哨兵) protected int size;//队列中元素的数目 //构造函数 public Deque_DLNode() { header = new DLNode(); trailer = new DLNode(); header.setNext(trailer); trailer.setPrev(header); size = 0; } //返回队列中元素数目 public int getSize() { return size; } //判断队列是否为空 public boolean isEmpty() { return (0 == size) ? true : false; } //取首元素(但不删除) public Object first() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); return header.getNext().getElem(); } //取末元素(但不删除) public Object last() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); return trailer.getPrev().getElem(); } //在队列前端插入新节点 public void insertFirst(Object obj) { DLNode second = header.getNext(); DLNode first = new DLNode(obj, header, second); second.setPrev(first); header.setNext(first); size++; } //在队列后端插入新节点 public void insertLast(Object obj) { DLNode second = trailer.getPrev(); DLNode first = new DLNode(obj, second, trailer); second.setNext(first); trailer.setPrev(first); size++; } //删除首节点 public Object removeFirst() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); DLNode first = header.getNext(); DLNode second = first.getNext(); Object obj = first.getElem(); header.setNext(second); second.setPrev(header); size--; return(obj); } //删除末节点 public Object removeLast() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); DLNode first = trailer.getPrev(); DLNode second = first.getPrev(); Object obj = first.getElem(); trailer.setPrev(second); second.setNext(trailer); size--; return(obj); } //遍历 public void Traversal() { DLNode p = header.getNext(); while (p != trailer) { System.out.print(p.getElem()+" "); p = p.getNex

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

leoliyao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值