《数据结构:从0到1》-10-双端队列与单调队列

双端队列与单调队列

如果说普通队列是单行道,只准进不准插队(FIFO),那么双端队列(Deque),就是一个双向通道,前后都能走人!而基于它诞生的单调队列(Monotonic Queue),作用也不容小视,能够解决很多经典难题。废话不多说,我们发车!在这里插入图片描述

一、 为什么需要双端队列?

1. 普通队列的局限

  • 就像食堂排队,只能从队尾进从队头出
  • 想临时让队尾的人离开?或者让一个VIP插队到前面?对不起,规则不允许。

2. 双端队列的诞生

  • 允许在队头队尾进行插入删除
  • 既能当队列用,也能当栈用。

以Web浏览器历史记录为例:

  • offerLast():你访问新页面,加到历史记录尾部。
  • pollLast():你点“后退”,从尾部移除当前页面。
  • pollFirst():历史记录满了,自动删除最老的记录。

二、 双端队列(Deque)

1. 双端队列是什么?一张图看懂!

双端队列是一种允许在线性表的两端进行插入和删除操作的数据结构。

操作全家福:

操作方向 操作类型 方法名示例 描述
队头(Head) 插入 addFirst(e), offerFirst(e) 在队伍最前面加人
删除 removeFirst(), pollFirst() 队伍最前面的人离开
查看 getFirst(), peekFirst() 看看队伍最前面是谁
队尾(Tail) 插入 addLast(e), offerLast(e) 在队伍最后面加人
删除 removeLast(), pollLast() 队伍最后面的人离开
查看 getLast(), peekLast() 看看队伍最后面是谁
2. 底层实现:为什么选择双向链表?

理由:

  • 单向链表:只能从头走到尾,想操作尾部需要O(n)时间
  • 双向链表:每个节点都知道它的前驱和后继,头尾操作都是O(1)

双向链表节点结构:

class ListNode {
   
   
    int val;        // 存储的值
    ListNode prev;  // 指向前一个节点
    ListNode next;  // 指向后一个节点
    
    public ListNode(int val) {
   
   
        this.val = val;
    }
}
3. 核心实现:带哨兵节点的双向链表

为什么要用哨兵节点?

  • 消除边界判断的麻烦!
  • 永远有head和tail节点,代码更简介

初始化状态:

head哨兵
tail哨兵

初始时,head的next指向tail,tail的prev指向head

代码实现核心部分:

public class LinkedListDeque {
   
   
    // 哨兵节点
    private ListNode head, tail;  
    private int size;
    
    public LinkedListDeque() {
   
   
        // 初始化两个哨兵节点,并互相连接
        head = new ListNode(-1);
        tail = new ListNode(-1);
        head.next = tail;
        tail.prev = head;
        size = 0;
    }
    
    /**
     * 队头插入 - 四步连接法
     */
    public void offerFirst(int val) {
   
   
        ListNode newNode = new ListNode(val);
        ListNode firstRealNode = head.next; 
        
        // 第一步:head -> newNode
        head.next = newNode;
        newNode.prev = head;
        
        // 第二步:newNode -> firstRealNode  
        newNode.next = firstRealNode;
        firstRealNode.prev = newNode;
        
        size++;
    }
    
    /**
     * 队尾插入 - 镜像操作
     */
    public void offerLast(int val) {
   
   
        ListNode newNode = new ListNode(val);
        // 当前最后一个真实节点
        ListNode lastRealNode = tail.prev; 
        
        // 第一步:lastRealNode -> newNode
        lastRealNode.next = newNode;
        newNode.prev = lastRealNode;
        
        // 第二步:newNode -> tail
        newNode.next = tail;
        tail.prev = newNode;
        
        size++;
    }
    
    /**
     * 队头删除
     */
    public int pollFirst() {
   
   
        if (isEmpty()) throw new RuntimeException("Deque is empty");
        
        ListNode firstNode = head.next;
        ListNode secondNode = firstNode.next;
        
        // head直接指向第二个节点
        head.next = secondNode;
        secondNode.prev = head;
        
        // 清理引用
        firstNode.prev = null;
        firstNode.next = null;
        
        size--;
        return firstNode.val;
    }
}

offerFirst操作流程如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QuantumLeap丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值