在队列中,我们仅能删除头部元素或在尾部添加元素。「双向队列 double-ended queue」提供了更高的灵活性,允许在头部和尾部执行元素的添加或删除操作。
双向队列常用操作
跳转到“双向队列常用操作”具体的方法名称需要根据所使用的编程语言来确定。
| 方法名 | 描述 | 时间复杂度 |
|---|---|---|
push_first() | 将元素添加至队首 | O(1) |
push_last() | 将元素添加至队尾 | O(1) |
pop_first() | 删除队首元素 | O(1) |
pop_last() | 删除队尾元素 | O(1) |
peek_first() | 访问队首元素 | O(1) |
peek_last() | 访问队尾元素 | O(1) |
直接使用编程语言中已实现的双向队列类:
/* 初始化双向队列 */deque<int> deque;
/* 元素入队 */deque.push_back(2); // 添加至队尾deque.push_back(5);deque.push_back(4);deque.push_front(3); // 添加至队首deque.push_front(1);
/* 访问元素 */int front = deque.front(); // 队首元素int back = deque.back(); // 队尾元素
/* 元素出队 */deque.pop_front(); // 队首元素出队deque.pop_back(); // 队尾元素出队
/* 获取双向队列的长度 */int size = deque.size();
/* 判断双向队列是否为空 */bool empty = deque.empty();双向队列实现 &应用*
跳转到“双向队列实现 &应用*”双向队列的实现与队列类似,可以选择链表或数组作为底层数据结构。
基于双向链表的实现
跳转到“基于双向链表的实现”双向链表的头节点和尾节点视为双向队列的队首和队尾,同时实现在两端添加和删除节点的功能。
| LinkedListQueue | push_last() | push_first() | pop_last() | pop_first() |
|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() |
/* 双向链表节点 */struct DoublyListNode { int val; // 节点值 DoublyListNode *next; // 后继节点指针 DoublyListNode *prev; // 前驱节点指针 DoublyListNode(int val) : val(val), prev(nullptr), next(nullptr) { }};
/* 基于双向链表实现的双向队列 */class LinkedListDeque { private: DoublyListNode *front, *rear; // 头节点 front ,尾节点 rear int queSize = 0; // 双向队列的长度
public: /* 构造方法 */ LinkedListDeque() : front(nullptr), rear(nullptr) { }
/* 析构方法 */ ~LinkedListDeque() { // 遍历链表删除节点,释放内存 DoublyListNode *pre, *cur = front; while (cur != nullptr) { pre = cur; cur = cur->next; delete pre; } }
/* 获取双向队列的长度 */ int size() { return queSize; }
/* 判断双向队列是否为空 */ bool isEmpty() { return size() == 0; }
/* 入队操作 */ void push(int num, bool isFront) { DoublyListNode *node = new DoublyListNode(num); // 若链表为空,则令 front 和 rear 都指向 node if (isEmpty()) front = rear = node; // 队首入队操作 else if (isFront) { // 将 node 添加至链表头部 front->prev = node; node->next = front; front = node; // 更新头节点 // 队尾入队操作 } else { // 将 node 添加至链表尾部 rear->next = node; node->prev = rear; rear = node; // 更新尾节点 } queSize++; // 更新队列长度 }
/* 队首入队 */ void pushFirst(int num) { push(num, true); }
/* 队尾入队 */ void pushLast(int num) { push(num, false); }
/* 出队操作 */ int pop(bool isFront) { if (isEmpty()) throw out_of_range("队列为空"); int val; // 队首出队操作 if (isFront) { val = front->val; // 暂存头节点值 // 删除头节点 DoublyListNode *fNext = front->next; if (fNext != nullptr) { fNext->prev = nullptr; front->next = nullptr; } delete front; front = fNext; // 更新头节点 // 队尾出队操作 } else { val = rear->val; // 暂存尾节点值 // 删除尾节点 DoublyListNode *rPrev = rear->prev; if (rPrev != nullptr) { rPrev->next = nullptr; rear->prev = nullptr; } delete rear; rear = rPrev; // 更新尾节点 } queSize--; // 更新队列长度 return val; }
/* 队首出队 */ int popFirst() { return pop(true); }
/* 队尾出队 */ int popLast() { return pop(false); }
/* 访问队首元素 */ int peekFirst() { if (isEmpty()) throw out_of_range("双向队列为空"); return front->val; }
/* 访问队尾元素 */ int peekLast() { if (isEmpty()) throw out_of_range("双向队列为空"); return rear->val; }
/* 返回数组用于打印 */ vector<int> toVector() { DoublyListNode *node = front; vector<int> res(size()); for (int i = 0; i < res.size(); i++) { res[i] = node->val; node = node->next; } return res; }};基于数组的实现
跳转到“基于数组的实现”使用环形数组来实现双向队列。
| ArrayQueue | push_last() | push_first() | pop_last() | pop_first() |
|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() |
/* 基于环形数组实现的双向队列 */class ArrayDeque { private: vector<int> nums; // 用于存储双向队列元素的数组 int front; // 队首指针,指向队首元素 int queSize; // 双向队列长度
public: /* 构造方法 */ ArrayDeque(int capacity) { nums.resize(capacity); front = queSize = 0; }
/* 获取双向队列的容量 */ int capacity() { return nums.size(); }
/* 获取双向队列的长度 */ int size() { return queSize; }
/* 判断双向队列是否为空 */ bool isEmpty() { return queSize == 0; }
/* 计算环形数组索引 */ int index(int i) { // 通过取余操作实现数组首尾相连 // 当 i 越过数组尾部后,回到头部 // 当 i 越过数组头部后,回到尾部 return (i + capacity()) % capacity(); }
/* 队首入队 */ void pushFirst(int num) { if (queSize == capacity()) { cout << "双向队列已满" << endl; return; } // 队首指针向左移动一位 // 通过取余操作实现 front 越过数组头部后回到尾部 front = index(front - 1); // 将 num 添加至队首 nums[front] = num; queSize++; }
/* 队尾入队 */ void pushLast(int num) { if (queSize == capacity()) { cout << "双向队列已满" << endl; return; } // 计算队尾指针,指向队尾索引 + 1 int rear = index(front + queSize); // 将 num 添加至队尾 nums[rear] = num; queSize++; }
/* 队首出队 */ int popFirst() { int num = peekFirst(); // 队首指针向后移动一位 front = index(front + 1); queSize--; return num; }
/* 队尾出队 */ int popLast() { int num = peekLast(); queSize--; return num; }
/* 访问队首元素 */ int peekFirst() { if (isEmpty()) throw out_of_range("双向队列为空"); return nums[front]; }
/* 访问队尾元素 */ int peekLast() { if (isEmpty()) throw out_of_range("双向队列为空"); // 计算尾元素索引 int last = index(front + queSize - 1); return nums[last]; }
/* 返回数组用于打印 */ vector<int> toVector() { // 仅转换有效长度范围内的列表元素 vector<int> res(queSize); for (int i = 0, j = front; i < queSize; i++, j++) { res[i] = nums[index(j)]; } return res; }};双向队列应用
跳转到“双向队列应用”双向队列兼具栈与队列的逻辑,因此它可以实现这两者的所有应用场景,同时提供更高的自由度。









