队列--数组实现
可以仿照利用数组实现栈的过程,使用数组实现队列。以front指向队首元素,值始终为数组首元素a[0]。出队时,front保持不变,删除队首元素,其余元素依次向前移动,时间复杂度是O(n)。入队时,根据队列大小将元素存储到相应位置。上述实现因为不断移动元素,效率太低。因此以下使用环形数组的形式来构造队列。定义两个变量:
front:指向队首元素。
rear:指向队尾元素的下一个位置。
另外设环形数组大小为len。初始状态下,front=rear=0。入队时,在rear指向的位置存储元素,然后令rear=(rear+1)%len。出队时,删除front指向的元素,然后令front=(front+1)%len。最后,front=rear既是队满的判断条件又是队空的判断条件,不过可以完美解决。在入队方法enqueue中,size==CAPACITY,那么就是队满了。在出队方法中,如果size==0,那么即使队空了。不过这种方法会导致原有队首元素被覆盖(覆盖原队首元素不符合队列的设计初衷),可以通过禁止队列规模超过CAPACITY-1的方法来解决此问题。这样,只有队空的时候才有front=rear。使用这种方法利用环形队列实现了”烫手山芋“游戏。烫手山芋游戏规则:一群小孩围成一圈,在他们之间传递山芋。其中,一个小孩负责数数,每数一次,就把山芋转交给左边的邻居,从1开始数起,数到k时拿着山芋的孩子出列,然后重新从1开始说,从”淘汰“孩子的邻居开始重新传递山芋。实现代码如下:
首先给出Queue接口的代码:
1 /** 2 * Created by hfz on 2016/8/2. 3 */ 4 public interface Queue { 5 void enqueue(Object obj);//入队 6 Object dequeue();//出队并返回对首元素 7 int getSize();//队列元素个数 8 boolean isEmpty();//判断队列是否为空 9 Object front();//返回对首元素 10 }
然后是实现代码:
1 import java.util.Objects; 2 import java.util.Random; 3 4 /** 5 * Created by hfz on 2016/8/2. 6 */ 7 /* 8 可以仿照利用数组实现栈的过程,使用数组实现队列。以front指向队首元素,值始终为数组首元素a[0]。 9 出队时,front保持不变,删除队首元素,其余元素依次向前移动,时间复杂度是O(n)。 10 入队时,根据队列大小将元素存储到相应位置。 11 12 上述实现因为不断移动元素,效率太低。因此以下使用环形数组的形式来构造队列。 13 定义两个变量: 14 front:指向队首元素。 15 rear:指向队尾元素的下一个位置。 16 另外设环形数组大小为len 17 初始状态下,front=rear=0。入队时,在rear指向的位置存储元素,然后令rear=(rear+1)%len。 18 出队时,删除front指向的元素,然后令front=(front+1)%len。 19 最后,front=rear既是队满的判断条件又是队空的判断条件,不过可以完美解决。在入队方法enqueue中, 20 size==CAPACITY,那么就是队满了。在出队方法中,如果size==0,那么即使队空了。不过这种方法会导致原有队首元素被覆盖(覆盖原队首元素 21 不符合队列的设计初衷),可以通过禁止队列规模超过CAPACITY-1的方法来解决此问题。这样,只有队空的时候才有front=rear。 22 各种方法时间复杂度均是O(1) 23 */ 24 25 public class Queue_Array implements Queue { 26 private static int CAPACITY=40; 27 private int capacity=0; 28 private Object[] S; 29 private int front=0; 30 private int rear=0; 31 32 public Queue_Array(int capacity){ 33 this.capacity=capacity; 34 S=new Object[capacity]; 35 } 36 public Queue_Array(){ 37 this(CAPACITY); 38 } 39 public void enqueue(Object obj){ 40 if(getSize()==CAPACITY-1){//队满 41 throw new ExceptionQueueFull("队满,不能入队"); 42 } 43 else{//队未满,入队 44 try { 45 46 S[rear] = obj; 47 //front=(front+1)%CAPACITY;//队首指针加1,再对数组长度取模。 48 rear = (rear + 1) % CAPACITY;//队尾指针加1,再对数组长度取模。 49 } 50 catch (ArrayIndexOutOfBoundsException ex){ 51 System.out.println(); 52 } 53 } 54 55 } 56 public Object dequeue(){ 57 if(rear==front){ 58 throw new ExceptionQueueEmpty("队空,不能出队"); 59 } 60 else{ 61 Object obj=S[front]; 62 S[front]=null; 63 front=(front+1)%CAPACITY; 64 return obj; 65 } 66 } 67 68 public Object front(){ 69 if(rear==front){ 70 throw new ExceptionQueueEmpty("队空,没有队首元素"); 71 } 72 else{ 73 return S[front]; 74 } 75 } 76 public boolean isEmpty(){ 77 if(front==rear){ 78 return true; 79 } 80 else { 81 return false; 82 } 83 } 84 public int getSize(){ 85 return (CAPACITY+rear-front)%CAPACITY;//rear取模之后可能小于front,不能直接用(rear-front) 86 } 87 88 89 public static void main(String[] args){ 90 String[] children={"A","B","C","D","E","F","G","H","I","J","K"}; 91 Queue_Array queue=new Queue_Array(); 92 // int len=queue.capacity; 93 for(String s:children){ 94 queue.enqueue(s); 95 } 96 Random rand=new Random(); 97 Object dequeueObj; 98 int k=rand.nextInt(children.length-1)+1; 99 //k=4; 100 while(queue.getSize()!=1){ 101 for(int i=1;i<k;++i){ 102 dequeueObj=queue.dequeue(); 103 queue.enqueue(dequeueObj); 104 } 105 dequeueObj=queue.dequeue(); 106 System.out.println(String.format("%s 退出",(String)dequeueObj)); 107 } 108 System.out.println(String.format("when k is %s winner is %s ",k,queue.front())); 109 } 110 } 111 112 class ExceptionQueueEmpty extends RuntimeException { 113 public ExceptionQueueEmpty(String err){ 114 super(err); 115 } 116 } 117 class ExceptionQueueFull extends RuntimeException{ 118 public ExceptionQueueFull(String err){ 119 super(err); 120 } 121 }
其实,就烫手山芋游戏来说,使用如下的入队和出队方法更好:
在入队方法enqueue中,size==CAPACITY,那么就是队满了。在出队方法中,如果size==0,那么即是队空了。这种方法会导致原有队首元素被覆盖,正好符合题意,可以将队列大小设置为孩子个数。如果使用禁止队列规模超过CAPACITY-1的方法,队列大小的设置要大于孩子个数,否则入队时会提示“队列已满”。