队列和栈

一、定义和概念

顺序队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

队列特点:先进先出

三种溢出现象:

(1)下溢:队列为空,出队,正常。可用作条件逻辑判断

(2)真上溢:队列满,入队,异常,需要避免

(3)假上溢:队列实际不满,但由于对头指针只增不减,空间无法重复利用,导致虚满,无法正常入队,可通过循环队列解决

循环队列

循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。

在循环队列中,当队列为空时,有 front=rear,而当所有队列空间全占满时,也有 front=rear。为了区别这两种情况,规定循环队列最多只能有 MaxSize-1 个队列元素,当循环队列中只剩下一个空存储单元时,队列就已经满了。因此,队列判空的条件是 front=rear,而队列判满的条件是 front = (rear+1)%MaxSize

(1)a,b,c,d,e 入队

(2)a,b 出队,对头指针指向 c

(3)假设队列 maxSize=6,插入 e 之后就出现假上溢,这时候 f 要入队,由于 a,b 元素已经出队位置空闲,所以 f 插入存储空间的第一个位置,将 f 设置为队尾。依次循环能避免假上溢的情况出现,从而将队列循环装满。

空对接判断条件:front = rear

满队列判断条件:(rear + 1) % MAXSIZE = front

为什么判断队列是否满的条件是 front = (rear+1)%MaxSize?

(1)正常情况判满条件是 rear+1=front

(2)有一种特殊情况,队列满了之后 rear+1=0,所以当队列满足了一个 maxSize 的轮回的时候会就归 0,所以此处需要根据 maxSize 取余,即 (rear+1)%MaxSize = front

栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。

栈是允许在同一端进行插入和删除操作的特殊线性表。允许进行插入和删除操作的一端称为栈顶(top),另一端为栈底(bottom);栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈。插入一般称为进栈(PUSH),删除则称为出栈/退栈(POP)。栈也称为先进后出表。

栈空的条件:因为指针从 0 开始,所以栈满的条件为 top == -1

栈满的条件:因为指针从 0 开始,所以栈满的条件为 top==maxsize-1

溢出现象:

(1)下溢:栈为空,出栈,正常。可用作条件逻辑判断

(2)真上溢:栈满,入队,异常,需要避免,不存在跟队列类似的假上溢的情况。

堆栈的基本特点:

(1)先入后出,后入先出。

(2)除头尾节点之外,每个元素有一个前驱,一个后继。

二、算法实现

循环队列

定义数组存储元素,定义队头指针和队尾指针

1、数组大小定义为元素个数 +1

2、队列判空:front == rear

3、队列判满:front == (rear + 1) % maxSize

4、入队:当前队尾指针指向的空间存储元素,队尾指针 +1

5、出队:返回当前队头元素,队头指针 +1

csharp 复制代码
import java.util.Arrays;
import java.util.stream.Collectors;
​
public class CircleQueue {
​
  /**
   * 数组长度
   */
  private int maxSize;
​
  /**
   * 队头指针
   */
  private int front;
​
  /**
   * 队尾指针
   */
  private int rear;
​
  private String[] queue;
​
  /**
   * 初始化循环队列
   * @param objSize 元素个数
   */
  public CircleQueue(int objSize) {
    // 循环队列为了区分空队列和满队列,所以多预留一个空元素空间,这里的maxSize比元素个数多1
    this.maxSize = objSize + 1;
    this.front = 0;
    this.rear = 0;
    this.queue = new String[this.maxSize];
  }
​
  /**
   * 队列是否空
   * @return
   */
  public boolean isEmpty() {
    return front == rear;
  }
​
  /**
   * 队列是否满
   * @return
   */
  public boolean isFull() {
    return front == (rear+1) % maxSize;
  }
​
  /**
   * 入队
   * @param a 入队元素
   */
  public void add(String a) {
    if (isFull()) {
      System.out.println("队列满");
      return;
    }
    queue[rear%maxSize] = a;
    rear = (rear+1)%maxSize;
  }
​
  /**
   * 出队
   * @return 出队元素
   */
  public String remove() {
    if (isEmpty()) {
      System.out.println("队列空");
      return null;
    }
    String a = queue[front%maxSize];
    queue[front%maxSize] = null;
    front = (front+1)%maxSize;
    return a;
  }
​
  public static void main(String[] args) {
    // 模拟一个4个元素大小队列的入队出队情况
    // a,b,c入队;【正常,元素a,b,c,front=0,rear=3】
    // a出队;【正常,元素b,c,front=1,rear=3】
    // d,e入队;【正常,元素b,c,d,e front=1,rear=0】// 循环队列
    // f入队;【异常,队列满】
    // b,c,d,e出队【正常,队列空 front=0,rear=0】
​
    CircleQueue circleQueue = new CircleQueue(4);
​
    System.out.print("a,b,c入队:");
    circleQueue.add("a");
    circleQueue.add("b");
    circleQueue.add("c");
    System.out.println(Arrays.stream(circleQueue.queue).collect(Collectors.toList()) + "front=" + circleQueue.front + ";rear" + circleQueue.rear);
​
    System.out.print("a出队:");
    String remove1 = circleQueue.remove();
    System.out.println(remove1 + ";" + Arrays.stream(circleQueue.queue).collect(Collectors.toList()) + "front=" + circleQueue.front + ";rear" + circleQueue.rear);
​
    System.out.print("d,e入队:");
    circleQueue.add("d");
    circleQueue.add("e");
    System.out.println(Arrays.stream(circleQueue.queue).collect(Collectors.toList()) + "front=" + circleQueue.front + ";rear" + circleQueue.rear);
​
    System.out.print("f入队:");
    circleQueue.add("f");
    System.out.println(Arrays.stream(circleQueue.queue).collect(Collectors.toList()) + "front=" + circleQueue.front + ";rear" + circleQueue.rear);
​
    System.out.print("b,c,d,e出队:");
    String remove2 = circleQueue.remove();
    String remove3 = circleQueue.remove();
    String remove4 = circleQueue.remove();
    String remove5 = circleQueue.remove();
    System.out.println(remove2+","+remove3+","+remove4+","+remove5+","+";" + Arrays.stream(circleQueue.queue).collect(Collectors.toList()) + "front=" + circleQueue.front + "rear" + circleQueue.rear);
  }
}

执行结果:

顺序栈

定义数组存储元素,定义栈顶指针

1、数组大小定义为元素个数

2、栈判空:top == -1

3、栈判满:top == maxSize -1

4、入栈:当前栈顶指针 +1,栈顶指针指向的空间存储元素

5、出栈:返回当前栈顶指针指向的元素,栈顶指针 -1

arduino 复制代码
package cn.gov.zcy.announcement;
​
import java.util.Arrays;
import java.util.stream.Collectors;
​
public class Stack {
​
  // 数组长度
  private int maxSize;
​
  /**
   * 栈顶指针
   */
  private int top;
​
  private String[] stack;
​
  /**
   * 初始化栈
   * @param objSize 元素个数
   */
  public Stack(int objSize) {
    maxSize = objSize;
    top = -1;
    stack = new String[maxSize];
  }
​
  /**
   * 判断栈是否空
   * @return
   */
  public boolean isEmpty() {
    return top == -1;
  }
​
  /**
   * 判断栈是否满
   * @return
   */
  public boolean isFull() {
    return top == maxSize-1;
  }
​
  /**
   * 入栈
   * @param a 入栈元素
   */
  public void push(String a) {
    if (isFull()) {
      System.out.println("栈满");
      return;
    }
    top = top + 1;
    stack[top] = a;
  }
​
  /**
   * 出栈
   * @return 出栈元素
   */
  public String pop() {
    if (isEmpty()) {
      System.out.println("栈空");
      return null;
    }
    String a = stack[top];
    top = top - 1;
    return a;
  }
​
  public static void main(String[] args) {
    // 模拟一个4个元素大小栈的入栈和出栈的情况
    // a,b,c,d入栈【正常,元素a,b,c,d,top=3】
    // e入栈【异常,栈满】
    // d,c,b,a出栈【正常,出栈顺序d,c,b,a,top=-1】
    // 出栈【异常,栈空】
    Stack test = new Stack(4);
​
    System.out.print("a,b,c,d入栈:");
    test.push("a");
    test.push("b");
    test.push("c");
    test.push("d");
    System.out.println(Arrays.stream(test.stack).collect(Collectors.toList()) + ";top=" + test.top);
​
    System.out.print("e入栈:");
    test.push("e");
    System.out.println(Arrays.stream(test.stack).collect(Collectors.toList()) + ";top=" + test.top);
​
    System.out.print("d,c,b,a出栈:");
    String pop1 = test.pop();
    String pop2 = test.pop();
    String pop3 = test.pop();
    String pop4 = test.pop();
    System.out.println(pop1 + "," + pop2 + "," + pop3 + "," + pop4 + "," + ";top=" + test.top);
​
    System.out.print("空栈出栈:");
    String pop5 = test.pop();
    System.out.println(pop5 + ";top=" + test.top);
}
​
}

执行结果:

队列思想应用实践

应用背景

一批已经发布的公告数据需要推送,且推送时间点需要满足发布后 10 分钟

队列实现介质:数据库表

队列实现先进先出:按照修改时间正序排序

入队:插入数据库表

出队:删除数据库表

重新入队:更新修改时间,通过重新入队可以解决已经被处理过并且处理异常的数据可以轮到后续的定时任务中处理

总结

队列和栈的定义和概念都比较简单,但队列和栈的思想都经过包装了各种介质被广泛应用。

参考文献

1.《百度百科》

推荐阅读

ASM 字节码增强

浅谈表单受控性及结合Hooks应用

Mybatis一级缓存问题

MySQL死锁浅析

探索Taro:跨平台开发的实践与原理

招贤纳士

政采云技术团队(Zero),Base 杭州,一个富有激情和技术匠心精神的成长型团队。规模 500 人左右,在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。

如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊......如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 zcy-tc@cai-inc.com

微信公众号

文章同步发布,政采云技术团队公众号,欢迎关注

相关推荐
kingwebo'sZone2 小时前
ASP.net WebAPI 上传图片实例(保存显示随机文件名)
后端·asp.net
桑榆肖物2 小时前
一个简单的ASP.NET 一致性返回工具库
后端·asp.net
组态软件5 小时前
web组态软件
前端·后端·物联网·编辑器·html
Peter_chq5 小时前
【计算机网络】多路转接之select
linux·c语言·开发语言·网络·c++·后端·select
cnsxjean8 小时前
SpringBoot集成Minio实现上传凭证、分片上传、秒传和断点续传
java·前端·spring boot·分布式·后端·中间件·架构
kingbal9 小时前
SpringCloud:Injection of resource dependencies failed
后端·spring·spring cloud
刘天远9 小时前
django实现paypal订阅记录
后端·python·django
ℳ₯㎕ddzོꦿ࿐9 小时前
Spring Boot集成MyBatis-Plus:自定义拦截器实现动态表名切换
spring boot·后端·mybatis
逸风尊者10 小时前
开发也能看懂的大模型:RNN
java·后端·算法
小钟不想敲代码11 小时前
第4章 Spring Boot自动配置
java·spring boot·后端