目录
1)双端队列的介绍
2)双端队列用双链表的实现代码演示
3)双端队列用固定数组的实现
代码演示
视频
【算法讲解016【入门】双端队列-双链表和固定数组实现】
Leecode
leecode641 设计循环双端队列
1)双端队列的介绍
可以从头部进,可以从头部出;也可以从尾部进,可以从尾部出。这种结构叫双端队列
双向链表为了跳转容易,在数外边设置一个节点包着,前指针,后指针。
比如我之前有个a,头指空,尾指空。
我现在又加了个b,那么b的头指针指向a,尾指针指向空,尾巴来到b上
我再加个c,一样的。我又想在头部加个d,一样思路
所以头部加数 尾部加数就会了
头部弹出 尾部弹出怎么办?
头部弹出
头移到a,a的前指针指向null,c或c++的同学把d释放掉,java会自己释放不用管。
同样方法把a弹出。
想从尾部弹出呢?
尾巴向前跳一个,让b的next指针指向空。c或c++的同学把d释放掉,java会自己释放不用管。
2)双端队列用双链表的实现代码演示
package C16;import java.util.Deque;
import java.util.LinkedList;public class Video_016_Deque {class MyCircularDeque1{//Deque是接口 <Integer>是泛型,相当于Deque<Integer> 的意思就是:“这是一个双端队列,并且我给它贴上了**‘只能存放整数 (Integer)’**的标签。”public Deque<Integer> deque = new LinkedList<>();//“我要声明 (declare) 一个变量,它的名字叫 deque。这个变量是 public(公开)的,并且它的类型是一个贴着 Integer(整数)标签的 Deque(双端队列功能清单)。”public int size;public int limit;/*** 初始化一个容量为k的双端队列* @param k 队列的容量*/public MyCircularDeque1(int k){//“现在,我要创建一个真正能干活的工人(new LinkedList<>()),这个工人完全遵守了 Deque 的规范,然后让我的 deque 变量去管理(指向)这个工人。”deque = new LinkedList<>();//初始时,队列中没有元素size = 0;//设置容量上限limit = k;}/*** 将一个元素添加到队首,如果操作成功,返回true*/public boolean insertFront(int value) {//在插入前检查队列是否已满if(isFull()){return false;//满了,添加失败}//调用LinkList自带的方法offerFirst(),它会高效地将元素添加到链表头部deque.offerFirst(value);//元素数量加1size++;//插入成功return true;}/*** 将一个元素添加到队尾,如果操作成功返回true*/public boolean insertLast(int value){//同样先检查队列是否已经满了if(isFull()){return false;}//调用自带的方法offerLast(),它会高效地将元素添加到链表尾部deque.offerLast(value);size++;return true;}/*** 从队首删除一个元素,如果操作成功返回true*/public boolean deleteFront(){//跟之前一样的操作if(isEmpty()){return false;}deque.pollFirst();size--;return true;}public boolean deleteLast(){if(isEmpty()){return false;}deque.pollLast();size--;return true;}/*** 从队首获取元素,如果队列为空返回-1*/public int getFront(){if (isEmpty()) {return -1;}// 调用 LinkedList 自带的 peekFirst(),它只查看头部的元素,不移除。return deque.peekFirst();}/*** 获取队尾元素。如果队列为空,返回 -1。* @return 队尾的元素,或 -1。*/public int getRear() {if (isEmpty()) {return -1;}// 调用 LinkedList 自带的 peekLast(),它只查看尾部的元素,不移除。return deque.peekLast();}/*** 检查双端队列是否为空。* @return 为空返回 true,否则返回 false。*/public boolean isEmpty() {// 我们自己维护了 size 变量,用它来判断最简单。return size == 0;}/*** 检查双端队列是否已满。* @return 已满返回 true,否则返回 false。*/public boolean isFull() {// 当 size 达到容量上限时,队列就满了。return size == limit;}}
}
3)双端队列用固定数组的实现
用固定数组(动态也可以)
size,l,r
有没有数字或者满没满,size管。如果size等于0,那么l和r等于啥都没意义。
当加入数字a时,把a放在第零位,l到r位置上放a。即第零位到第零位放a。size变为1。
再加入b,r变为第一位。size再加1。
再加c
再加d。
头部再加个e。
怎么做?
l已经在第零位了,怎么办?
加到最后的位置上。
一共不是k位吗?这道题k是10,所以l来到第k-1位置上,即第9位。所以说总结头部添加的办法:
当l==0时,放在第k-1位,l=k-1;
当l≠0时,放在第l-1位,l--。
比如再在头部加个f所以现在从头部到尾部的就是890123这样的顺序。
如果再在头部加个g弹出呢?
从头部弹出,也就是l往右窜。即总结为:头部加数那么l往左窜,头部弹数则l往右窜。这就是l的逻辑。
模拟一下全过程
原来是这样
弹出g
弹出f
弹出e..
弹出a
弹出b
加入k加入s,加入p,加入l,加入n...
在尾部加入M
所以现在的整个队列就是cdkspcznm
从尾巴弹出总结一下:
从头部加入,l向左走,如果l==0,把数放在k-1的位置,然后赋值l=k-1;
如果l≠0,那么把数放在l-1的位置,然后赋值l--;
从头部出,首先给用户l位置的数[l],如果l==k-1,那么赋值l=0;
如果l≠k-1,那么赋值l++;
从尾部入,如果l==k-1,把数放在0的位置,然后赋值r=0;
如果r≠k-1,那么把数放在r+1的位置,然后赋值r++;
从尾出,首先给用户r位置的数[r],然后要往左走,所以如果r==0,那么r赋值为k-1,即第k-1位;
如果r≠0呢?从尾出那么就是r--就行了。
这就是所有的逻辑 环形双端队列
代码演示
public class MyCircularDeque2 {//底层的数据容器,一个固定大小的数组public int[] deque;//l:left/front指针,指向队头元素所在的索引//r:right/rear指针,指向队尾元素所在的索引//size:当前队列中的元素数量//limit:队列的容量上限(数组的大小)public int l,r,size,limit;/*** 构造方法:初始化一个容量为k的双端队列*/public MyCircularDeque2(int k){//创建一个能容纳k个整数的数组deque = new int[k];//初始时,所有指针和计数器都为0(或者一个无效状态)l = r = size = 0;//设置容量上限limit = k;}/*** 将一个元素添加到队首*/public boolean insertFront(int value){//先检查是否已经满if(isEmpty()){//队头和队尾指针都指向0号位置l = r = 0;//在0号位置放入新元素deque[0] = value;}else{//如果不为空,我们需要l往左移动,但如果它本来就在最左边,所以我们将它移动到limit-1位置上l = l ==0?(limit - 1):(l - 1);//在计算出的新位置l放入元素deque[1] = value;}//元素数量加1size++;return true;}/*** 将一个元素添加到队尾。*/public boolean insertLast(int value) {if (isFull()) {return false;}if (isEmpty()) {// 和 insertFront 一样,第一个元素 l 和 r 都指向 0l = r = 0;deque[0] = value;} else {// 移动 r 指针,为新元素在“后面”腾出位置// 这是另一个“循环”的关键!// r == limit - 1 ? 0 : (r + 1)// 意思是:// 如果 r 指针已经在末尾了 (limit - 1),那么它的“后一个”位置就是环绕到开头的 0。// 否则,它的后一个位置就是 r + 1。r = r == limit - 1 ? 0 : (r + 1);// 在计算出的新位置 r 放入元素deque[r] = value;}size++;return true;}/*** 从队首删除一个元素。*/public boolean deleteFront() {if (isEmpty()) {return false;}// 删除队首元素,我们只需要把 l 指针向“后”移动一位即可// 同样是循环移动// l == limit - 1 ? 0 : (l + 1)// 意思是:如果 l 在末尾,下一个位置是 0;否则是 l + 1l = l == limit - 1 ? 0 : (l + 1);// 元素数量减 1size--;return true;}/*** 从队尾删除一个元素。*/public boolean deleteLast() {if (isEmpty()) {return false;}// 删除队尾元素,我们只需要把 r 指针向“前”移动一位// 循环移动// r == 0 ? (limit - 1) : (r - 1)// 意思是:如果 r 在开头,前一个位置是 limit - 1;否则是 r - 1r = r == 0 ? (limit - 1) : (r - 1);size--;return true;}/*** 从队首获取元素。*/public int getFront() {if (isEmpty()) {return -1;}// 队首元素就是 l 指针指向的元素return deque[l];}/*** 获取队尾元素。*/public int getRear() {if (isEmpty()) {return -1;}// 队尾元素就是 r 指针指向的元素return deque[r];}public boolean isEmpty() {return size == 0;}public boolean isFull() {return size == limit;}}