本篇博客大纲:
本篇会用到理论篇的只是点,理论介绍可以点击队列理论篇查看
思维导图:
队列的基本功能:
模拟实现:
链式结构:
完整代码可以参考后面的代码或者通过我的gietee队列链式结构模拟实现(点击显示连接)
队列的链式结构是双链表,由于每个结点都是单独的一个类,所以定义为内部类;内部类中有记录数值域和两个记录前后结点的指针域,多个结点连接成如下图。
首先创建 MyLinkQueue 队列的基础要素
成员变量和内部类
其中head和last都是要代表每个结点的,类型是内部类ListNode的类型
next和prev初始化并不知道应该指向哪里,所以构造方法时先不构造
usedSized用来记录队列中元素的个数
public class MyLinkQueue {
static class ListNode {//内部类
public int val;//内部类的数值域
public ListNode next;内部类的指针域
public ListNode prev;
public ListNode(int val) {//构造方法
this.val = val;
}
}
public ListNode head;
public ListNode last;
public int usedSize;
}
offer()方法的实现:
思路流程:
public boolean offer(int val){
ListNode node = new ListNode(val);
if(head == null){
head = node;
last = node;
}else {
last.next = node;
node.prev = last;
last = last.next;
}
usedSize++;
return true;
}
poll()方法的实现:
poll是返回头结点的val并删除,所以重点在于删除之前head.val要提前记录,以免head = head.next后找不到前面的结点
思路流程:
public int poll(){
//空结点
if(head == null){
return -1;
}
//单结点
int ratVal = head.val;
if(head.next == null){
head = null;
last = null;
return ratVal;
}
//多节点
head = head.next;
head.prev = null;
usedSize--;
return ratVal;
}
peek()方法的实现:
思路:如果不是空链表,那么只需要返回head.next就是队头的值
public int peek(){
if(head == null){
return -1;
}
return head.val;
}
size()方法的实现:
public int size(){
return usedSize;
}
isEmpty方法的实现:
只需要将头节点置为null那么即使时多个结点,head不再引用后面的结点,后面的结点的内存就会一个接着一个被收回
public boolean Empty(){
return head == null;
}
完整代码:
public class MyLinkQueue {
static class ListNode {
public int val;
public ListNode next;
public ListNode prev;
public ListNode(int val) {
this.val = val;
}
}
public ListNode head;
public ListNode last;
public int usedSize;
public boolean offer(int val){
ListNode node = new ListNode(val);
if(head == null){
head = node;
last = node;
}else {
last.next = node;
node.prev = last;
last = last.next;
}
usedSize++;
return true;
}
public int poll(){
//空结点
if(head == null){
return -1;
}
//单结点
int ratVal = head.val;
if(head.next == null){
head = null;
last = null;
return ratVal;
}
//多节点
head = head.next;
head.prev = null;
usedSize--;
return ratVal;
}
public int peek(){
if(head == null){
return -1;
}
return head.val;
}
public boolean Empty(){
return head == null;
}
public int size(){
return usedSize;
}
}
顺序结构:
(LeetCode题点设计循环队列击显示链接)
顺序结构完整代码需要的请查看码云:我的码云
首先需要创建基本要素:
数组、front和rear,之后再对方法进行填充
在这里我们使用的是利用rear = (rear + 1)% elem.length;的公式记录下标的,所以有一个空间的浪费
如果想创建k大小的数组,那么就需要k+1
public MyCircularQueue(int k) {
elem = new int[k+1];
}
front和rear定义为int型,是因为它充当的是数组的下标
class MyCircularQueue {
int[] elem;
public int front;//队头
public int rear;//队尾
public MyCircularQueue(int k) {
elem = new int[k+1];//因为这种方法浪费了一个位置,所以要提前加上一个,有效的位置才是k个
}
}
因为无论是入队 or 出队 or 获取 都需要判断队列是满还是空,才能继续入队和出队操作,所以需要先把isEmpty和isFull给构造了
isEmpty()和isFull()的构造:
isEmpty:
当数组为空的时候,front和rear指向的下标相同(不一定时零下标,因为循环队列队头和队尾在哪里都有可能为空)
public boolean isEmpty() {
if(front == rear){
return true;
}
return false;
//优化 return front == rear;
}
isFull:
当满的时候,rear再往下走就与front重合,所以需要利用公式(rear + 1)% elem.length (rear往下走的下一个下标)== front;判断是不是相等
public boolean isFull() {
if ((rear + 1) % elem.length == front){
return true;
}
return false;
//return (rear + 1) % elem.length == front;//优化
}
代码中都有优化后的代码。
enQueue():
rear是当前可以存放的数据元素的下标,如图
先判断满没满,没满就赋值,并且rear = (rear + 1)%elem.length往下走一步
public boolean enQueue(int value) {
//判断是不是满的
if(isFull()){
return false;
}
elem[rear] = value;
rear = (rear + 1) % elem.length;
return true;
}
deQueue():
思路:先判断是不是空,不是空front就往下走一步,但原来的front下标的数据元素不用管,因为当入队的时候rear走到原有的front下标时,赋值之后就会被覆盖了 ,如图:
public boolean deQueue() {
//先判断是不是空的
if(isEmpty()){
return false;
}
front = (front + 1) % elem.length;
return true;
}
Front():
获取队头元素,但不删除。
front就是队头的数据元素的下标,返回front下标下的值就可以了
public int Front() {
//判断是不是空的
if(isEmpty()){
return -1;
}
return elem[front];
}
Rear():
获取队尾的数据元素。
难点在于rear是当前可以存放数据元素的下标,要找到前一个位置如果rear不在下标为0的位置,只需要rear-1即可,但是如果rear下标为0,那么rear-1就越界了
所以当rear下标为零时,只需要elem.length -1 就可以获取前一个数据元素的下标,如图
public int Rear() {
//先判断是不是空
if(isEmpty()){
return -1;
}
int index = (rear == 0) ? elem.length -1 : rear -1;
return elem[index];
}
下节预告:
LeetCode题目:
用栈实现队列
用队列实现栈