队列题目:设计循环双端队列

题目

标题和出处

标题:设计循环双端队列

出处:641. 设计循环双端队列

难度

6 级

题目描述

要求

设计你的循环双端队列实现。

实现 MyCircularDeque \texttt{MyCircularDeque} MyCircularDeque 类:

  • MyCircularDeque(k) \texttt{MyCircularDeque(k)} MyCircularDeque(k) 初始化对象,将循环双端队列的大小设为 k \texttt{k} k
  • boolean   insertFront(int   value) \texttt{boolean insertFront(int value)} boolean insertFront(int value) 向循环双端队列的队首插入一个元素。如果操作成功则返回 true \texttt{true} true,否则返回 false \texttt{false} false
  • boolean   insertLast(int   value) \texttt{boolean insertLast(int value)} boolean insertLast(int value) 向循环双端队列的队尾插入一个元素。如果操作成功则返回 true \texttt{true} true,否则返回 false \texttt{false} false
  • boolean   deleteFront() \texttt{boolean deleteFront()} boolean deleteFront() 从循环双端队列的队首删除一个元素。如果操作成功则返回 true \texttt{true} true,否则返回 false \texttt{false} false
  • boolean   deleteLast() \texttt{boolean deleteLast()} boolean deleteLast() 从循环双端队列的队尾删除一个元素。如果操作成功则返回 true \texttt{true} true,否则返回 false \texttt{false} false
  • int   getFront() \texttt{int getFront()} int getFront() 获取队首元素。如果双端队列为空,返回 -1 \texttt{-1} -1
  • int   getRear() \texttt{int getRear()} int getRear() 获取队尾元素。如果双端队列为空,返回 -1 \texttt{-1} -1
  • boolean   isEmpty() \texttt{boolean isEmpty()} boolean isEmpty() 如果循环双端队列为空则返回 true \texttt{true} true,否则返回 false \texttt{false} false
  • boolean   isFull() \texttt{boolean isFull()} boolean isFull() 如果循环双端队列已满则返回 true \texttt{true} true,否则返回 false \texttt{false} false

你不允许使用内置的队列数据结构。

示例

示例 1:

输入:
["MyCircularDeque",   "insertLast",   "insertLast",   "insertFront",   "insertFront",   "getRear",   "isFull",   "deleteLast",   "insertFront",   "getFront"] \texttt{["MyCircularDeque", "insertLast", "insertLast", "insertFront", "insertFront", "getRear", "isFull", "deleteLast", "insertFront", "getFront"]} ["MyCircularDeque", "insertLast", "insertLast", "insertFront", "insertFront", "getRear", "isFull", "deleteLast", "insertFront", "getFront"]
[[3],   [1],   [2],   [3],   [4],   [],   [],   [],   [4],   []] \texttt{[[3], [1], [2], [3], [4], [], [], [], [4], []]} [[3], [1], [2], [3], [4], [], [], [], [4], []]
输出:
[null,   true,   true,   true,   false,   2,   true,   true,   true,   4] \texttt{[null, true, true, true, false, 2, true, true, true, 4]} [null, true, true, true, false, 2, true, true, true, 4]
解释:
MyCircularDeque   myCircularDeque   =   new   MyCircularDeque(3); \texttt{MyCircularDeque myCircularDeque = new MyCircularDeque(3);} MyCircularDeque myCircularDeque = new MyCircularDeque(3);
myCircularDeque.insertLast(1); \texttt{myCircularDeque.insertLast(1);} myCircularDeque.insertLast(1); // 返回 True \texttt{True} True
myCircularDeque.insertLast(2); \texttt{myCircularDeque.insertLast(2);} myCircularDeque.insertLast(2); // 返回 True \texttt{True} True
myCircularDeque.insertFront(3); \texttt{myCircularDeque.insertFront(3);} myCircularDeque.insertFront(3); // 返回 True \texttt{True} True
myCircularDeque.insertFront(4); \texttt{myCircularDeque.insertFront(4);} myCircularDeque.insertFront(4); // 返回 False \texttt{False} False,双端队列已满
myCircularDeque.getRear(); \texttt{myCircularDeque.getRear();} myCircularDeque.getRear(); // 返回 2 \texttt{2} 2
myCircularDeque.isFull(); \texttt{myCircularDeque.isFull();} myCircularDeque.isFull(); // 返回 True \texttt{True} True
myCircularDeque.deleteLast(); \texttt{myCircularDeque.deleteLast();} myCircularDeque.deleteLast(); // 返回 True \texttt{True} True
myCircularDeque.insertFront(4); \texttt{myCircularDeque.insertFront(4);} myCircularDeque.insertFront(4); // 返回 True \texttt{True} True
myCircularDeque.getFront(); \texttt{myCircularDeque.getFront();} myCircularDeque.getFront(); // 返回 4 \texttt{4} 4

数据范围

  • 1 ≤ k ≤ 1000 \texttt{1} \le \texttt{k} \le \texttt{1000} 1k1000
  • 0 ≤ value ≤ 1000 \texttt{0} \le \texttt{value} \le \texttt{1000} 0value1000
  • 最多调用 3000 \texttt{3000} 3000 insertFront \texttt{insertFront} insertFront insertLast \texttt{insertLast} insertLast deleteFront \texttt{deleteFront} deleteFront deleteLast \texttt{deleteLast} deleteLast getFront \texttt{getFront} getFront getRear \texttt{getRear} getRear isEmpty \texttt{isEmpty} isEmpty isFull \texttt{isFull} isFull

前言

这道题和「设计循环队列」相似,区别在于这道题需要设计的结构是双端队列,双端队列支持在队首和队尾添加元素和删除元素。

循环双端队列的实现有两种方式,分别是基于数组和基于双向链表。数组和双向链表本身并不是环形结构,在实现时需要模拟环形结构。

解法一

思路和算法

使用数组实现循环双端队列时,需要维护数组、循环双端队列的大小(即最多允许存储的元素个数)、队首下标、队尾下标和循环双端队列中的元素个数。其中,数组的长度为循环双端队列的大小,队首下标表示队首元素所在位置的下标,队尾下标表示队尾元素的后一个位置的下标。

构造方法中,将数组 deque \textit{deque} deque 初始化为长度 k k k 的数组,将循环双端队列的容量 capacity \textit{capacity} capacity 设为 k k k,将队首下标 head \textit{head} head 和队尾下标 tail \textit{tail} tail 都初始化为 0 0 0,将元素个数 size \textit{size} size 初始化为 0 0 0

对于在队首添加元素操作,首先判断循环双端队列是否已满,如果循环双端队列已满则返回 false \text{false} false,如果循环双端队列未满则执行在队首添加元素操作。

  1. head : = ( head + capacity − 1 )   m o d   capacity \textit{head} := (\textit{head} + \textit{capacity} - 1) \bmod \textit{capacity} head:=(head+capacity1)modcapacity,取余运算确保 head \textit{head} head 在下标范围内。

  2. deque [ head ] : = value \textit{deque}[\textit{head}] := \textit{value} deque[head]:=value

  3. size : = size + 1 \textit{size} := \textit{size} + 1 size:=size+1

  4. 返回 true \text{true} true

由于 head \textit{head} head 是队首元素所在位置的下标,因此需要首先更新 head \textit{head} head 然后将 value \textit{value} value 赋给 deque [ head ] \textit{deque}[\textit{head}] deque[head]

对于在队尾添加元素操作,首先判断循环双端队列是否已满,如果循环双端队列已满则返回 false \text{false} false,如果循环双端队列未满则执行在队尾添加元素操作。

  1. deque [ tail ] : = value \textit{deque}[\textit{tail}] := \textit{value} deque[tail]:=value

  2. tail : = ( tail + 1 )   m o d   capacity \textit{tail} := (\textit{tail} + 1) \bmod \textit{capacity} tail:=(tail+1)modcapacity,取余运算确保 tail \textit{tail} tail 在下标范围内。

  3. size : = size + 1 \textit{size} := \textit{size} + 1 size:=size+1

  4. 返回 true \text{true} true

由于 tail \textit{tail} tail 是队尾元素的后一个位置的下标,因此需要首先将 value \textit{value} value 赋给 deque [ tail ] \textit{deque}[\textit{tail}] deque[tail] 然后更新 tail \textit{tail} tail

对于在队首删除元素操作,首先判断循环双端队列是否为空,如果循环双端队列为空则返回 false \text{false} false,如果循环双端队列不为空则执行在队首删除元素操作。

  1. head : = ( head + 1 )   m o d   capacity \textit{head} := (\textit{head} + 1) \bmod \textit{capacity} head:=(head+1)modcapacity,取余运算确保 head \textit{head} head 在下标范围内。

  2. size : = size − 1 \textit{size} := \textit{size} - 1 size:=size1

  3. 返回 true \text{true} true

对于在队尾删除元素操作,首先判断循环双端队列是否为空,如果循环双端队列为空则返回 false \text{false} false,如果循环双端队列不为空则执行在队尾删除元素操作。

  1. tail : = ( tail + capacity − 1 )   m o d   capacity \textit{tail} := (\textit{tail} + \textit{capacity} - 1) \bmod \textit{capacity} tail:=(tail+capacity1)modcapacity,取余运算确保 tail \textit{tail} tail 在下标范围内。

  2. size : = size − 1 \textit{size} := \textit{size} - 1 size:=size1

  3. 返回 true \text{true} true

对于获取队首元素和获取队尾元素操作,首先判断循环双端队列是否为空,如果循环双端队列为空则返回 − 1 -1 1,如果循环双端队列不为空则返回对应下标处的元素。

  • 对于获取队首元素操作,返回 deque [ head ] \textit{deque}[\textit{head}] deque[head]

  • 对于获取队尾元素操作,返回 deque [ ( tail + capacity − 1 )   m o d   capacity ] \textit{deque}[(\textit{tail} + \textit{capacity} - 1) \bmod \textit{capacity}] deque[(tail+capacity1)modcapacity]

对于检查循环双端队列是否为空和检查循环双端队列是否已满操作,这两种情况下都有 head = tail \textit{head} = \textit{tail} head=tail,因此需要根据循环双端队列中的元素个数判断。

  • 当且仅当 size = 0 \textit{size} = 0 size=0 时,循环双端队列为空。

  • 当且仅当 size = capacity \textit{size} = \textit{capacity} size=capacity 时,循环双端队列已满。

检查循环双端队列是否为空和检查循环双端队列是否已满操作可以在其余的操作中复用。具体而言,在 insertFront \texttt{insertFront} insertFront insertLast \texttt{insertLast} insertLast 中调用 isFull \texttt{isFull} isFull 判断循环双端队列是否已满,在 deleteFront \texttt{deleteFront} deleteFront deleteLast \texttt{deleteLast} deleteLast getFront \texttt{getFront} getFront getRear \texttt{getRear} getRear 中调用 isEmpty \texttt{isEmpty} isEmpty 判断循环双端队列是否为空。

代码

class MyCircularDeque {
    int[] deque;
    int capacity;
    int head;
    int tail;
    int size;

    public MyCircularDeque(int k) {
        deque = new int[k];
        capacity = k;
        head = 0;
        tail = 0;
        size = 0;
    }
    
    public boolean insertFront(int value) {
        if (isFull()) {
            return false;
        }
        head = (head + capacity - 1) % capacity;
        deque[head] = value;
        size++;
        return true;
    }
    
    public boolean insertLast(int value) {
        if (isFull()) {
            return false;
        }
        deque[tail] = value;
        tail = (tail + 1) % capacity;
        size++;
        return true;
    }
    
    public boolean deleteFront() {
        if (isEmpty()) {
            return false;
        }
        head = (head + 1) % capacity;
        size--;
        return true;
    }
    
    public boolean deleteLast() {
        if (isEmpty()) {
            return false;
        }
        tail = (tail + capacity - 1) % capacity;
        size--;
        return true;
    }
    
    public int getFront() {
        if (isEmpty()) {
            return -1;
        }
        return deque[head];
    }
    
    public int getRear() {
        if (isEmpty()) {
            return -1;
        }
        return deque[(tail + capacity - 1) % capacity];
    }
    
    public boolean isEmpty() {
        return size == 0;
    }
    
    public boolean isFull() {
        return size == capacity;
    }
}

复杂度分析

  • 时间复杂度:构造方法和每一项操作的时间复杂度是 O ( 1 ) O(1) O(1)

  • 空间复杂度: O ( k ) O(k) O(k),其中 k k k 是循环双端队列的大小。

解法二

思路和算法

由于循环双端队列需要支持在队首和队尾添加元素和删除元素,因此使用双向链表。使用双向链表实现循环双端队列时,需要维护队首结点、队尾结点、循环双端队列的大小(即最多允许存储的元素个数)和循环双端队列中的元素个数。

构造方法中,将队首结点 head \textit{head} head 和队尾结点 tail \textit{tail} tail 初始化为 null \text{null} null,将循环双端队列的容量 capacity \textit{capacity} capacity 设为 k k k,将元素个数 size \textit{size} size 初始化为 0 0 0

对于在队首添加元素操作,首先判断循环双端队列是否已满,如果循环双端队列已满则返回 false \text{false} false,如果循环双端队列未满则执行在队首添加元素操作。

  1. 创建新结点 node \textit{node} node,新结点的值为 value \textit{value} value

  2. 如果循环双端队列为空,则令 head : = node \textit{head} := \textit{node} head:=node tail : = node \textit{tail} := \textit{node} tail:=node;如果循环双端队列不为空,则令 head . prev : = node \textit{head}.\textit{prev} := \textit{node} head.prev:=node node . next : = head \textit{node}.\textit{next} := \textit{head} node.next:=head,然后令 head : = node \textit{head} := \textit{node} head:=node

  3. head . prev : = tail \textit{head}.\textit{prev} := \textit{tail} head.prev:=tail tail . next : = head \textit{tail}.\textit{next} := \textit{head} tail.next:=head,形成循环结构。

  4. size : = size + 1 \textit{size} := \textit{size} + 1 size:=size+1

  5. 返回 true \text{true} true

对于在队尾添加元素操作,首先判断循环双端队列是否已满,如果循环双端队列已满则返回 false \text{false} false,如果循环双端队列未满则执行在队尾添加元素操作。

  1. 创建新结点 node \textit{node} node,新结点的值为 value \textit{value} value

  2. 如果循环双端队列为空,则令 head : = node \textit{head} := \textit{node} head:=node tail : = node \textit{tail} := \textit{node} tail:=node;如果循环双端队列不为空,则令 tail . next : = node \textit{tail}.\textit{next} := \textit{node} tail.next:=node node . prev : = tail \textit{node}.\textit{prev} := \textit{tail} node.prev:=tail,然后令 tail : = node \textit{tail} := \textit{node} tail:=node

  3. head . prev : = tail \textit{head}.\textit{prev} := \textit{tail} head.prev:=tail tail . next : = head \textit{tail}.\textit{next} := \textit{head} tail.next:=head,形成循环结构。

  4. size : = size + 1 \textit{size} := \textit{size} + 1 size:=size+1

  5. 返回 true \text{true} true

对于在队首删除元素操作,首先判断循环双端队列是否为空,如果循环双端队列为空则返回 false \text{false} false,如果循环双端队列不为空则执行在队首删除元素操作。

  1. head : = head . next \textit{head} := \textit{head}.\textit{next} head:=head.next

  2. head . prev : = tail \textit{head}.\textit{prev} := \textit{tail} head.prev:=tail tail . next : = head \textit{tail}.\textit{next} := \textit{head} tail.next:=head,形成循环结构。

  3. size : = size − 1 \textit{size} := \textit{size} - 1 size:=size1

  4. 返回 true \text{true} true

对于在队尾删除元素操作,首先判断循环双端队列是否为空,如果循环双端队列为空则返回 false \text{false} false,如果循环双端队列不为空则执行在队尾删除元素操作。

  1. tail : = tail . prev \textit{tail} := \textit{tail}.\textit{prev} tail:=tail.prev

  2. head . prev : = tail \textit{head}.\textit{prev} := \textit{tail} head.prev:=tail tail . next : = head \textit{tail}.\textit{next} := \textit{head} tail.next:=head,形成循环结构。

  3. size : = size − 1 \textit{size} := \textit{size} - 1 size:=size1

  4. 返回 true \text{true} true

对于获取队首元素和获取队尾元素操作,首先判断循环双端队列是否为空,如果循环双端队列为空则返回 − 1 -1 1,如果循环双端队列不为空则返回对应结点的值。

  • 对于获取队首元素操作,返回 head . value \textit{head}.\textit{value} head.value

  • 对于获取队尾元素操作,返回 tail . value \textit{tail}.\textit{value} tail.value

对于检查循环双端队列是否为空和检查循环双端队列是否已满操作,需要根据循环双端队列中的元素个数判断。

  • 当且仅当 size = 0 \textit{size} = 0 size=0 时,循环双端队列为空。

  • 当且仅当 size = capacity \textit{size} = \textit{capacity} size=capacity 时,循环双端队列已满。

检查循环双端队列是否为空和检查循环双端队列是否已满操作可以在其余的操作中复用。

代码

class MyCircularDeque {
    Node head;
    Node tail;
    int capacity;
    int size;

    public MyCircularDeque(int k) {
        head = null;
        tail = null;
        capacity = k;
        size = 0;
    }
    
    public boolean insertFront(int value) {
        if (isFull()) {
            return false;
        }
        Node node = new Node(value);
        if (isEmpty()) {
            head = tail = node;
        } else {
            head.prev = node;
            node.next = head;
            head = node;
        }
        head.prev = tail;
        tail.next = head;
        size++;
        return true;
    }
    
    public boolean insertLast(int value) {
        if (isFull()) {
            return false;
        }
        Node node = new Node(value);
        if (isEmpty()) {
            head = tail = node;
        } else {
            tail.next = node;
            node.prev = tail;
            tail = node;
        }
        head.prev = tail;
        tail.next = head;
        size++;
        return true;
    }
    
    public boolean deleteFront() {
        if (isEmpty()) {
            return false;
        }
        head = head.next;
        head.prev = tail;
        tail.next = head;
        size--;
        return true;
    }
    
    public boolean deleteLast() {
        if (isEmpty()) {
            return false;
        }
        tail = tail.prev;
        head.prev = tail;
        tail.next = head;
        size--;
        return true;
    }
    
    public int getFront() {
        if (isEmpty()) {
            return -1;
        }
        return head.value;
    }
    
    public int getRear() {
        if (isEmpty()) {
            return -1;
        }
        return tail.value;
    }
    
    public boolean isEmpty() {
        return size == 0;
    }
    
    public boolean isFull() {
        return size == capacity;
    }
}

class Node {
    int value;
    Node prev;
    Node next;

    public Node(int value) {
        this.value = value;
    }
}

复杂度分析

  • 时间复杂度:构造方法和每一项操作的时间复杂度是 O ( 1 ) O(1) O(1)

  • 空间复杂度: O ( k ) O(k) O(k),其中 k k k 是循环双端队列的大小。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

伟大的车尔尼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值