手写简单STL标准库——Deque的实现

C++ STL中的deque(双端队列)是一种双开口的,拥有连续储存空间的数据结构。双开口意思就是说既可以在头部进行插入删除元素,又可以在尾部进行插入删除元素。它结合了vector和list的优点。

(1)与vector相比:vector没有头插头删的操作,而deque有!

(2)与list相比:list没有连续的存储空间,而deque有!


deque的特性

双端操作:deque支持在前端和后端执行快速的插入和删除操作,使其成为处理需要在两端进行频繁操作的问题的理想选择

随机访问:与list不同,deque允许在常量时间内对元素进行随机访问。这是因为deque的内部结构采用分段数组,而不是链表,从而提供了更好的随机访问性能。

动态扩展:deque的大小可以动态调整,无需事先分配固定大小的内存。这使得deque适用于需要动态增长和缩小的情况。

保持迭代器有效性:deque在进行插入和删除操作时,能够更好地保持迭代器的有效性。这意味着在进行这些操作后,不会导致所有迭代器失效。

内存局部性:deque的内部结构利用了多个缓冲区,有助于提高内存局部性,从而在某些情况下提供更好的性能。

举例说明,关于随机访问:

在C++中,std::deque允许在常量时间内对元素进行随机访问,这意味着可以通过索引直接访问deque中的元素,而不会随deque的大小而增加访问时间。下面是一个简单的例子来说明这一点:

#include <iostream>
#include <deque>

int main() {
    // 创建一个包含一些元素的deque
    std::deque<int> myDeque = {1, 2, 3, 4, 5};

    // 在常量时间内对元素进行随机访问
    std::cout << "Element at index 2: " << myDeque[2] << std::endl;
    std::cout << "Element at index 4: " << myDeque[4] << std::endl;

    return 0;
}

在上面的例子中,myDeque[2] 和 myDeque[4] 的访问时间是常量的,不受deque的大小的影响。

这是因为std::deque使用了分段数组的结构,每个分段都是一个固定大小的数组,因此可以直接计算索引的位置,而不需要遍历整个容器。

请注意,虽然std::deque允许在常量时间内对元素进行随机访问。

但与std::vector相比,std::deque在访问元素时可能涉及更多的间接层次,因此在一些情况下,std::vector的随机访问性能可能略优。

在选择容器时,需要根据具体的使用情况和需求权衡不同容器的优劣。


STL标准库中deque的基本用法

//要使用deque,首先需要包含相关的头文件:
#include <deque>

//接下来,可以声明一个deque对象,并开始使用它:
std::deque<int> myDeque;

//可以使用push_front和push_back在deque的前端和后端插入元素:
myDeque.push_front(1);
myDeque.push_back(2); 

//使用pop_front和pop_back从deque的前端和后端删除元素:
myDeque.pop_front();
myDeque.pop_back();

deque工作原理

手绘有点丑将就看吧,后期再更新= _= 

  • 无论是push_front还是push_back, 都需要判断当前数组是否已满, 已满的话将其扩容为原容量的2倍, 将原来的数组元素复制到新的数组中。
  • push_front需要将frontIndex自减后在frontIndex位置插入, frontIndex自减后需要加上capacity后对capacity取模, 这使得如果frontIndex为负, frontIndex将指向从数组末尾。
  • push_back直接在backIndex位置插入, 然后将backIndex自增, backIndex自增后需要对capacity取模, 这使得如果backIndex越界, backIndex将指向从数组开始的位置

实现deque

#include <iostream>
#include <sstream>
#include <algorithm>
#include <string>
#include <stdexcept>

template<typename T>
class Deque
{
private:
	T* elements;//就是一个循环一维数组
	size_t capacity;//数组的容量
	size_t frontIndex;//deque的前端索引
	size_t backIndex;//deque的后端索引
	size_t size;//deque中元素的数量
public:
	//构造函数
	Deque()
		:elements(nullptr)
		,capacity(0)
		,frontIndex(0)
		,backIndex(0)
		,size(0)
	{}
	~Deque() {
		clear();
		delete[] elements;
	}

	//再deque的前端插入元素
	void push_front(const T& value) {
		//检查是否需要扩容
		if (size == capacity) {
			resize();
		}
		//计算新的前端索引
		frontIndex = (frontIndex - 1 + capacity) % capacity;

		// 在新的前端位置插入元素
		elements[frontIndex] = value;

		//增加deque的元素
		++size;
	}
	/*
	* 在队列前端插入元素。首先检查容量并调整数组大小(如果需要)
	  然后更新 frontIndex 并在新位置插入元素,最后增加 size。
	  frontIndex = (frontIndex - 1 + capacity) % capacity;是为了防止数组索引为负, 使其指向尾端。
	*/

	//在deque后端加入插入元素
	void push_back(const T& value) {
		//检查是否需要扩容
		if (size == capacity) {
			resize();
		}

		//在新的后端插入位置元素
		elements[backIndex] = value;

		//计算新的后端索引
		backIndex = (backIndex + 1) % capacity;

		//增加deque的元素
		++size;
	}
	/*
	在队列后端插入元素。同样先检查容量并调整数组大小,然后直接在 backIndex 插入元素,
	更新 backIndex,最后增加 size。
	backIndex = (backIndex + 1) % capacity;是为了防止索引越界, 使其回到数组开始的位置。
	*/

	//从deque的前端移除元素
	void pop_front() {
		//检查deque是否为空
		if (size == 0) {
			throw std::out_of_range("Deque is empty"); 
		}
		frontIndex = (frontIndex + 1) % frontIndex;
		--size;
	}

	//从deque的后端移除元素
	void pop_back() {
		//检查deque是否为空
		if (size == 0) {
			throw std::out_of_range("Deque is empty");
		}
		backIndex = (backIndex - 1 + capacity) % capacity;

		--size;
	}

	//随机访问元素
	T& operator[](int index) {
		if (index < 0 || index >= size) {
			throw std::out_of_range("Index out of range");
		}
		return elements[(index + frontIndex) % capacity];
	}

	//获取deque中的元素数量
	size_t getSize() const() {
		return size;
	}

	// 清空deque
	void clear() {
		while (size > 0) {
			pop_front();
		}
	}

	//打印deque的元素
	void printElement() const {
		size_t index = frontIndex;
		for (size_t i = 0; i < size; ++i) {
			std::cout << elements[index] << " ";
			index = (index + 1) % capacity;
		}
		std::cout << std::endl;
	}
private:
	void resize() {
		//计算新的容量
		size_t newCapacity = (capacity == 0) ? 1 : capacity * 2;

		//创建新的数组
		T* newElements = new T[newCapacity];

		//复制元素到新数组
		for (size_t i = 0; i < size; ++i) {
			newCapacity[i] = elements[index];
			index = (index + 1) % capacity;
		}

		//释放就数组的内存
		delete[] elements;

		//更新成员变量
		elements = newElements;
		capacity = newCapacity;
		frontIndex = 0;
		backIndex = size;
	}
};

int main() {
	// 创建一个 Deque 对象
	Deque<int> myDeque;

	int N;
	std::cin >> N;
	// 读走回车
	getchar();
	std::string line;
	// 接收命令
	for (int i = 0; i < N; i++) {
		std::getline(std::cin, line);
		std::istringstream iss(line);
		std::string command;
		iss >> command;
		int value;

		if (command == "push_back") {
			iss >> value;
			myDeque.push_back(value);
		}

		if (command == "push_front") {
			iss >> value;
			myDeque.push_front(value);
		}

		if (command == "pop_back") {
			if (myDeque.getSize() == 0) {
				continue;
			}
			myDeque.pop_back();
		}

		if (command == "pop_front") {
			if (myDeque.getSize() == 0) {
				continue;
			}
			myDeque.pop_front();
		}

		if (command == "clear") {
			myDeque.clear();
		}

		if (command == "size") {
			std::cout << myDeque.getSize() << std::endl;
		}

		if (command == "get") {
			iss >> value;
			std::cout << myDeque[value] << std::endl;
		}

		if (command == "print") {
			if (myDeque.getSize() == 0) {
				std::cout << "empty" << std::endl;
			}
			else {
				myDeque.printElements();
			}
		}
	}
	return 0;
}

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值