队列、循环队列和双端队列

目录

1、队列

1.1 概念

2.2 队列的使用

 2.3 队列模拟实现

2、循环队列

2.1 循环队列的认识 

2.2 设计循环队列 

 3. 双端队列 (Deque)


1、队列

1.1 概念

队列 :只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out)。
入队列:进行插入操作的一端称为 队尾( Tail/Rear
出队列:进行删除操作的一端称为 队头 ( Head/Front

队列、循环队列和双端队列_第1张图片 

2.2 队列的使用

Java 中, Queue 是个接口,底层是通过双向链表实现

队列、循环队列和双端队列_第2张图片 

队列的一些方法如下:

方法
功能
boolean offer(E e)
入队列
E poll()
出队列
peek() 获取队头元素
int size()
获取队列中有效元素个数
boolean isEmpty() 检测队列是否为空

注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。 

举例: 

public static void main(String[] args) {
    Queue q = new LinkedList<>();
    q.offer(1);
    q.offer(2);
    q.offer(3);
    q.offer(4);
    q.offer(5); // 从队尾入队列
    System.out.println(q.size());
    System.out.println(q.peek()); // 获取队头元素
    q.poll();
    System.out.println(q.poll()); // 从队头出队列,并将删除的元素返回
    if(q.isEmpty()){
        System.out.println("队列空");
    }else{
        System.out.println(q.size());
    }
}

 2.3 队列模拟实现

首先,我们定义一个MyLinkQueue类,队列的底层是由双向链表实现的,所以在类中我们定义一个节点内部类,代码如下:

public class MyLinkQueue {
    static class ListNode {
        public int val;
        public ListNode prev;
        public ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }
    public ListNode head;
    public ListNode last;
    public int usedSize;  //队列中元素个数
} 

接下来是队列相关操作的模拟实现,以下方法均是 MyLinkQueue类的成员方法。

入队列:

直接在链表的结尾加入一个节点即可。

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;
    }

出队列:

有以下三种情况: 

1. 队列为空
2. 队列中只有一个元素----链表中只有一个节点---直接删除
3. 队列中有多个元素---链表中有多个节点----将第一个节点删除
public int poll() {
        if (head == null) {
            return -1;
        }
        int retval = head.val;
        if (head.next == null) {
            head = null;
            last = null;
            return retval;
        }
        head = head.next;
        head.prev = null;
        usedSize--;
        return retval;
    }

获取队头元素:

 public int peek() {
        if (head == null) {
            return -1;
        }
        return head.val;
    }

获取队列中有效元素个数: 

 public int size() {
        return usedSize;
    }

检测队列是否为空:

public boolean empty() {
        return head == null;
    } 

 

2、循环队列

2.1 循环队列的认识 

队列、循环队列和双端队列_第3张图片 

循环队列其实是由数组实现的 

队列头:front  队列尾:rear 

rear 是当前可以存放元素的下标,若rear在下标 7 的位置,如何来到下标 0 的位置呢?

通过 rear = (rear+1)%len (len为数组的长度)式子即可实现。

对于循环队列,我们如何区分是空是满呢,有以下方法: 

  • 通过添加 size 属性记录
  • 保留一个位置(浪费一个空间表示满,浪费的空间不放元素)
  • 使用标记

队列、循环队列和双端队列_第4张图片 

保留一个位置(浪费一个空间表示满,浪费的空间不放元素)的方法,则:

循环队列为空的条件:rear = front ;

循环队列为满的条件: (rear+1)%len = front;

 

2.2 设计循环队列 

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 

class MyCircularQueue {
    public int[] elem;
    public int front;
    public int rear;
    public MyCircularQueue(int k) {
        elem = new int[k];
    }

    //进入队列
    public boolean enQueue(int value) {
        if (isFull()) {  //首先要判断队列是否满
            return false;
        }
        elem[rear] = value;  //不满,直接在rear位置插入元素
        rear = (rear+1)%elem.length;  //rear指向下一个位置
        return true;
    }

    //出队列
    public boolean deQueue() {
        if (isEmpty()) {  //首先要判断队列是否为空
            return false;
        }
        front = (front+1)%elem.length;  //将队头front指向下一个位置
        return true;
    }

    //得到队头元素
    public int Front() {
        if (isEmpty()) {
            return -1;
        }
        return elem[front];
    }

    //得到队尾元素
    public int Rear() {
        if (isEmpty()) {
            return -1;
        }
        //在这里我们要判断一下rear此时的位置
        //若rear在下标为 0 的位置,则返回index = elem.length-1位置的元素
        //若不在下标为 0 的位置,则返回index = rear-1位置的元素
        int index = (rear == 0)?elem.length-1:rear-1;
        return elem[index];
    }

    public boolean isEmpty() {
        return front==rear;
    }

    public boolean isFull() {
        return front == (rear+1)%elem.length;
    }
}

 3. 双端队列 (Deque)

双端队列( deque )是指允许两端都可以进行入队和出队操作的队列, deque “double ended queue” 的简称。
那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

队列、循环队列和双端队列_第5张图片 

Deque是一个接口,使用时必须创建LinkedList的对象。  

队列、循环队列和双端队列_第6张图片 

在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口。

Deque stack = new ArrayDeque<>();// 双端队列的线性实现
Deque queue = new LinkedList<>();// 双端队列的链式实现

 

你可能感兴趣的:(Java-数据结构,java,数据结构,leetcode)