class 013 队列和栈-链表、数组实现

这篇文章是看了“左程云”老师在b站上的讲解之后写的, 自己感觉已经能理解这个题目了, 所以就将整个过程写下来了。

这个是“左程云”老师个人空间的b站的链接, 数据结构与算法讲的很好很好, 希望大家可以多多支持左程云老师, 真心推荐.
https://space.bilibili.com/8888480?spm_id_from=333.999.0.0

顺便提供一下测试链接:循环队列:
https://leetcode.cn/problems/design-circular-queue/

在这里插入图片描述

1. 栈和队列的介绍与实现

栈和队列都可以使用 链表数组 进行实现.

1.1 队列的逻辑实现

队列就相当于是进行排队:从后面进, 肯定是到了最前面了, 然后弹出的时候, 从前面出. (先进先出).

在这里插入图片描述

1.1.1 队列用链表实现

  1. 可以先设置一个“头指针”, “尾指针”, 在进行实现的时候, 进来一个数据, 将这个数据包装成“链表”的形式,
    1. 第一个数据传入的时候, 将“头指针”和“尾指针”同时指向第一个数据,
    2. 然后后面数据加入的时候, 移动“尾指针”, 将“尾指针”移动队列的尾部.
    3. 弹出一个数据的时候, 就修改“头指针”, 将“头指针”向后移动.

1.1.2 队列用数组实现

在我们做题的时候, 题目中的数据量一般都是确定的, 所以我们可以直接用数组实现, 只要是加入数据的总次数不超过 n 就可以进行设置.
比如:题目中说明给的数据量是 5000, 那我们的数组就干脆直接设置为:arr[5000].

  1. 设置一个左边界和右边界:l 和 r, 此时l = r = 0. 里面有数据的条件:l < r, 若是没有数据说明:l == r.
    1. 若是进入一个数据, 那么就将 r++:queue[r++] = num,
    2. 若是弹出一个数据, 那么就将 l++:return queue[l++], 将 lr 的位置靠近.

在这里插入图片描述

1.1.3 代码实例

数组 的方式实现队列.

// 实际刷题时更常见的写法,常数时间好  
// 如果可以确定加入操作的总次数不超过n,那么可以用  
// 一般笔试、面试都会有一个明确数据量,所以这是最常用的方式
public static class Queue2 {  
  
    public int[] queue;  
    public int l;  
    public int r;  
  
    // 加入操作的总次数上限是多少,一定要明确  
    public Queue2(int n) {  
       queue = new int[n];  
       l = 0;  
       r = 0;  
    }  
  
    // 调用任何方法之前,先调用这个方法来判断队列内是否有东西  
    public boolean isEmpty() {  
       return l == r;  
    }  
  
    // 插入一个数据  
    public void offer(int num) {  
       queue[r++] = num;  
    }  
  
    // 弹出一个数据  
    public int poll() {  
       return queue[l++];  
    }  
    // ?  
    // l...r-1 r    // [l..r)    
    public int head() {  
       return queue[l];  
    }  
  
    public int tail() {  
       return queue[r - 1];  // 因为是 r 是数据加进来的位置, 所以头位置是 r - 1.
    }  
  
    public int size() {  
       return r - l;        // 队列的大小是“r - l”.
    }  
  
}

1.2 栈的逻辑实现

栈就像是图片上的形式, 只能从最顶处放入, 也只能从最顶处拿出. 所以栈的结构是:
后进先出.
在这里插入图片描述

1.2.1 栈使用数组实现

在做题的时候, 数据量是确定的, 保证同时在栈里的元素个数不会超过 n 就可以使用
数组 实现.

  1. 先设置一个数组 arr[], 设置一个 size变量:size = 0 然后放入数据
    1. 放入数据的时候, 放到 size 的位置, 然后将 size++.
    2. 弹出数据的时候, 弹出 size - 1, 然后将 size--.
      1. 而且弹出数据之后也是不需要进行清理的, 因为后续放入数据的时候会直接将原来残留的数据覆盖掉.

1.2.2 代码实例

// 实际刷题时更常见的写法,常数时间好  
// 如果可以保证同时在栈里的元素个数不会超过n,那么可以用  
// 也就是发生弹出操作之后,空间可以复用  
// 一般笔试、面试都会有一个明确数据量,所以这是最常用的方式  
public static class Stack2 {  
  
    public int[] stack;  
    public int size;  
  
    // 同时在栈里的元素个数不会超过n  
    public Stack2(int n) {  
       stack = new int[n];  
       size = 0;  
    }  
  
    // 调用任何方法之前,先调用这个方法来判断栈内是否有东西  
    public boolean isEmpty() {  
       return size == 0;  
    }  
  
    public void push(int num) {  
       stack[size++] = num;  
    }  
  
    public int pop() {  
       return stack[--size];  
    }  
  
    public int peek() {  
       return stack[size - 1];  
    }  
  
    public int size() {  
       return size;  
    }  
  
}

2. 环形队列的实现

2.1 环形队列的逻辑结构

环形队列的意思就是:设置一个数组:arr[], 然后将数据进行放入, 假设数组的长度是:5, 放入数据, 等到放满的时候, 将已经使用过的值弹出, 然后可以继续将数据进行放入.

那这样来看, 放入数据的时候, 只要同时在这个数组内的数据量不超过 5, 就能一直存放数据.

在这里插入图片描述

2.2 代码实例

题目难点:边界的位置不好扣, 经历一系列的加入和弹出, 右边移动一个位置, 左边移动一个位置, 这样就会麻烦, 头尾一直在不断的相互追赶, 这就会非常的麻烦, 所以需要一个简单的方法, 比如引入一个 变量, 将头尾进行“解耦”.

解释:

  1. 首先和正常队列一样设置 l 和 r, l = r = 0, 继续设置一个 size = 0, size 表示队列的大小, limit 是对应的队列的限制(最大的长度).
  2. 开始对队列进行操作, (并且要判断其正确性)
    1. 若是将数据放入队列(放入尾部): r++, 此时队列也扩大的, 所以:size++. 若是到了边界:limit - 1 就返回 0, 只要满足 size < limit 就一定正确. (因为这说明这个队列中还有位置).
    2. 若是将数据弹出队列(弹出头部):l++, 此时队列缩小, 所以:size--. 若是到了边界:limit - 1 就返回 0 只要满足 size > 0 一定正确. (因为说明这个队列存在可以弹出的数据).
    3. 这样就利用了 size 这个变量实现了“头尾”的解耦性. 不用管“头尾”相互追了.

这个方法的实现, 自己随便用几个数据, 然后实现下面代码中的几个主要方法就行了, 尝试一遍就会明白如何利用 size 实现了“头尾”的解耦性

在这里插入图片描述

MyCircularQueue {  
  
    public int[] queue;  
  
    public int l, r, size, limit;  
  
    // 同时在队列里的数字个数,不要超过k  
    public MyCircularQueue(int k) {  
       queue = new int[k];  
       l = r = size = 0;  
       limit = k;  
    }  
  
    // 如果队列满了,什么也不做,返回false  
    // 如果队列没满,加入value,返回true  
    public boolean enQueue(int value) {  
       if (isFull()) {  
          return false;  
       } else {  
          queue[r] = value;  
          // r++, 结束了,跳回0  
          r = r == limit - 1 ? 0 : (r + 1);  
          size++;  
          return true;  
       }  
    }  
  
    // 如果队列空了,什么也不做,返回false  
    // 如果队列没空,弹出头部的数字,返回true  
    public boolean deQueue() {  
       if (isEmpty()) {  
          return false;  
       } else {  
          // l++, 结束了,跳回0  
          l = l == limit - 1 ? 0 : (l + 1);     
          size--;  
          return true;  
       }  
    }  
  
    // 返回队列头部的数字(不弹出),如果没有数返回-1  
    public int Front() {  
       if (isEmpty()) {  
          return -1;  
       } else {  
          return queue[l];      
       }  
    }  
  
    public int Rear() {  
       if (isEmpty()) {  
          return -1;  
       } else {                      // 看原来的位置在哪里? 若是 r != 0, 那就是 r - 1 位置是尾部,  
          int last = r == 0 ? (limit - 1) : (r - 1);   //若是 r == 0, 那就是 limit - 1 位置  
          return queue[last];  
       }   
}  
  
    public boolean isEmpty() {  
       return size == 0;  
    }  
  
    public boolean isFull() {  
       return size == limit;  
    }  
  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值