当前位置: 首页 > news >正文

【数据结构和算法】5. 堆栈和队列

本文根据 数据结构和算法入门 视频记录

文章目录

  • 1. 堆栈(Stack)
    • 1.1 概念
    • 1.2 数组栈实现
    • 1.3 链式栈实现
  • 2. 队列(Queue)
    • 2.1 概念
    • 2.2 数组队列实现
    • 2.3 链式队列实现

在这一章我们来了解两个很特殊的数据结构:堆栈 (Stack) 和队列 (Queue)。这两个数据结构类似垃圾桶和队伍,栈是先进后出型,队列是先进先出型。

1. 堆栈(Stack)

1.1 概念

堆栈是一种常用的数据结构,这种数据结构的存储方式和垃圾桶一样,后面放进去的元素可以先取出来,而最早放入的元素会被压在最下面,最后才能被拿出来。我们也可以把栈的储存方式简单理解为堆盘子,后面加入的盘子会被堆到最上面,最早堆入的盘子在最下面:
在这里插入图片描述
所以栈是一种后进先出(Last In First Out)的数据结构,后入的元素先出,先入的元素后出。

堆栈主要支持以下两种操作:

  • 入栈(Push):将一个元素放入栈,用来加入数据。
  • 出栈(Pop):将一个元素弹出栈,用来删除数据。

还有以下两种辅助操作:

  • Peek:查看最顶部的元素。
  • isEmpty:查看栈是否为空。

在这里插入图片描述

1.2 数组栈实现

链表有两种实现方式,一种是数组,另一种是链表。数据的实现方式很简单,以下是数组栈的定义:

public class Stack {static final int CAPACITY = 1000;int top;int stack[];public Stack() {top = -1;stack = new int[CAPACITY];}
}

在数组栈中,我们使用数组来储存数据,其中包含一个CAPACITY来限制栈的容量,并使用指针top来记录最顶端元素的位置,top初始化为-1,代表数组栈没有任何元素。以下是push和pop方法的定义:

public boolean push(int val) {if (top >= (CAPACITY - 1)) {System.out.println("Stack Overflow.");return false;}stack[++top] = val;return true;
}public int pop() {if (top < 0) {System.out.println("Stack Underflow.");return 0;}int element = stack[top--];return element;
}

在push中,我们需要检查顶部元素是否达到容量限制,如果是,输出“溢出栈”错误。否则移动top指针,加入新的元素。在pop中,也要查看栈是否为空,如果是,那么输出“栈下溢”错误。否则将top指针减一。另外还有peek和isEmpty的实现:

public int peek() {if (top < 0) {System.out.println("Stack Underflow");return 0;}int element = stack[top];return element;
}public boolean isEmpty() {return top < 0;
}

peek也要查看栈是否为空,如果不是,直接返回top指向的元素。isEmpty只要查看top是否小于0即可。

1.3 链式栈实现

除了数组栈,我们也可以使用链表来实现栈,以下是链式栈的定义:

public class ListStack {static class StackNode {int val;StackNode next;StackNode(int val) {this.val = val;}}StackNode top;public ListStack() {top = null;}
}

在链式栈中,我们先定义节点StackNode,节点中包含数值和下一个节点的指针。在链式栈中,我们只需要记录top节点,在初始化时定义为null。以下是push和pop的定义:

public void push(int val) {StackNode newNode = new StackNode(val);if (top == null) {top = newNode;} else {StackNode temp = top;top = newNode;newNode.next = temp;}System.out.println(val + " is pushed to stack.");
}public int pop() {if (top == null) {System.out.println("Stack is Empty.");return Integer.MIN_VALUE;}int popped = top.val;top = top.next;return popped;
}

在push中,我们先创建新的节点newNode,如果栈为空,那么直接将newNode赋给top。如果不为空,就将新元素的下一节点指向当前的top,并将newNode更新为top节点。在pop中,也要先检查栈是否为空,不为空的话,记录下top的数据作为返回值,并将top更新为自己的下一个节点。以下是链式栈peek和isEmpty的定义:

public int peek() {if (top == null) {System.out.println("Stack is empty.");return Integer.MIN_VALUE;}return top.val;
}public boolean isEmpty() {return top == null;
}

在栈不为空的情况下,peek只需查看top的值即可,isEmpty也只要查看top是否是null就可以了。不管是用数组还是链表来实现栈,我们都只要处理头节点top,所以栈的所有操作都为O(1)。

2. 队列(Queue)

2.1 概念

队列是很好理解的一种数据结构,顾名思义,队列数据结构就和我们平时排队一样,先进入的元素先出,后进入的元素后出。队列的两端都是开的,一段负责插入新元素,另一端负责删除元素。

在这里插入图片描述
队列主要支持以下两种操作:

  • 入队(enqueue):增加一个新的元素
  • 出队(dequeue):删除一个元素

还支持其他辅助操作:

  • peek – 查看队列最前端的元素
  • isFull – 查看队列是否满了
  • isEmpty – 查看队列是否为空

2.2 数组队列实现

队列和堆栈一样,也可以使用两种实现方式,一种是使用数组,叫做顺序队列,另一种是使用链表实现,叫做链式队列。以下我们会先实现一种更常见的数组队列,叫做循环队列,它和基础的顺序队列相比较,更能有效地利用数组空间。以下是循环队列的定义:

public class ArrayQueue {int front, rear, size;int capacity;int array[];public ArrayQueue(int capacity) {this.capacity = capacity;front = rear = size = 0;array = new int[capacity];}
}

在循环队列中,我们需要capacity来限制队列的长度,并创建两个指针front和rear,front用来指向队列的头部,而rear指向队列的尾部。队列总是从头部取出元素,从尾部插入新元素,在操作队列时,我们只需要移动front和rear两个指针即可。我们还需要一个额外的size变量来记录元素的数量,front,rear和size都初始化为0 。

以下是enqueue和dequeue的定义:

public void enqueue(int item) {if (isFull()) return;array[rear] = item;rear = (rear + 1) % capacity;size++;System.out.println(item + " is enqueued.");
}public int dequeue() {if (isEmpty()) return Integer.MIN_VALUE;int item = array[front];front = (front + 1) % capacity;size --;return item;
}

在新元素入队的时候,我们需要先判断队列是否已满(isFull的代码在下一段)。如果未满,那么就把元素插入rear的位置,并将rear加1,并与capacity取模,然后增加size。在出队的时候,先要检查队列是否为空(isEmpty的代码在下一段),记录下删除元素的值后,我们将front指针增加1,与capacity取模,然后将size减少1。以下是辅助操作的定义:

public int peek() {if (isEmpty()) return Integer.MIN_VALUE;return array[front];
}public boolean isFull() {return size == capacity;
}public boolean isEmpty() {return size == 0;
}

peek只要查看front指针指向的值即可,isFull要检查size是否和容量capacity相同,isEmpty直接查看size是否等于0。

2.3 链式队列实现

用链表实现队列也很简单,和数组实现相似,也需要两个指针(front和rear)来实现。以下是ListQueue和QueueNode的定义:

public class ListQueue {QueueNode front;QueueNode rear;static class QueueNode {int value;QueueNode next;public QueueNode(int value) {this.value = value;}}
}

在链式队列中,我们需要定义节点QueueNode,QueueNode中含有两个值:一个是节点的数值value,另一个是指向下一个节点的next指针。在链式队列ListQueue中,我们只需要两个节点front和rear,front用来指向队列最前端的节点,而rear用来指向尾节点。以下是两个重要操作enqueue和deuque的实现:

public void enqueue(int value) {QueueNode newNode = new QueueNode(value);if (this.rear == null) { // Queue is emptythis.front = this.rear = newNode;return;}this.rear.next = newNode;this.rear = newNode;
}public int dequeue() {if (this.front == null) {System.out.println("The queue is empty.");return Integer.MIN_VALUE;}QueueNode frontNode = this.front;this.front = this.front.next;if (this.front == null) {this.rear = null;}return frontNode.value;
}

在enqueue中,我们先创建一个新的节点,如果队列为空,那么将头节点和尾节点同时指向新节点,结束操作。如果队列不为空,只要尾节点的next指针指向新节点,然后将尾节点指向新节点。在dequeue中,如果头节点front为空,直接返回默认数值。队列不为空的情况下,记录下front的数值作为返回值,并将头节点更新为下一节点。

相关文章:

  • 算法基础_数据结构【KMP + Trie 树 + 并查集 】
  • postgreSQL 如何使用 dblink
  • 微信小程序拖拽排序有效果图
  • 机器人进阶---视觉算法(六)傅里叶变换在图像处理中怎么用
  • 【Pytorch 中的扩散模型】去噪扩散概率模型(DDPM)的实现
  • Facebook商城开通全攻略:如何解决所在地区不可使用问题?
  • IPoIB驱动接收路径深度解析:从数据包到协议栈
  • 在Pytorch中使用Tensorboard可视化训练过程
  • 晨控CK-FR12与欧姆龙NX系列PLC配置EtherNet/IP通讯连接操作手册
  • Spring Boot多环境配置详解
  • GTS-400 系列运动控制器板(七)----修改限位开关触发电平
  • Cline Roo Code
  • 野外价值观:在真实世界的语言模型互动中发现并分析价值观
  • 【AI微信小程序开发】大转盘小程序项目代码:自设转盘选项和概率(含完整前端+后端代码)
  • docker 常见命令
  • Docker 数据卷
  • 基于 Vue 的Tiptap 富文本编辑器使用指南
  • vivado XMP使用
  • linux复习
  • 从M个元素中查找最小的N个元素时,使用大顶堆的效率比使用小顶堆更高,为什么?
  • 大家聊中国式现代化|刘亮:因地制宜发展新质生产力,推动经济高质量发展
  • 广电总局加快布局超高清视听产业链,多项成果亮相
  • 全国总工会成立100周年,工运历史和发展成就展将对外展出
  • 第八届进博会将致力于打造“五个高”,为展商增值赋能
  • 上海体彩中心2025年“五一”假期体彩销售及兑奖事宜通告
  • 文甦任四川乐山市委副书记,曾赴外交部挂职副司长