java 双端队列的实现_Java实现自定义双端队列(链表和数组两种方式)

前面写过单端队列,文章链接。

提示:自定义实现的集合框架源码在这里。

单端队列有数组和链表两种实现方式。数组的实现方式相对比链表复杂一些,需要理解数学关系。

接下来,是时候实现双端队列了。

简单介绍

单端队列只能从一端插入元素,另一端取出元素。而双端队列则可以在两端均可以插入和删除元素。

双端队列可以充当单端队列,也可以用于充当栈。

在Java中,LinkedList的内部使用双端链表队列原理实现,而ArrayList的内部使用双端数组队列原理实现。

基本方法

双端队列接口Deque和Queue以及Stack中对应的方法如下:

398e855f5b0281661851d6b07a766783.png

数组实现

数组实现的方法和单端队列时实现的方式差不多,都是规定了一个head和tail。

head用来表示头元素的位置,或者翻译官方API的话是即将删除和查看的那个元素。

tail用来表示尾元素的下一个位置,或者翻译官方API的话是元素即将被插入的位置。

在这里需要注意的是和单端队列的实现方式不同的是这里核心的数学表达式采用的是逻辑与操作,以此来确定元素的操作位置。

首先数组的初始容量capacity__必须是2的指数倍__,可以是8、16,但不能是10或者20。

以tail为例,在add方法中,使用的数学表达式如下:1tail = (tail + 1) & (capacity - 1);

这样的效果和下面的表达式是等价的:1tail = (tail + 1) % capacity;

这是为什么呢?

因为capacity为2的指数倍,比如8。

其二进制的表示为1000,那么capacity - 1为7,对应的二进制的表示为0111,其低位均为1。

假设tail是4,那么4&7就为4;若tail是7,那么tail+1是8,而8&7为0,则tail下一个插入的位置是0,结果是正确的。

如果是head进行addFirst操作的话,那么就是head-1,head-1可能是负数,使用(head-1)&(capacity-1)可以解决负数的问题。

如果是使用求模符号,则为(head+capacity-1)%capacity。

使用逻辑运算的速度一般快于四则运算的速度,所以使用逻辑与运算操作是更好的。

链表实现

使用链表实现,不同与单端队列,链表节点需要同时记录前一个节点和后一个节点。

单端队列的实现,我采用的做法是头结点只是起到引导作用,牵住整个链表,没有实际的存储意义,这样操作起来会比较方便。

而双端队列所有的节点都是有意义的。

在双端队列的增删改的操作中,需要仔细考虑被操作节点与前后节点(即使没有)的关系。

源码下载

源码我放在了[这里],这是自己实现Java集合框架的一部分。欢迎提出宝贵意见。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * 基于双向链表实现双端队列结构 */ package dsa; public class Deque_DLNode implements Deque { protected DLNode header;//指向头节点(哨兵) protected DLNode trailer;//指向尾节点(哨兵) protected int size;//队列中元素的数目 //构造函数 public Deque_DLNode() { header = new DLNode(); trailer = new DLNode(); header.setNext(trailer); trailer.setPrev(header); size = 0; } //返回队列中元素数目 public int getSize() { return size; } //判断队列是否为空 public boolean isEmpty() { return (0 == size) ? true : false; } //取首元素(但不删除) public Object first() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); return header.getNext().getElem(); } //取末元素(但不删除) public Object last() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); return trailer.getPrev().getElem(); } //在队列前端插入新节点 public void insertFirst(Object obj) { DLNode second = header.getNext(); DLNode first = new DLNode(obj, header, second); second.setPrev(first); header.setNext(first); size++; } //在队列后端插入新节点 public void insertLast(Object obj) { DLNode second = trailer.getPrev(); DLNode first = new DLNode(obj, second, trailer); second.setNext(first); trailer.setPrev(first); size++; } //删除首节点 public Object removeFirst() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); DLNode first = header.getNext(); DLNode second = first.getNext(); Object obj = first.getElem(); header.setNext(second); second.setPrev(header); size--; return(obj); } //删除末节点 public Object removeLast() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); DLNode first = trailer.getPrev(); DLNode second = first.getPrev(); Object obj = first.getElem(); trailer.setPrev(second); second.setNext(trailer); size--; return(obj); } //遍历 public void Traversal() { DLNode p = header.getNext(); while (p != trailer) { System.out.print(p.getElem()+" "); p = p.getNex

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值