【数据结构与算法】1.2 队列

队列:是一个特殊的线性表,遵循先进先出(FIFO)原则,不允许插队。在front端(队头)进行删除操作,在表的rear端(队尾)进行插入操作。和栈一样,队列是操作受限制的线性表。

队列的特点:

a. 线性表:数组/链表

b. FIFO

队列的分类:

顺序(单向)队列——Queue:只能在一段插入数据,另一端删除数据;

循环(双向)队列——Deque:每一段都可以进行插入数据和删除数据的操作。

                      

优先队列:按优先级进行插入的队列——在插入数据的时候,进行排序

阻塞队列:在队列的基础上增加了阻塞操作,即当队列为空时,从队头取数据会被阻塞,直到队列中有数据时;当队列已满时,往队列插数据会被阻塞,直到队列中有空闲位置。(实现时涉及多线程,锁等概念)

 

队列的基本操作:

入队enquue(),放一个数据到队列尾部;

出队dequeue(),从队列头部取一个元素

队列的实现方式:顺序队列&链式

实践:使用数组实现一个单向队列

public class ArrayQueue {

    private int data[]; //数据
    private int head = 0;//队头
    private int rear = 0;//队尾
    private int queueLength = 0;    //队列的大小

    //构造函数
    public ArrayQueue(int queueLength){
        data = new int[queueLength];
        this.queueLength = queueLength;
    }

    public void enqueue(int m){
        if (isFull()){  //队列已满
//            return;       //如果队列满了什么都不做,在队列数据几进几出以后,队列的利用率会下降
            //优化:当队列满了,且head!=0时,进行队列移动,腾出空间
            //时间复杂度:最好情况下是O(1),最差情况下是O(n)
            if(head >0){
                int[] temp = new int[queueLength];
                for(int i=0;i<queueLength;i++){
                    temp[i]=data[head+i];
                }
                data=temp;
            }else{
                return;
            }
        }else{
            data[rear] = m;
            rear++;
        }
    }

    public int dequeue(){
        if(isEmpty()){
            return -1;
        }else{
            int pop = data[head];   //先进先出
            head++;
            return  pop;
        }
    }

    public boolean isFull(){
        return rear == queueLength;
    }

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

}

单向队列的空间利用率问题,导致需要在队列已满时进行移动队列腾出多余空间;

因此循环队列的优势展示了出来。

Q:循环队列怎么判断队列已满?

A:a. 增加属性 size; 表示当前已经存储了n个数据

      b. (rear + 1)%n == head;        //为什么不是rear+1 == head:当队列刚开始head还是0,但是rear已经是n-1的时候,等式就不相等了

Q:循环队列怎么判断队列为空?

A:head == rear

public class CircleArrayQueue {

    private int data[]; //数据
    private int head = 0;//队头
    private int rear = 0;//队尾
    private int queueLength = 0;    //队列的大小
    private int size = 0;   //实际存储的数据大小

    //构造函数
    public CircleArrayQueue(int queueLength){
        data = new int[queueLength];
        this.queueLength = queueLength;
    }

    public void enqueue(int m){
        if (isFull()){  //队列已满
            return;
        }else{
            data[rear] = m;
            rear = (rear+1) % queueLength;  //循环队列的rear不能无限制增长
        }
    }

    public int dequeue(){
        if(isEmpty()){
            return -1;
        }else{
            int pop = data[head];   //先进先出
            head = (head + 1) % queueLength;//循环队列的head不能无限制增长
            return  pop;
        }
    }
    //循环队列是否已满
    public boolean isFull(){
        return (rear +1) % queueLength == head;
    }
    //循环队列是否为空
    public boolean isEmpty(){
        return head == rear;
    }

}

Q:线程池里当任务满时,此时又来一个新任务,线程池是如何处理的?具体有哪些策略?这些策略又是如何实现的?

A:a. 排队:阻塞队列。

          若在公平的情况下,即是FIFO。同时分为无限的队列(例如链表,容易出现OOM问题eg: LinkedBlockingQueue),和有限的队列(例如数组,Interger.MAX ≈ 21亿,在小型系统中,预估请求量的情况下,使用)。

      b. 丢弃:不处理数据,直接抛出

阻塞队列的应用:

eg:数据同步系统的架构:将大量异源的数据进行汇总。在数据入Kafka之后,使用阻塞队列实现“队列的限容”,使系统处理更稳定,最后汇总到数据仓库BI System中

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值