Queue队列,Deque双端队列,循环队列

文章目录

    • Queue队列
    • 双端队列 (Deque)
    • 循环队列
    • 小结
  • 十一、其他文章接口
    • 1.String方法(重要,对于操作字符串有巨大的帮助)
    • 2.java常用的接口及其方法(包含拷贝,比较,排序,构造器)
    • 3.初阶数据结构
      • 3.1 顺序表:ArrayList
      • 3.2 链表:LinkedList
      • 3.3 栈:Stack
      • 3.4 队列:Queue
      • 3.5 二叉树:Tree
      • 3.6 优先级队列:PriorityQueue(堆排序)
      • 3.7 Map和Set
    • 4. 排序(7种方式)
      • 4.1 插入排序(两种)
      • 4.2 选择排序(两种)
      • 4.3 快速排序
      • 4.4 堆排序
      • 4.5 归并排序
    • 5.多线程
    • 6.网络编程
    • 7.HTML
    • 8.数据库Mysql

Queue队列

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出

入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

Queue队列,Deque双端队列,循环队列_第1张图片
队列中既然可以存储元素,那底层肯定要有能够保存元素的空间,通过我之前的文章了解到常见的空间类型有两种:顺序结构(ArrayList)和链式结构(LinkedList)(双向链表)。

单向队列自我实现

public class Mysqueue {
    static class Node{
        public int val;
        public Node next;
        public Node(int val){
            this.val=val;
        }

    }
    public Node head;
    public Node last;
    public int usedSize;
    //入栈
    public void offer(int val){
        Node node=new Node(val);
        if (head==null){
            head=node;
            last=node;
        }else {
            last.next=node;
            last=node;
        }
        usedSize++;
    }
    //出队
    public int poll(){
        if (!Empty()){
            throw new EmptyEixception("队列为空");
        }
        int ret= head.val;;
        head=head.next;

        //判断出一个节点,head和last都为空
        if (head==null){
            last=null;
        }
        usedSize--;
        return ret;
    }
    private boolean Empty(){
        return usedSize==0;
    }

    public int peek(){
        if (!Empty()){
            throw new EmptyEixception("队列为空");
        }
        return head.val;
    }

    public int getUsedSize(){
        return usedSize;
    }
}

双端队列 (Deque)

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

Queue队列,Deque双端队列,循环队列_第2张图片
双端对列的实现其实与单向队列的实现差别不大,主要是增加了头出队和尾入队的功能,无论对于用链表实现还是顺序表实现,都可以。

这里我用我继续用链表实现头的入队和出队:

//头入队
    public void offerFirst(int val){
        Node node=new Node(val);
        if (head==null){
            head=node;
            last=node;
        }else {
            last.next=node;
            last=node;
        }
        usedSize++;
    }
    //头出队
    public void pollFirst(){
        if (head==null){
            return;
        }else {
            head=head.next;
        }
        usedSize--;
    }

这里我用我继续用链表实现尾的入队和出队:

    public int pollLast(){
        if (!Empty()){
            throw new EmptyEixception("队列为空");
        }
        int ret= head.val;;
        head=head.next;

        //判断出一个节点,head和last都为空
        if (head==null){
            last=null;
        }
        usedSize--;
        return ret;
    }

    public void offerLast(int val){
        Node node=new Node(val);
        if (last==null){
            return ;
        }else {
            last.next=node;
            last=node;
        }
    }

循环队列

生产者消费者模型时可以就会使用循环队列,什么是生产与消费者模型呢?这个主要是在多线程中会有体现,这个模型可以理解为,一个生产猫罐头的车间,这个我称呼它为生产者,而猫就是这个罐头的消费者,问题来了,一个车间的生产效率是一定的,猫的数量看增长情况。而为了使效率最大化,一个循环的车间就必不可少。理解成数据结构中的循环队列就可。
Queue队列,Deque双端队列,循环队列_第3张图片
其实大家可以看见,对于此模型在队头和队尾中间有个空白格。这其实就涉及到循环队列的两种实现方法。而这个图片就是其中一个实现方法。这里我先不说,请看下面的。

首先我们先思考一个问题。需要用什么结构进行实现?队列的实现方式无非就是顺序表或者是链表。若是使用链表的话,我们只需要保证(尾节点)last.next存储(头结点)head的位置信息就可以保证相连。那么如果用顺序表又该如何实现呢?

如果使用顺序表,那么就要明确一个概念就是。如何确定结构中元素是满的?实际上在内存中,是没有这个环状的存储模型,这个只是我们需要设计成这样,我们需要用逻辑去将下面的顺序表用逻辑去实现上面的图。
Queue队列,Deque双端队列,循环队列_第4张图片
其实我们在这里就可以设置一个双指针针,一个指针保持在头的位置,还有一个指针随着填充的元素不断向后。很好,问题又出现了,假设整个数组已经满了,那么如果这个指针再往后面移动,那么就会爆出空指针异常,为了应对这个情况。此时就出现了两种方式,一种是通过取余的方式获取两个指针之间的位置。而这种方式呢,我们就需要用一个空余一个位置来防止。指针出现超出的情况。 还有一种呢,就是通过记录元素个数来判断。

这里呢,我们选择一种较为难的方式来进行实现,也就是取余的方式。其实取余呢,就是在循环。大家手中有纸笔的话,可以去尝试做一件事情就是假设的一个变量对四进行取值余,我们从1开始。到时结束我们会发现。其实在4这个范围内循环了两次半。
Queue队列,Deque双端队列,循环队列_第5张图片

视频:

循环队列添加元素

而删除的操作就是将last的位置向前移动,就可以将这个元素进行删除,如果要进行锁定删除,那么就需要去查找这个元素的所在位置。

循环队列的实现
这里我将我所实现的代码进行上传。

public class MyCircularQueue {
    private int[] elem;
    private int front;//队列的头
    private int rear;//队列的尾
    //浪费空间就得+1
    public MyCircularQueue(int k) {
        this.elem=new int[k+1];
    }
    //添加元素
    public boolean enQueue(int value) {
        if (isFull()){
            return false;
        }
        elem[rear]=value;
        rear=(rear+1)%elem.length;
        return true;

    }
    //删除头元素
    public boolean deQueue() {
        if (isEmpty()){
            return false;
        }
        front=(front+1)% elem.length;
        return true;
    }
    //得到头元素
    public int Front() {
        if (isEmpty()){
            return -1;
        }
        return elem[front];
    }
    //获取末尾元素
    public int Rear() {
        if (isEmpty()){
            return -1;
        }
        int index=(rear==0)?elem.length-1:rear-1;
        return elem[index];
    }

    public boolean isEmpty() {
        return front==rear;
    }
    //检查队列是否为满
    public boolean isFull() {
        if ((rear+1)%elem.length==front){
            return true;
        }
        return false;
    }
}

问题:

  1. 用什么结构进行构造?
  2. 无论是链表还是顺序表都是线性结构,如何让他们循环起来?
  3. 如何判断是否满了?

答:

  1. 链表或顺序表都可以,但这里我用的是顺序表
  2. 可以通过三种方式,
    第1种,通过取余的方式获取循环的位置。
    第2种,通过记录元素个数。来判断元素指针所达到的位置。
    第3种,通过标记位置来判断。
    这里我通过的是取余的方式获取循环位置。
  3. 通过两种方式。
    第1种。通过记录元素个数来达到数组容量。
    第2种就是。在设置的数组容量之上加一个空白来判断末尾。

小结

  1. 实际java中双端队列提供了两种。(常用),单向列的功能,双端都有。。。
    Deque stack = new ArrayDeque<>();//双端队列的线性实现
    Deque queue = new LinkedList<>();//双端队列的链式实现
  2. Deque的队列是实际上是接口,需要具体实例化
  3. 数据结构中最重要的是思路以及方法,具体实现功能其实在库中已有。所以学会这种处理模式才是终身受益。

十一、其他文章接口

1.String方法(重要,对于操作字符串有巨大的帮助)

文章链接

2.java常用的接口及其方法(包含拷贝,比较,排序,构造器)

文章链接

3.初阶数据结构

3.1 顺序表:ArrayList

文章链接

3.2 链表:LinkedList

文章链接

3.3 栈:Stack

文章链接

3.4 队列:Queue

文章链接

3.5 二叉树:Tree

文章链接

3.6 优先级队列:PriorityQueue(堆排序)

文章链接

3.7 Map和Set

HashMap和HashSet,TreeMap和TreeSet
文章链接

4. 排序(7种方式)

4.1 插入排序(两种)

4.2 选择排序(两种)

4.3 快速排序

4.4 堆排序

里面有堆排序的实现和逻辑
文章链接

4.5 归并排序

5.多线程

文章链接

6.网络编程

7.HTML

8.数据库Mysql

文章链接

你可能感兴趣的:(java,数据结构,算法)