返回

在 LeetCode 上解决问题 641:设计循环双端队列

前端







## 导言

在计算机科学中,队列是一种遵循先进先出(FIFO)原则的数据结构。双端队列是一种特殊的队列,它允许从队头和队尾同时进行插入和删除操作。在 LeetCode 上,问题 641 要求我们设计一个循环双端队列,其中元素在内部存储中形成一个循环,优化了空间利用率。

## 实现

要设计一个循环双端队列,我们可以使用数组作为底层数据结构。数组的循环特性允许我们在不移动元素的情况下实现插入和删除操作。以下是如何实现循环双端队列的步骤:

1. **定义属性:** 
   - `head`:队头指针,指向队头元素。
   - `tail`:队尾指针,指向队尾元素的下一个位置(空位)。
   - `capacity`:队列的容量,表示其可以容纳的最大元素数量。
   - `size`:队列的当前大小,表示已存储的元素数量。
   - `arr`:存储元素的数组。

2. **初始化:** 
   - 在构造函数中,使用给定的容量创建一个数组。
   - 将 `head` 和 `tail` 指针都初始化为 0。
   - 将 `size` 初始化为 03. **插入:** 
   - **队头插入:** 将元素插入队头。
     - 将 `head` 指针回退一步,如果回退到数组开头,则循环到数组结尾。
     - 将元素存储在 `arr[head]` 中。
     - 更新 `size`。
   - **队尾插入:** 将元素插入队尾。
     - 将元素存储在 `arr[tail]` 中。
     - 将 `tail` 指针前进一步,如果前进到数组结尾,则循环到数组开头。
     - 更新 `size`。

4. **删除:** 
   - **队头删除:** 从队头删除元素。
     - 将 `head` 指针前进一步,如果前进到数组结尾,则循环到数组开头。
     - 删除 `arr[head]` 中的元素。
     - 更新 `size`。
   - **队尾删除:** 从队尾删除元素。
     - 将 `tail` 指针回退一步,如果回退到数组开头,则循环到数组结尾。
     - 删除 `arr[tail]` 中的元素。
     - 更新 `size`。

5. **其他操作:** 
   - **isEmpty:** 检查队列是否为空。
   - **isFull:** 检查队列是否已满。
   - **getFront:** 获取队头元素。
   - **getRear:** 获取队尾元素。

## 代码实现

```java
class MyCircularDeque {
    private int[] arr;
    private int head;
    private int tail;
    private int capacity;
    private int size;

    public MyCircularDeque(int capacity) {
        this.capacity = capacity;
        arr = new int[capacity];
        head = 0;
        tail = 0;
        size = 0;
    }

    // 队头插入
    public void insertFront(int value) {
        if (isFull()) {
            throw new IllegalStateException("队列已满");
        }
        head = (head - 1 + capacity) % capacity;
        arr[head] = value;
        size++;
    }

    // 队尾插入
    public void insertLast(int value) {
        if (isFull()) {
            throw new IllegalStateException("队列已满");
        }
        arr[tail] = value;
        tail = (tail + 1) % capacity;
        size++;
    }

    // 队头删除
    public int deleteFront() {
        if (isEmpty()) {
            throw new IllegalStateException("队列为空");
        }
        int value = arr[head];
        head = (head + 1) % capacity;
        size--;
        return value;
    }

    // 队尾删除
    public int deleteLast() {
        if (isEmpty()) {
            throw new IllegalStateException("队列为空");
        }
        tail = (tail - 1 + capacity) % capacity;
        int value = arr[tail];
        size--;
        return value;
    }

    // 判断队列是否为空
    public boolean isEmpty() {
        return size == 0;
    }

    // 判断队列是否已满
    public boolean isFull() {
        return size == capacity;
    }

    // 获取队头元素
    public int getFront() {
        if (isEmpty()) {
            throw new IllegalStateException("队列为空");
        }
        return arr[head];
    }

    // 获取队尾元素
    public int getRear() {
        if (isEmpty()) {
            throw new IllegalStateException("队列为空");
        }
        return arr[(tail - 1 + capacity) % capacity];
    }
}

技巧和示例

  • 使用循环数组可以有效地实现队列的插入和删除操作,避免移动元素。
  • 仔细处理边界情况,例如队列为空或已满的情况。
  • 提供明确的错误消息,帮助用户了解操作失败的原因。

结论

本文深入探讨了解决 LeetCode 上问题 641 的方法,该问题要求设计一个高效的循环双端队列。我们介绍了循环双端队列的数据结构、实现方法和代码实现,并分享了一些有用的技巧和示例。通过理解这些概念,读者可以轻松地解决类似的问题,并提高他们在数据结构和算法方面的技能。