赞
踩
目录
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO的原则。
进栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据在栈顶。
假设S = ( a 1 , a 2 , . . . . a n ) S=(a1, a2,....an)S=(a1,a2,....an),则称a 1 a1a1为栈底元素,a n anan为栈顶元素。栈中元素按a 1 , a 2 , . . . a n a1,a2,...ana1,a2,...an的次序进栈,那么出栈的第一个元素应为栈顶元素。
如图:
栈在现实生活中的例子:
也是遵循后进先出,先进看不出原则
- import java.util.Stack;
-
- class Main {
- public static void main(String[] args) {
- Stack<Integer> stack = new Stack<>();
- stack.push(1);
- stack.push(2);
- stack.push(3);
- stack.push(4);
- stack.push(5); //插入5个元素, 1 2 3 4 5 栈顶是5, 栈底是1
- System.out.println(stack.size()); // 获取栈中有效元素个数---> 5
- System.out.println(stack.peek()); // 获取栈顶元素---> 5
- System.out.println(stack.pop()); // 5出栈,栈中剩余1 2 3 4,栈顶元素为4
- System.out.println(stack.pop()); // 4出栈,栈中剩余1 2 3 栈顶元素为3
-
- //为空则返回true 否则false
- if(stack.empty()){
- System.out.println("栈空");
- }else{
- System.out.println(stack.size()); //出了两个元素还有1 2 3----->3个
- }
- }
- }
从上图中可以看到,Stack继承了Vector,Vector和ArrayList类似,都是动态的顺序表,不同的是Vector是线程安全的。
- package Stack;
-
- import java.lang.reflect.Array;
- import java.util.Arrays;
- import java.util.Stack;
-
- public class myStack {
- public int[] arr; // 用于存储栈元素的数组
- public int usedSize; // 记录栈中已使用的元素个数
- public static final int MAX = 10; // 栈的最大容量
-
- // 构造函数,初始化数组
- public myStack(){
- arr = new int[MAX];
- }
-
- // 获取栈中已使用的元素个数
- public int size(){
- return usedSize;
- }
-
- // 判断栈是否已满,若满则扩容
- private void isFull(){
- if (usedSize == MAX) {
- arr = Arrays.copyOf(arr, arr.length * 2); // 扩容为原来的两倍
- }
- }
-
- // 打印栈中元素
- public void display(){
- for (int i = 0; i < usedSize; i++) {
- System.out.print(arr[i]+" ");
- }
- System.out.println();
- }
-
- // 入栈操作
- public void push(int data){
- // 判断是否栈满,若满则扩容
- isFull();
- arr[usedSize++] = data; // 将元素入栈
- }
-
- // 判断栈是否为空
- private boolean isEmpty(){
- if (usedSize == 0){
- return true;
- }
- return false;
- }
-
- // 出栈操作,获取并删除栈顶元素
- public int pop(){
- // 判断是否栈空 如果空了就没必要出栈了
- if (isEmpty()){
- throw new EmptyStackException("栈为空了!");
- }
- int ret = arr[usedSize-1]; // 获取栈顶元素
- usedSize--; // 栈中元素个数减一
- return ret; // 返回被删除的栈顶元素
- }
-
- // 获取栈顶元素 如果空了就不用返回栈顶元素了
- public int peek(){
- if (isEmpty()){
- throw new RuntimeException("栈为空了");
- }
- return arr[usedSize-1]; // 返回栈顶元素
- }
-
-
- // 获取栈中已使用的元素个数
- public int getUsedSize() {
- return usedSize;
- }
- }
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(FirstIn First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)
生活中体现出队列也挺常见的, 比如排队, 先来的先排在前面, 后来的排在后面
在Java中,Queue是个接口,底层是通过链表实现的。
- import java.util.LinkedList;
- import java.util.Queue;
- class Main {
- public static void main(String[] args) {
- Queue<Integer> q = new LinkedList<>();
- q.offer(1);
- q.offer(2);
- q.offer(3);
- q.offer(4); // 从队尾入队列 队头是1 队尾是4
-
- // 获取队列有效元素个数 -> 4
- System.out.println(q.size());
-
- // 获取队头元素 -> 1
- System.out.println(q.peek());
-
- // 从队头出队列,并将删除的元素返回 -> 1
- System.out.println(q.poll());
-
- //为空则返回true 否则false
- if (q.isEmpty()) {
- System.out.println("队列空");
- } else {
- System.out.println(q.size()); //前面执行了一次删除操作, 还剩下3个元素
- }
- }
注意:Queue是个接口,在实例化时必须实例化LinkedList的对象,因为LinkedList实现了Queue接口。
- package Queue;
-
- public class myQueue {
-
- static class ListNode{
- public int val;
- public ListNode next;
-
- public ListNode(int val){
- this.val = val;
- }
- }
- public int usedSize; // 已使用的队列元素个数
- public ListNode head; // 队列头部节点
- public ListNode tail; // 队列尾部节点
-
- // 入队操作
- public void offer(int data){
- ListNode node = new ListNode(data); // 创建新节点
- if (head == null){ // 如果队列为空
- tail = node;
- head = node;
- }else {
- tail.next = node; // 将新节点连接到队列尾部
- tail = tail.next; // 更新尾部节点
- }
- }
-
- // 出队操作
- public int poll(){
- if (head == null){ // 如果队列为空
- return -1; // 返回-1表示出队失败
- }
- ListNode cur = head; // 记录当前头部节点
- head = head.next; // 头部指针后移
- return cur.val; // 返回出队的元素值
- }
-
- // 获取队首元素
- public int peek(){
- if (head == null){ // 如果队列为空
- return -1; // 返回-1表示队列为空
- }
- return head.val; // 返回队首元素的值
- }
-
- // 打印队列中元素
- public void display(){
- ListNode cur = head;
- while (cur != null){ // 遍历队列
- System.out.print(cur.val+" "); // 打印节点值
- cur = cur.next; // 指针后移
- }
- System.out.println();
- }
-
- // 获取队列中元素个数
- public int size(){
- ListNode cur = head;
- int len = 0;
- while (cur != null){ // 遍历队列
- cur = cur.next; // 指针后移
- len++; // 元素个数加一
- }
- return len; // 返回队列中元素个数
- }
- }
当我们使用队列这种基本的数据结构时,很容易发现,随着入队和出队操作的不断进行,队列的数据区域不断地偏向队尾方向移动。当我们的队尾指针指向了队列之外的区域时,我们就不能再进行入队操作了,而此时由于队头已经偏离原来位置,也就意味着实际上队列并没有满。这种实际上还有空间,但是却发生了溢出的现象就称为假溢出现象
什么是假溢出现象
假设有以下队列,初始元素为1、2、3、4、队头元素是1,队尾元素就4
当我们弹出队头元素两次得到队列:
这就是假溢出, 可以看出还有多余的空间, 但是当队尾入一个元素发现满了
如何解决这个问题?
为了避免假溢出问题,我们需要对队列进行优化---循环队列, 形如如图
循环队列的性质
数组实现
(rear+1)% len == front
循环队列题
622. 设计循环队列编辑https://leetcode.cn/problems/design-circular-queue/
循环队列代码实现:
- package Queue;
-
- import java.util.Arrays;
-
- public class CircleQueue {
-
- public int[] arr; // 数组存储队列元素
- public int usedSize; // 已使用的队列元素个数
- public int font; // 队列头部指针
- public int rear; // 队列尾部指针
- public static final int MAX = 5; // 队列最大容量
-
- public CircleQueue(){
- arr = new int[MAX]; // 初始化数组
- }
-
- // 打印队列中元素
- public void display(){
- for (int i = font; i < arr.length-1; i++) {
- System.out.print(arr[i]+" "); // 打印队列元素
- }
- System.out.println();
- }
-
- // 判断队列是否已满
- private boolean isFull(){
- if ((rear + 1) % MAX == font){ // 判断队列是否已满
- return true;
- }
- return false;
- }
-
- // 入队操作
- public boolean offer(int data){
- if(isFull()){ // 如果队列已满
- return false; // 返回入队失败
- }
- arr[rear] = data; // 将元素放入队列尾部
- rear = (rear + 1) % arr.length; // 更新队列尾部指针
- usedSize++; // 更新队列元素个数
- return true; // 返回入队成功
- }
-
- // 判断队列是否为空
- private boolean isEmpty(){
- if (font == rear){ // 判断队列是否为空
- return true;
- }
- return false;
- }
-
- // 出队操作
- public boolean poll(){
- if (isEmpty()){ // 如果队列为空
- return false; // 返回出队失败
- }
- font = (font+1) % arr.length; // 头部指针后移
- usedSize--; // 更新队列元素个数
- return true; // 返回出队成功
- }
-
- // 获取队头元素
- public int Front(){
- if(isEmpty()){ // 如果队列为空
- return -1; // 返回-1表示队列为空
- }
- int ret = arr[font]; // 获取队头元素
- return ret; // 返回队头元素的值
- }
-
- // 获取队尾元素
- public int Rear(){
- if (isEmpty()){ // 如果队列为空
- return -1; // 返回-1表示队列为空
- }
- int ret = (rear == 0) ? arr.length-1 : rear - 1; // 计算队尾元素的下标
- return arr[ret]; // 返回队尾元素的值
- }
-
- // 获取队列元素个数
- public int size() {
- return usedSize; // 返回队列元素个数
- }
-
- }
栈(Stack)和队列(Queue)是两种常见的数据结构,它们在数据存储和访问的方式上有一些重要的区别。
栈(Stack)是一种后进先出(LIFO,Last In First Out)的数据结构,类似于一摞盘子,最后放入的盘子最先取出。栈的操作包括入栈(push)和出栈(pop),只能在栈顶进行操作,不支持随机访问。栈常用于实现函数调用、表达式求值、括号匹配等场景。
队列(Queue)是一种先进先出(FIFO,First In First Out)的数据结构,类似于排队等候的过程,先到先得。队列的操作包括入队(offer)和出队(poll),元素从队列的前端出队,从队列的后端入队。队列常用于实现广度优先搜索、任务调度等场景。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。