【简易版tinySTL】 deque容器

2024-06-21 03:44
文章标签 容器 简易版 deque tinystl

本文主要是介绍【简易版tinySTL】 deque容器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 基本概念
    • 功能
    • 思路
      • 数据结构
      • 循环数组实现
    • 代码实现
      • deque.h
      • test.cpp
    • 代码详解
      • 变量
      • push_front
      • push_back
      • pop_front、pop_back
      • operator[]
      • clear
      • printElements
      • resize
    • 本实现版本 和 C++ STL标准库实现版本的区别:

基本概念

功能: 双端数组,可以对头端进行插入删除操作

deque与vector区别:

  • vector对于头部的插入删除效率低,数据量越大,效率越低
  • deque相对而言,对头部的插入删除速度回比vector快
  • vector访问元素时的速度会比deque快,这和两者内部实现有关

image-20240520214345386

功能

设计一个名为 Deque 的 Deque 类,该类具有以下功能和特性:

1、基础成员函数

  • 构造函数:初始化 Deque 实例
  • 析构函数:清理资源,确保无内存泄露

2、核心功能

  • 在 Deque 末尾添加元素
  • 在 Deque 开头添加元素
  • 删除 Deque 末尾的元素
  • 删除 Deque 开头的元素
  • 获取 Deque 中节点的数量
  • 删除 Deque 中所有的元素

3、迭代与遍历

  • 打印 Deque 中的元素

4、辅助功能

  • 重载[]运算符以对 Deque 进行索引访问

思路

下面代码将会使用循环数组的方式来模拟双端队列,实现了一个模板类 deque

数据结构

  • elements:指向队列元素的指针
  • frontIndex:队列第一个元素的索引
  • backIndex:指向队列最后一个元素,下一位地址的索引。如果空间满了,则指向最后一个元素
  • size:当前队列元素个数
  • capacity:队列的最大容量

循环数组实现

如下图所示:

  1. push_front(10):首先要开辟一块连续的地址空间 unuseded(与vector一样,当存储容量capacity满了之后,我们要扩展空间,扩展后的容量将会翻倍),此时frontIndexbackIndex都指向NULL。随后,该块内存会分配数据(10),然后frontIndexbackIndex都会指向这个地址。
  2. push_back(20):由于存储空间不够用了,系统会再开辟一块内存,其大小为2,分配数据(20),然后backIndex移动到该地址,frontIndex不动
  3. push_front(30):同上,系统会再开辟一段存储空间,其大小为4。由于是在队头插入,且地址是连续的,为了模拟出循环队列的效果,我们会让frontIndex指针移到队尾,并将数据(30)存在队尾。当我们想遍历元素的时候,frontIndex会沿着 ♻ 绿色箭头的方向遍历(不用担心,这其实很好实现)
  4. push_back(0):存储空间够用,则backIndex指向的地址赋值(0),然后backIndex后移一位

image-20240520193850545

代码实现

deque.h

#pragma once#include <iostream>
#include <stdexcept>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>namespace mystl{
template <class T>
class deque{
public:T* elements;size_t frontIndex;size_t backIndex;size_t size;size_t capacity;deque():elements(nullptr), size(0), capacity(0),frontIndex(0),backIndex(0){};~deque(){size = 0;elements = nullptr;frontIndex = 0;backIndex = 0;}void push_front(const T& value){if(size == capacity){resize();}frontIndex = (frontIndex - 1 + capacity)%capacity;elements[frontIndex] = value;size++;}void push_back(const T& value){if(size == capacity){resize();}elements[backIndex] = value; // ?backIndex = (backIndex + 1)%capacity;size++;}void pop_front(){if(size == 0){throw std::out_of_range("deque is empty");}frontIndex = (frontIndex+1)%capacity;size--;}void pop_back(){if(size == 0){throw std::out_of_range("deque is empty");}backIndex = (backIndex - 1 + capacity)%capacity;size--;}T& operator[](int index){if(index >= size){throw std::out_of_range("deque is empty");}return elements[(frontIndex+index)%capacity];}size_t getSize() const{return size;}void clear(){while(size > 0){pop_front();}}void printElements() 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:2*capacity;T* newElements = new T[newCapacity];size_t index = frontIndex;for(size_t i =0; i<size; ++i){newElements[i] = elements[index];index = (index+1)%capacity;}delete[] elements;elements = newElements;capacity = newCapacity;frontIndex = 0;backIndex = size;}
};
}

test.cpp

#include "deque.h"
void dequeTest()
{mystl::deque<int> d1;d1.push_front(10);d1.push_back(20);d1.push_front(30);d1.push_back(0);d1.printElements();std::cout << d1[2] << std::endl;d1.pop_back();d1.printElements();d1.pop_front();d1.printElements();d1.clear();d1.printElements();
}int main()
{dequeTest();system("pause");return 0;
}

代码详解

变量

T* elements;
size_t frontIndex;
size_t backIndex;
size_t size;
size_t capacity;
  • elements:指向队列元素的指针
  • frontIndex:队列第一个元素的索引
  • backIndex:指向队列最后一个元素,下一位地址的索引。如果空间满了,则指向最后一个元素
  • size:当前队列元素个数
  • capacity:队列的最大容量

除了elements是T指针外,其它都是整型

push_front

记住我们赋值的1、2、3、4……都是常量,所以输入形参是 const T& value

void push_front(const T& value)
{if(size == capacity){resize();}frontIndex = (frontIndex - 1 + capacity)%capacity;elements[frontIndex] = value;size++;
}
  • 在队列前端插入元素。首先检查容量并调整数组大小(如果需要)

  • 更新 frontIndexfrontIndex = (frontIndex - 1 + capacity) % capacity;

    如果frontIndex不在队头,那frontIndex往前移一位

    如果frontIndex在队头,即 frontIndex = 0,那么frontIndex-1= -1(-1 + capacity)% capcitys = capcitys - 1,也就是当前存储空间的最后一个地址

  • 在新位置插入元素,最后增加 size

push_back

void push_back(const T& value)
{if(size == capacity){resize();}elements[backIndex] = value; // ?backIndex = (backIndex + 1)%capacity;size++;
}
  • 在队列后端插入元素。首先检查容量并调整数组大小(如果需要),在 backIndex位置插入元素

  • 更新 backIndexbackIndex = (backIndex + 1)%capacity

    backIndex一般都是小于capacity,因此实际情况下backIndex = backIndex + 1

  • 最后增加 size

pop_front、pop_back

void pop_front()
{if(size == 0){throw std::out_of_range("deque is empty");}frontIndex = (frontIndex+1)%capacity;size--;
}void pop_back()
{if(size == 0){throw std::out_of_range("deque is empty");}backIndex = (backIndex - 1 + capacity)%capacity;size--;
}

从队列前(后)端移除元素。首先检查队列是否为空,然后更新 frontIndexbackIndex),最后减少 size

operator[]

T& operator[](int index)
{if(index >= size){throw std::out_of_range("deque is empty");}return elements[(frontIndex+index)%capacity];
}

(frontIndex+index)%capacity

把取值范围限定在[0,capacity-1]这个区间,如下图:当frontIndex = 3,在后面时,如果index = 2,capacity = 4,则(3+2)% 4 = 1;则是 20 这个元素

image-20240520211909053

如下图:当frontIndex = 0,在后面时,如果index = 2,capacity = 4,则(0+2)% 4 = 2;则是 0 这个元素

image-20240520212242898

clear

void clear()
{while(size > 0){pop_front();}
}

清空队列中的所有元素,通过不断调用 pop_front 方法实现。

printElements

void printElements() 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;
}

打印队列中的所有元素,从 frontIndex 开始遍历,直到打印完所有元素。这里使用的index是是从frontIndex开始计算的索引, 而不是实际上的数组索引

frontIndex会沿着 ♻ 绿色箭头的方向遍历

image-20240520213130054

resize

void resize()
{size_t newCapacity = (capacity == 0)? 1:2*capacity;T* newElements = new T[newCapacity];size_t index = frontIndex;for(size_t i =0; i<size; ++i){newElements[i] = elements[index];index = (index+1)%capacity;}delete[] elements;elements = newElements;capacity = newCapacity;frontIndex = 0;backIndex = size;
}

当数组容量不足以容纳更多元素时,创建一个新的数组,将现有元素复制到新数组中,释放旧数组,并更新相关成员变量。

需要注意的是, 原来的数组中, 逻辑上索引为0的位置(也就是frontIndex)并不一定存储在数组实际上的0索引处, 但resize后将逻辑索引和实际索引都统一起来。如图所示:

image-20240520214054837

本实现版本 和 C++ STL标准库实现版本的区别:

标准库中的 std::deque(双端队列)通常是通过一个或者多个连续存储区域(即一维数组)来实现的,而不是单一的连续数组, 这多个一维数组连接起来形成了deque数组, 目前的实现采用的是循环数组, 缺点就是resize时要复制旧数组, 而官方的std::deque只需要再串联一个一维数组就可以了, 效率更高

内存管理、异常安全性、迭代器、性能优化均未实现

这篇关于【简易版tinySTL】 deque容器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1080111

相关文章

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

如何将Tomcat容器替换为Jetty容器

《如何将Tomcat容器替换为Jetty容器》:本文主要介绍如何将Tomcat容器替换为Jetty容器问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Tomcat容器替换为Jetty容器修改Maven依赖配置文件调整(可选)重新构建和运行总结Tomcat容器替

C++从序列容器中删除元素的四种方法

《C++从序列容器中删除元素的四种方法》删除元素的方法在序列容器和关联容器之间是非常不同的,在序列容器中,vector和string是最常用的,但这里也会介绍deque和list以供全面了解,尽管在一... 目录一、简介二、移除给定位置的元素三、移除与某个值相等的元素3.1、序列容器vector、deque

C++常见容器获取头元素的方法大全

《C++常见容器获取头元素的方法大全》在C++编程中,容器是存储和管理数据集合的重要工具,不同的容器提供了不同的接口来访问和操作其中的元素,获取容器的头元素(即第一个元素)是常见的操作之一,本文将详细... 目录一、std::vector二、std::list三、std::deque四、std::forwa

Python容器类型之列表/字典/元组/集合方式

《Python容器类型之列表/字典/元组/集合方式》:本文主要介绍Python容器类型之列表/字典/元组/集合方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 列表(List) - 有序可变序列1.1 基本特性1.2 核心操作1.3 应用场景2. 字典(D

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

Spring核心思想之浅谈IoC容器与依赖倒置(DI)

《Spring核心思想之浅谈IoC容器与依赖倒置(DI)》文章介绍了Spring的IoC和DI机制,以及MyBatis的动态代理,通过注解和反射,Spring能够自动管理对象的创建和依赖注入,而MyB... 目录一、控制反转 IoC二、依赖倒置 DI1. 详细概念2. Spring 中 DI 的实现原理三、

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF