Java数据结构基础–循环队列与链队列
队列定义:
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
循环队列(基于数组)
完整代码:
public class Queue {
private int rear,front;//队首,队尾指针
private Object element[];//存储数组
private int maxSize;
private boolean IsEmpty(){//判空
return rear == front?true:false;
}
private boolean IsFull() {//判满
return (rear+1)%maxSize==front?true:false;
}
public int getSize() {//求队长
return (rear - front + maxSize)%maxSize;
}
public Queue(int maxSize ) {//构造函数
this.maxSize = maxSize+1;
this.front=0;
this.rear=0;
element = new Object[maxSize];
}
public boolean EnQueue(Object x) {//入队列
if(IsFull() == true) {return false;}
element[rear] = x;
rear = (rear + 1)%maxSize;
return true;
}
public boolean DeQueue() {//退队列
if(IsEmpty() == true) {return false;}
front = (front + 1)%maxSize;
return true;
}
public Object getFront() {//取队首
if(IsEmpty()) {return false;}
return element[front];
}
@Override
public String toString() {
String result = "";
for(int i = front;i != rear;i=(i+1)%maxSize) {
result += element[i] + "-->";
}
result += "null";
return result;
}
}
部分代码详解:
循环队列将数组头尾相连,形成一个闭合环,通过front和rear的在环内的移动实现入队列和退队列。
为了区分队列满和队列空,一般会空出一个位置不存数据。从而front=rear时队列空;rear+1=front为队列满。
判断队列是否已满
private boolean IsFull() {//判满
return (rear+1)%maxSize==front?true:false;
}
(1)(x%maxSize)是在0~maxSize-1之间循环的数字;
(2)原先判定条件为rear+1==front则为满,但是在多次入队列和退队列的过程后会出现下图情况。
(3)则rear+1=front就不能用了。我们应该使判断条件在0~maxSize循环起来,即(rear+1)%maxSize。如图rear=7时,(7+1)%8= =0,队列已满。
求队列长度getSize():
public int getSize() {//求队长
return (rear - front + maxSize)%maxSize;
}
此时分为两种情况:rear > front 和 rear < front:
(1)rear > front
(rear - front + maxSize)%maxSize式子实际上就变成了
(rear%maxSize - front%maxSize) + 0==》rear - front
(2)rear < front
(1)rear - front求出来的是负数,同时绝对值也是还未存数据的地方的长度(图中的rear-front=-2,而未存数据的地方时(2)和(3),长度为2)
(2)rear - front + maxSize:最大存储数-空余存储数=已存数据长度
(3)而%maxSize的就是使(rear - front + maxSize)中的maxSize在两种不同情况下取不同的值。
入队列与退队列:
在理解上面之后,入队和退队就变得更好理解了
public boolean EnQueue(Object x) {//入队列
if(IsFull() == true) {return false;}
element[rear] = x;
rear = (rear + 1)%maxSize;
return true;
}
(rear + 1)%maxSize就是为了防止下图情况,使rear + 1在0~7之间循环
public boolean DeQueue() {//退队列
if(IsEmpty() == true) {return false;}
front = (front + 1)%maxSize;
return true;
}
退队列也是同样的想法。
测试代码:
public class Main {
public static void main(String[] args) {
Queue qu = new Queue(6);
qu.EnQueue(1);
qu.EnQueue(2);
System.out.println(qu);
qu.DeQueue();
qu.EnQueue(3);
qu.EnQueue(4);
System.out.println(qu);
qu.DeQueue();
System.out.println(qu);
System.out.println(qu.getSize());
}
}
运行结果:
链式队列(基于链表):
public class Queue {
class Node{
private Object data;
private Node next;
public Node(Object data,Node next) {
this.data = data;
this.next = next;
}
public Node(Object x) {
this.data = x;
this.next = null;
}
}
private Node front,rear;//头指针,尾指针。
private boolean IsEmpty() {//判空
return front == null?true:false;
}
public boolean EnQueue(Object x) {//入队列
if(IsEmpty()) {
front = rear = new Node(x);
if(front == null) {return false;}
}else {
rear.next = new Node(x);
rear = rear.next;
if(rear == null) {return false;}
}
return true;
}
public boolean DeQueue() {//退队列
if(IsEmpty()) {return false;}
front = front.next;
return true;
}
public Object getFront() {//取队首
return front.data;
}
public int getSize() {//获取长度
int k =0;
Node p = front;
while(p!=null) {
p=p.next;
k++;
}
return k;
}
@Override
public String toString() {
String result = "";
Node p = front;
while(p != null) {
result += p.data + "-->";
p=p.next;
}
return result+"null";
}
}
在链队列中的rear和front不再是下标,而是两个Node节点。
判空:
private boolean IsEmpty() {//判空
return front == null?true:false;
}
只需要判断front是否为空,即头元素是否存在。
入队列与退队列:
public boolean EnQueue(Object x) {//入队列
if(IsEmpty()) {
front = rear = new Node(x);
if(front == null) {return false;}
}else {
rear.next = new Node(x);
rear = rear.next;
if(rear == null) {return false;}
}
return true;
}
(1)第一个条件判断:
front = rear = new Node(x)==》front与rear指向新节点
(2)else语句:
rear.next = new Node(x)= =》将rear.next指向新节点
rear = rear.next;= =》rear指针移动
(1)链队列与上面的循环队列的不同在于,链队列rear的指向是已经存数据的了;循环队列rear的指向是未存数据的。
(2)注意rear.next = new Node(x)得在rear = rear.next之前,得先开辟空间,在将指针移动。
public boolean DeQueue() {//退队列
if(IsEmpty()) {return false;}
front = front.next;
return true;
}
将front向后移动,改变队首即可。
求长度getSzie():
public int getSize() {//获取长度
int k =0;
Node p = front;
while(p!=null) {
p=p.next;
k++;
}
return k;
}
与之前的单链表相同,都是通过指针的后移计数,计算队列长度。
测试代码:
public class Main {
public static void main(String[] args) {
Queue qu = new Queue();
qu.EnQueue(1);
qu.EnQueue(2);
qu.EnQueue(3);
qu.EnQueue(4);
System.out.println(qu);
qu.DeQueue();
System.out.println(qu);
System.out.println(qu.getSize());
}
}
运行结果:
感谢你的阅读,欢迎留言。