来源于书籍《数据结构与算法(Java描述)》邓俊辉 著 机械工业出版社
队列
队列的抽象数据类型就是一个容器,其中的对象排成一个序列,我们只能访问和取出排在最前端(Front)的对象,只能在队列的尾部(Rear)插入新对象。正是按照这一规则,才能保证最先被插入的对象首先被删除(FIFO)。
使用数组实现
public interface Queue <T> {
int getSize();
boolean isEmpty();
//取队首元素,但不删除
Object front() throws QueueEmptyException;
void enqueue(T obj) throws QueueFullException;//入队
T dequeue() throws QueueEmptyException;//出队
void Traversal();//遍历
}
public class QueueEmptyException extends RuntimeException{
public QueueEmptyException(String err){
super(err);
}
}
public class QueueFullException extends RuntimeException{
public QueueFullException(String err){
super(err);
}
}
package com.kqgeo.bigdata.study.queue;
/**
* @Auther: bigboss
* @Date: 2020/8/7 11:06
* @Description:
*/
public class QueueByArray<T> implements Queue<T> {
public static final int CAPACITY = 1000;
protected int capacity;
protected Object[] Q;
protected int f;//队首元素的位置
protected int r;//队尾元素的位置
public QueueByArray(){
this(CAPACITY);
}
public QueueByArray(int cap){
capacity=cap;
Q=new Object[capacity];
}
@Override
public int getSize() {
return (capacity-f+r)%capacity;
}
@Override
public boolean isEmpty() {
return (f == r);
}
@Override
public Object front() throws QueueEmptyException {
if(isEmpty()){
throw new QueueEmptyException("Queue is empty!");
}
return Q[f];
}
@Override
public void enqueue(T obj) throws QueueFullException {
if(getSize() == capacity-1){
throw new QueueFullException("队列满了");
}
Q[r]=obj;
r=(r+1)%capacity;
}
@Override
public T dequeue() throws QueueEmptyException {
Object ele;
if(isEmpty()){
throw new QueueEmptyException("Queue is empty!");
}
ele=Q[f];
Q[f]=null;
f=(f+1)%capacity;
return (T) ele;
}
@Override
public void Traversal() {
for (int i = f; i < r; i++) {
System.out.print(Q[i]+" ");
}
System.out.println();
}
public static void main(String[] args) {
Queue<String> queue=new QueueByArray<>();
queue.enqueue("zhangsan");
queue.enqueue("lisi");
queue.enqueue("wangwu");
System.out.println(queue.getSize());
queue.Traversal();
}
}
Josephus 环
孩提时的你是否玩过“烫手山芋”游戏:一群小孩围成一圈,有一个刚出锅的山芋在他们之间传递。其中一个孩子负责数数,每数一次,拿着山芋的孩子就把山芋转交给右边的邻居。一旦数到某个特定的数,拿着山芋的孩子就必须退出,然后重新数数。如此不断,最后剩下的那个孩子就是幸运者。
通常,数数的规则总是从 1 开始,数到 k 时让拿着山芋的孩子出列,然后重新从 1 开始。Josephus问题可以表述为:n 个孩子玩这个游戏,最后的幸运者是谁?
为了解答这个问题,我们可以利用队列结构来表示围成一圈的n个孩子。一开始,假定对应于队列首节点的那个孩子拿着山芋。然后,按照游戏的规则,把“土豆”向后传递到第k个孩子(交替进行k次dequeue()和k次enqueue()操作),并让她出队(dequeue())。如此不断迭代,直到队长(getSize())为 1。
public class Josephus {
//利用队列模拟Josephus环
public static Object josephus(Queue queue,int k){
if(queue.isEmpty()) return null;
while (queue.getSize()>1){
queue.Traversal();
for (int i = 0; i < k; i++) {
//将山芋向前传递k次
queue.enqueue(queue.dequeue());
}
Object e=queue.dequeue();//拿着山芋的孩子退出
System.out.println(e+"退出");
}
return queue.dequeue();//返回最后的赢家
}
public static Queue buildQueue(Object[] obs){
Queue queue=new QueueByArray();
for (int i = 0; i < obs.length; i++) {
queue.enqueue(obs[i]);
}
return queue;
}
public static void main(String[] args) {
String[] kid = {
"Alice", "Bob", "Cindy", "Doug", "Ed",
"Fred", "Gene", "Hope", "Irene", "Jack",
"Kim", "Lance", "Mike", "Nancy", "Ollie"};
System.out.println("最终的幸运者是" + josephus(buildQueue(kid), 5));
}
}