队列
队列是只能在一端进行插入,一端进行删除的线性表。允许插入的一端称为队尾,允许删除的一端为队头。跟我们平常排队买东西是一样的,后来者要插入时只能排在队尾,买完东西的人是从队头出去的(删除)。因为队列中队尾和队头都可以进行操作,所以定义两个指针,front指针指向队头元素,rear指针指向队尾元素的下一个位置。
既然队列也是线性表,那么也分顺序存储结构和链式存储结构。在队列的顺序存储结构中,在队头进行删除元素的操作,在队尾插入元素,可能导致分配的数组空间浪费很大一部分内存,很不明智,因此出现了循环队列。循环队列是指队列头尾相接的顺序存储结构。当队列为空时,front=rear;当队列满时,数组中还有一个空闲单元。
当rear>front时,也就是队尾指针指向的数组下标大于队头指针指向的数组下标,此时队列的长度为rear-front;当rear<front时,此时队列的长度为rear-front+QueSize;所以队列的长度的通用计算公式为(rear-front+QueSize)%QueSize。
一.顺序存储结构
1.队列的结构定义
代码实现:
class SqQueue{ int maxSize=8; Object[] array=new Object[maxSize]; int front; int rear; }
2.初始化一个空循环队列
代码实现:
public static void initQueue(SqQueue sq){ sq.front=0; sq.rear=0; }
3.计算队列的长度
代码实现:
public static int calculateLength(SqQueue sq){ int length=(sq.rear-sq.front+sq.maxSize)%sq.maxSize; return length; }
4.循环队列的入队列操作
算法思路:
1. 判断队列是否满,队列已满的判定条件为队列中还有一个空闲单元,%QueSize是考虑到队列的最后一个位置,如果直接rear+1则指针溢出队列,而求余则将指针指向队头第一个位置;
2. 队尾(即rear指针指向的位置)插入元素;
3. rear指针向后移一位,%QueSize原因如1;
代码实现:
public static void EnQueue(SqQueue sq,Object e){ //判断队列是否满 if((sq.rear+1)%sq.maxSize==sq.front){ System.out.println("队列已满"); }else{ //队尾插入元素e sq.array[sq.rear]=e; //队尾指针向后移一位 sq.rear=(sq.rear+1)%sq.maxSize; } }
5.循环队列的出队列操作
算法思路:
1. 判断队列是否为空,队列为空的判定条件为rear==front;
2. front指针后移一位;
代码实现:
public static void DeQueue(SqQueue sq){ //判断队列是否为空队列 if(sq.rear==sq.front){ System.out.println("队列为空"); }else{ //队头元素赋值给e Object e=sq.array[sq.front]; //指针后移一位 sq.front=(sq.front+1)%sq.maxSize; } }
完整代码:
package com.java.Queue; import java.util.Arrays; public class QueueTest { static SqQueue sq=new SqQueue(); public static void main(String[] args){ EnQueue(sq,"A"); EnQueue(sq,"B"); EnQueue(sq,"C"); EnQueue(sq,"D"); DeQueue(sq); DeQueue(sq); System.out.println(sq); System.out.println(calculateLength(sq)); } //循环队列的出队列操作 public static void DeQueue(SqQueue sq){ //判断队列是否为空队列 if(sq.rear==sq.front){ System.out.println("队列为空"); }else{ //队头元素赋值给e Object e=sq.array[sq.front]; sq.array[sq.front]=null; //指针后移一位 sq.front=(sq.front+1)%sq.maxSize; } } //循环队列的入队列操作 public static void EnQueue(SqQueue sq,Object e){ //判断队列是否满 if((sq.rear+1)%sq.maxSize==sq.front){ System.out.println("队列已满"); }else{ //队尾插入元素e sq.array[sq.rear]=e; //队尾指针向后移一位 sq.rear=(sq.rear+1)%sq.maxSize; } } //计算循环队列的长度 public static int calculateLength(SqQueue sq){ int length=(sq.rear-sq.front+sq.maxSize)%sq.maxSize; return length; } //初始化一个空循环队列 public static void initQueue(SqQueue sq){ sq.front=0; sq.rear=0; } } class SqQueue{ int maxSize=8; Object[] array=new Object[maxSize]; int front; int rear; @Override public String toString() { return "SqQueue [maxSize=" + maxSize + ", array=" + Arrays.toString(array) + ", front=" + front + ", rear=" + rear + "]"; } }
二.链式存储结构
队列的链式存储结构和单链表差不多,只不过之只能在队尾插入元素在队头删除元素。还有一个区别是链式存储结构中队头指针指向的是队列的头结点,队尾指针指向队尾结点;而在顺序存储结构中,队头指针指向队列的队头元素,而队尾指针指向队尾元素的下一结点。队列的链式存储结构也是不需要事先分配内存空间的。
1.链式队列结点
class QNode{ Object data; QNode next; public QNode(Object data,QNode next){ this.data=data; this.next=next; } @Override public String toString() { return "QNode [data=" + data + ", next=" + next + "]"; } }
2.初试化空队列,空队列时front和rear都指向头结点
class LinkQueue{ static QNode front=new QNode("头结点",null); QNode rear=front; }
3.入队操作(尾进)
1. 将新结点赋值给队尾指针的下一个元素;
2. 将队尾指针重新指向新结点;
代码实现:
public static void EnLinkQueue(LinkQueue lq,QNode node){ lq.rear.next=node; lq.rear=node; }
4.出队操作(头出)
1. 先判断队列栈中是否有元素;
2. 若非空栈,头结点后移一位;
3. 如果头结点之后的结点为要出栈的元素,rear指向front;
代码实现:
public static void DeLinkQueue(LinkQueue lq){ //先判断队列栈中是否有元素 if(lq.front==lq.rear){ System.out.println("空栈"); }else{ //要出栈的元素 QNode p=lq.front.next; //头结点后移一位 lq.front.next=p.next; } //如果头结点之后的结点为要出栈的元素,rear指向front if(lq.rear==lq.front.next){ lq.rear=lq.front; } }
完整代码:
package com.java.Queue; public class LinkQueueTest { static QNode node=new QNode("A",null); static QNode node1=new QNode("B",null); static LinkQueue lq=new LinkQueue(); public static void main(String[] args){ EnLinkQueue(lq,node); System.out.println(lq.rear); EnLinkQueue(lq,node1); System.out.println(lq.rear); DeLinkQueue(lq); System.out.println(lq.front.next); } //出队操作(头出) public static void DeLinkQueue(LinkQueue lq){ //先判断队列栈中是否有元素 if(lq.front==lq.rear){ System.out.println("空栈"); }else{ //要出栈的元素 QNode p=lq.front.next; //头结点后移一位 lq.front.next=p.next; } //如果头结点之后的结点为要出栈的元素,rear指向front if(lq.rear==lq.front.next){ lq.rear=lq.front; } } //入队操作(尾进) public static void EnLinkQueue(LinkQueue lq,QNode node){ lq.rear.next=node; lq.rear=node; } } //初始化空队列,空队列时front和rear都指向头结点 class LinkQueue{ static QNode front=new QNode("头结点",null); QNode rear=front; } class QNode{ Object data; QNode next; public QNode(Object data,QNode next){ this.data=data; this.next=next; } @Override public String toString() { return "QNode [data=" + data + ", next=" + next + "]"; } }