📅  最后修改于: 2023-12-03 15:22:24.907000             🧑  作者: Mango
双端队列是一种具有队列和栈的特性的数据结构,可以在队列的两端进行插入和删除操作。循环数组是一种实现队列数据结构的常用技术,可以避免频繁的内存分配和释放。本文将介绍如何使用模板类和循环数组来实现一个动态双端队列。
template <typename T>
class Deque {
public:
Deque();
~Deque();
void push_back(const T& elem);
void pop_back();
void push_front(const T& elem);
void pop_front();
bool empty() const;
size_t size() const;
T& back();
T& front();
private:
T* data_;
int size_;
int capacity_;
int front_;
int rear_;
void expand();
};
在这个类中,我们定义了一个模板类Deque
,模板参数表示队列中存储的数据类型。这个类有以下公有成员函数:
Deque()
:构造函数,初始化队列;~Deque()
:析构函数,销毁队列;void push_back(const T& elem)
:将元素elem
插入到队列的尾部;void pop_back()
:删除队列尾部的元素;void push_front(const T& elem)
:将元素elem
插入到队列的头部;void pop_front()
:删除队列头部的元素;bool empty() const
:判断队列是否为空;size_t size() const
:返回队列的大小;T& back()
:返回队列尾部的元素;T& front()
:返回队列头部的元素。类中还定义了以下私有成员变量:
T* data_
:指向存储数据的数组;int size_
:队列中元素的数量;int capacity_
:存储数据的数组的容量;int front_
:循环数组的头指针;int rear_
:循环数组的尾指针。为了避免队列满时无法插入数据的情况,我们需要实现一个扩容函数来动态调整存储数据的数组的容量。我们可以每次将数组的容量翻倍来扩容,这样可以保证每次的时间复杂度为O(1)
。
template <typename T>
void Deque<T>::expand() {
int new_capacity = capacity_ * 2;
T* new_data = new T[new_capacity];
for (int i = 0; i < size_; ++i) {
new_data[i] = data_[(front_ + i) % capacity_];
}
front_ = 0;
rear_ = size_;
capacity_ = new_capacity;
delete[] data_;
data_ = new_data;
}
在这个函数中,我们首先将数组的容量翻倍,然后创建一个新的数组new_data
来保存数据。我们将原数组中的元素复制到新数组中,并更新头指针和尾指针来指向新数组的头和尾。最后,我们释放原数组的内存,将指针指向新数组。
对于双端队列中的插入和删除操作,我们需要分别考虑头部和尾部的情况。当队列满时,我们需要调用扩容函数来增大数组的容量。具体实现如下:
template <typename T>
void Deque<T>::push_back(const T& elem) {
if (size_ == capacity_) {
expand();
}
data_[rear_] = elem;
rear_ = (rear_ + 1) % capacity_;
++size_;
}
template <typename T>
void Deque<T>::pop_back() {
if (empty()) {
throw std::out_of_range("the deque is empty");
}
rear_ = (rear_ - 1 + capacity_) % capacity_;
--size_;
}
template <typename T>
void Deque<T>::push_front(const T& elem) {
if (size_ == capacity_) {
expand();
}
front_ = (front_ - 1 + capacity_) % capacity_;
data_[front_] = elem;
++size_;
}
template <typename T>
void Deque<T>::pop_front() {
if (empty()) {
throw std::out_of_range("the deque is empty");
}
front_ = (front_ + 1) % capacity_;
--size_;
}
在插入和删除操作中,我们需要注意循环数组的特性。当插入尾部元素时,我们将元素插入到rear_
指向的位置,并将rear_
指针向后移动一位,如果rear_
指针到了数组的尾部,则将其指向数组的头部。当删除尾部元素时,我们将rear_
指针向前移动一位,并将size_
的值减一。
当插入头部元素时,我们将元素插入到front_
指向的位置,并将front_
指针向前移动一位,如果front_
指针到了数组的头部,则将其指向数组的尾部。当删除头部元素时,我们将front_
指针向后移动一位,并将size_
的值减一。
我们可以使用以下代码来测试双端队列的实现:
#include <iostream>
#include <stdexcept>
#include "deque.h"
int main() {
Deque<int> d;
d.push_back(1);
d.push_back(2);
d.push_back(3);
d.push_front(0);
d.pop_back();
d.pop_front();
std::cout << d.front() << '\n';
std::cout << d.back() << '\n';
std::cout << d.size() << '\n';
d.push_front(-1);
std::cout << d.front() << '\n';
std::cout << d.back() << '\n';
std::cout << d.size() << '\n';
return 0;
}
在这个示例中,我们创建了一个整型的双端队列,并向其插入一些元素。然后,我们分别删除队列的头部和尾部元素,并输出队列头部和尾部的元素,以及队列的大小。最后,我们再向队列的头部插入一个元素,再次输出队列头部和尾部的元素,以及队列的大小。
本文介绍了如何使用模板类和循环数组来实现一个动态双端队列。这个双端队列具有队列和栈的特性,并支持在队列的两端进行插入和删除操作。通过使用循环数组来避免频繁的内存分配和释放,我们可以实现高效的双端队列。