数据结构——数组

数组

github地址

数组基础

  • 数组最大的有点:快速查询。索引快
  • 数组最好应用于 “索引有语义” 的情况
  • 但并非所有有语义的索引都适用于数组(身份证号)
  • 数组也可以处理 ”索引没有语义“ 的情况

封装数组类

  • 数组类该具备的功能:增 删 改 查

使用泛型:

  • 让我们的数据结构可以放置 “任何” 数据类型

实现

构造函数

我们希望有两个构造函数,可以支持给定容量和默认容量,这里默认容量我们设置成 10 :

template<typename T>
MyArray<T>::MyArray() {size_ = 0;capacity_ = 10;data_ = new T[capacity_];std::cout << "调用 MyArray() 构造." << std::endl;
}template<typename T>
MyArray<T>::MyArray(int capacity) {if (capacity <= 0) {std::cout << "MyArray(int) error. Capacity is illegal." << std::endl;throw "MyArray(int) error. Capacity is illegal.";}size_ = 0;capacity_ = capacity;data_ = new T[capacity_];std::cout << "调用 MyArray(int capacity) 构造." << std::endl;
}

析构函数

template<typename T>
MyArray<T>::~MyArray() {if (nullptr != data_) {delete[] data_;data_ = nullptr;}size_ = 0;capacity_ = 0;std::cout << "调用 ~MyArray() 析构." << std::endl;
}

拷贝构造和拷贝赋值

首先添加拷贝构造测试:

// 初始化
class ArrayTest : public testing::Test {protected:void SetUp() override {for (int i = 0; i < 10; ++i) {my_array_.AddLast(i);}}void TearDown() override {}MyArray<int> my_array_;
};// 拷贝构造测试
TEST_F(ArrayTest, CopyConstructorTest) {MyArray<int> my_array(my_array_);EXPECT_EQ(10,my_array.GetSize());EXPECT_EQ(9,my_array.Get(9));
}

这里出现了 TEST_F,在TDD实例有例子。

拷贝构造函数和赋值函数:

template<typename T>
MyArray<T>::MyArray(const MyArray &arr) {this->size_     = arr.size_;this->capacity_ = arr.capacity_;this->data_     = new T[capacity_];for (int i = 0; i < size_; ++i) {this->data_[i] = arr.data_[i];}std::cout << "调用  MyArray(const MyArray &arr) 拷贝构造" << std::endl;
}template<typename T>
MyArray<T> &MyArray<T>::operator=(const MyArray<T> &arr) {assert(this != &arr);if (this->data_ != nullptr) {delete[] this->data_;this->data_  = nullptr;}//分配内存this->size_     = arr.size_;this->capacity_ = arr.capacity_;this->data_     = new T[capacity_];//拷贝数据for (int i = 0; i < size_; i++) {//如果是自定义的复杂数据类型,必须对 = 运算赋进行重载,  operator=this->data_[i] = arr.data_[i];}std::cout << "调用 = 赋值操作 " << std::endl;return* this;
}

移动构造和移动赋值

首先添加测试用例:

// 移动构造和移动赋值测试
TEST_F(ArrayTest, MoveTest) {// 移动拷贝构造MyArray<int> my_array(std::move(my_array_));// 移动赋值传入本身 会报错
//  my_array = std::move(my_array);
//  EXPECT_EQ(10,my_array.GetSize());
//  EXPECT_EQ(9,my_array.Get(9));MyArray<int> my_array1;// 移动赋值my_array1 = std::move(my_array);EXPECT_EQ(10,my_array1.GetSize());EXPECT_EQ(0,my_array.GetSize());EXPECT_EQ(0,my_array_.GetSize());
}

移动构造和移动赋值:

template<typename T>
MyArray<T>::MyArray(MyArray &&arr) noexcept {assert(this != &arr);capacity_ = std::exchange(arr.capacity_, 0);size_     = std::exchange(arr.size_, 0);    // 非类类型成员的显式移动data_     = std::move(arr.data_);           // 类类型成员的显式移动arr.data_ = nullptr;std::cout << "调用  MyArray(const MyArray &&arr) 移动构造函数" << std::endl;
}template<typename T>
MyArray<T> &MyArray<T>::operator=(MyArray<T> &&arr) noexcept {assert(this != &arr);if (this->data_ != nullptr) {delete[] this->data_;this->data_ = nullptr;}//分配内存this->size_ = std::exchange(arr.size_, 0);this->capacity_ = std::exchange(arr.capacity_, 0);this->data_ = new T[capacity_];//拷贝数据for (int i = 0; i < size_; i++) {//如果是自定义的复杂数据类型,必须对 = 运算赋进行重载,  operator=this->data_[i] = std::move(arr.data_[i]);}std::cout << "调用 = 移动赋值操作 " << std::endl;return * this;
}

<< 重载

测试用例:

// 测试 << 重载
TEST_F(ArrayTest, OperatorTest) {std::cout << my_array_ << std::endl;
}

实现:

// 内部友元函数实现 重载输出 << 操作符,这里必须定义成友元
friend std::ostream &operator<<(std::ostream &out, MyArray<T> &obj) {out << "MyArray size = " << obj.size_ << ", Capacity = " << obj.capacity_ << std::endl;out << "MyArray: [";for (int i = 0; i < obj.size_; ++i) {out << obj.data_[i];if (i != obj.size_ - 1)out << ", ";}out << "] ";return out;
}

[] 重载

测试用例:

// [] 测试
TEST_F(ArrayTest, SquareBracketsTest) {EXPECT_EQ(0,my_array_[0]);EXPECT_EQ(1,my_array_[1]);
}

实现:

template<typename T>
T &MyArray<T>::operator[](int index) {if (index < 0 || index >= size_) {std::cout << "[] fail. Index is illegal." << std::endl;throw "[] fail. Index is illegal.";}std::cout << "[] 调用" << std::endl;return this->data_[index];
}

添加元素

首先添加测试:

TEST(ArrayTestNoF, AddAndGet) {MyArray<int> my_array;my_array.Add(0,1);my_array.Add(1,2);my_array.Add(2,3);EXPECT_EQ(1,my_array.Get(0));EXPECT_EQ(2,my_array.Get(1));EXPECT_EQ(3,my_array.Get(2));my_array.AddFirst(4);EXPECT_EQ(4,my_array.Get(0));EXPECT_EQ(4,my_array.GetFirst());my_array.AddLast(5);EXPECT_EQ(5,my_array.Get(4));EXPECT_EQ(5,my_array.GetLast());
}

我们希望提供三个接口:在指定位置插入元素,在头部和尾部插入元素

后两个方法可以通过第一个方法实现,我们先实现第一个方法,如下:

template<typename T>
void MyArray<T>::Add(int index, T t) {if (index < -1 || index > size_) {std::cout << "Add fail. Index is illegal." << std::endl;throw "Add fail. Index is illegal.";}if (IsFull()) {  Resize(capacity_ * 2);}for (int i = size_; i > index; --i) {data_[i] = data_[i - 1];}data_[index] = t;size_++;
}

后两个方法在此基础上:

template<typename T>
void MyArray<T>::AddFirst(T t) {Add(0, t);
}template<typename T>
void MyArray<T>::AddLast(T t) {Add(size_, t);
}

判断空满

测试用例:

TEST(ArrayTestNoF, IsEmpty) {MyArray<int> my_array(10);EXPECT_TRUE(my_array.IsEmpty());
}
template<typename T>
bool MyArray<T>::IsFull() const {return size_ == capacity_;
}template<typename T>
bool MyArray<T>::IsEmpty() const {return size_ == 0;
}

获取容量和大小

测试用例:

TEST_F(ArrayTest, GetSizeAndGetCapacity) {EXPECT_EQ(10,my_array_.GetCapacity());EXPECT_EQ(10,my_array_.GetSize());
}

实现:

template<typename T>
int MyArray<T>::GetSize() const {return size_;
}template<typename T>
int MyArray<T>::GetCapacity() const {return capacity_;
}

对指定位置元素赋值

测试用例:

TEST_F(ArrayTest, Set) {my_array_.Set(0,11);EXPECT_EQ(11,my_array_.GetFirst());
}

实现:

template<typename T>
void MyArray<T>::Set(int index, T t) {if (index < 0 || index >= size_) {std::cout << "Set Fail. Index is illegal." << std::endl;throw "Set fail. Index is illegal.";}if (IsEmpty()) {std::cout << "Set Fail. Array is empty." << std::endl;throw "Set fail. Array is empty.";}data_[index] = t;
}

查找元素

测试用例:

TEST_F(ArrayTest, FindAndContain) {EXPECT_EQ(0,my_array_.Find(0));EXPECT_TRUE(my_array_.Contain(1));EXPECT_FALSE(my_array_.Contain(111));
}

实现:

template<typename T>
int MyArray<T>::Find(T t) const {if (IsEmpty()) {std::cout << "Find fail. Array is empty." << std::endl;throw "Find Fail. Array is empty";}for (int i = 0; i < size_; ++i) {if (data_[i] == t) {return i;}}return -1;
}template<typename T>
bool MyArray<T>::Contain(T t) const {return Find(t) != -1;
}

调整空间

测试用例:

TEST_F(ArrayTest, Resize) {EXPECT_EQ(10,my_array_.GetSize());EXPECT_EQ(10,my_array_.GetCapacity());my_array_.AddLast(10);EXPECT_EQ(11,my_array_.GetSize());EXPECT_EQ(20,my_array_.GetCapacity());for (int j = 0; j < 6; ++j) {my_array_.RemoveLast();}EXPECT_EQ(5,my_array_.GetSize());EXPECT_EQ(20,my_array_.GetCapacity());my_array_.RemoveLast();EXPECT_EQ(4,my_array_.GetSize());EXPECT_EQ(10,my_array_.GetCapacity());
}

全部的源码传送门

时间复杂度分析

  • 添加操作       O(n)
    • addLast(e)      O(1)
    • addFirst(e)      O(n)
    • add(index,e)   O(n/2)=O(n)

最坏情况:O(n) , resize() 时间复杂度 O(n)

  • 删除操作 O(n)
    • removeLast()     O(1)
    • removeFirst()     O(n)
    • remove(index)    O(n/2)=O(n)

最坏情况:O(n) , resize() 时间复杂度 O(n)

  • 修改操作 O(1)

    • set(index,e)    O(1)
  • 查找操作

    • get(index)    O(1)
    • contains(e)  O(n)
    • find(e)           O(n)

均摊复杂度分析

由于 resize() O(n) 的存在,所以每次 addLastremoveLast 时间复杂度依然是 O(n) ?

resize()复杂度分析

假设当前 capacity 为 8 ,并且每次添加操作都是 addLast , 9 次添加操作触发一次 resize(), 总共进行了 9 次基本操作, n 次操作才会触发一次时间复杂度为 O(n) 的操作,平摊到前 n 次操作,相当于前 n 次操作每次复杂度为 O(1+1)=O(1)

在这个例子中,这样均摊计算,比计算最坏情况更有意义。

但是会出现这么一种情况:

复杂度振荡

size 为 n 时,我们先后执行 addLast , removeLast , addLast , removeLast , 每一次的操作时间复杂度都是 O(n) 的,出现问题的原因在于 removeLastresize 过于着急 (Eager)

解决方案:Lazy

size == capacity / 4 时,才将 capacity 减半。

测试是否有内存泄露

使用 valgrind 测试测试用例是否存在内存泄露

valgrind --leak-check=full --show-reachable=yes --trace-children=yes ./ArraysTest

使用 Valgrind 分析 C++ 程序时,有一些问题需要留意。例如,这个程序并没有发生内存泄漏,但是从 HEAP SUMMARY 可以看到,程序分配了 303 次内存,但却只释放了 303 次内存,为什么会这样呢?
  实际上这是由于 C++ 在分配内存时,为了提高效率,使用了它自己的内存池。当程序终止时,内存池的内存才会被操作系统回收,所以 Valgrind 会将这部分内存报告为 reachable 的,需要注意,reachable 的内存不代表内存泄漏,例如,从上面的输出中可以看到,有 72704 个字节是 reachable 的,但没有报告内存泄漏。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/448065.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

十分钟入门 RocketMQ

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 本文首先引出消息中间件通常需要解决哪些问题&#xff0c;在解决这些问题当中会遇到什么困难&#xff0c;Apache RocketMQ作为阿里开源的…

高智商孩子14个独有的特点

每一位家长都希望自己的孩子具有高智商&#xff0c;但据专家分析孩子的智商一种是与生俱来的&#xff0c;另一种是在2岁之前还可以提高的&#xff0c;一起来看看怎样才能提高孩子的智商? 智商高的孩子都具有哪些特点? 提高孩子智商的方法 1、改变儿童的饮食习惯。 提高孩…

Onvif2.6.1命名空间前缀对照

Onvif2.6.1命名空间前缀对照 tds http://www.onvif.org/ver10/device/wsdl tev http://www.onvif.org/ver10/events/wsdl tls http://www.onvif.org/ver10/display/wsdl tmd http://www.onvif.org/ver10/deviceIO/wsdl timg http://www.onvif.org/ver20/imaging/wsdl trt…

使用delegate类型设计自定义事件

在C#编程中&#xff0c;除了Method和Property&#xff0c;任何Class都可以有自己的事件&#xff08;Event&#xff09;。定义和使用自定义事件的步骤如下&#xff1a; &#xff08;1&#xff09;在Class之外定义一个delegate类型&#xff0c;用于确定事件程序的接口 &#xff0…

各种学习资源 文档、手册 (Docker 、springboot 、Guava、git、logback 、Linux 、MQ、vue、Axios)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. Docker 中文手册 &#xff1a;https://yeasy.gitbooks.io/docker_practice/advanced_network/bridge.html 2. RESTful java with JA…

C语言的“编译时多态”

typeof 在 kernel 中的使用 —— C 语言的“编译时多态” C 语言本身没有多态的概念&#xff0c;函数没有重载的概念。然而随着 C 语言编写的软件逐渐庞大&#xff0c;越来越多地需要引入一些其他语言中的特性&#xff0c;来帮助更高效地进行开发&#xff0c;Linux kernel 是一…

看脸色知体内各积毒 有效清洁内脏妙方

观察下五脏六腑是否中毒。 淤血、痰湿、寒气这些不能及时排出体外&#xff0c;危害健康和精气神的物质&#xff0c;中医称之为毒素&#xff0c;在镜子里你也可以看出它们。识别之后&#xff0c;你更需要有效的内脏清洁妙方! 症状一&#xff1a;面色青两侧长痘黄褐斑愁云满面…

UTC Time

整个地球分为二十四时区&#xff0c;每个时区都有自己的本地时间。在国际无线电通信场合&#xff0c;为了统一起见&#xff0c;使用一个统一的时间&#xff0c;称为通用协调时(UTC, Universal Time Coordinated)。UTC与格林尼治平均时(GMT, Greenwich Mean Time)一样&#xff0…

解决:Unknown custom element: <myData> - did you register the component correctly? For recursive compon

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. 引用一个组件报错&#xff1a; Unknown custom element: <myData> - did you register the component correctly?For recursi…

无处不在的container_of

无处不在的container_of linux 内核中定义了一个非常精炼的双向循环链表及它的相关操作。如下所示&#xff1a; struct list_head {struct list_head* next, * prev; };ubuntu 12.04 中这个结构定义在 /usr/src/linux-headers-3.2.0-24-generic/include/linux/types.h 中&…

程序员学习能力提升三要素

摘要&#xff1a;IT技术的发展日新月异&#xff0c;新技术层出不穷&#xff0c;具有良好的学习能力&#xff0c;能及时获取新知识、随时补充和丰富自己&#xff0c;已成为程序员职业发展的核心竞争力。本文中&#xff0c;作者结合多年的学习经验总结出了提高程序员学习能力的三…

时间,数字 ,字符串之间的转换

package com.JUtils.base;import java.sql.Timestamp; import java.text.SimpleDateFormat;/*** 转换工具类<br>* 若待转换值为null或者出现异常&#xff0c;则使用默认值**/ public class ConvertUtils {/*** 字符串转换为int*** param str * 待转换的字符串* param …

宏定义及相关用法

宏定义及相关用法 欢迎各位补充 目录 一些成熟软件中常用的宏定义&#xff1a;使用一些内置宏跟踪调试&#xff1a;宏定义防止使用时错误&#xff1a;宏与函数 带副作用的宏参数 特殊符号&#xff1a;’#’、’##’ 1、一般用法2、当宏参数是另一个宏的时候 __VA_ARGS__与##…

解决:Cannot read property ‘component‘ of undefined ( 即 vue-router 0.x 转化为 2.x)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 vue项目原本是用0.x版本的vue-router&#xff0c;但是去报出&#xff1a;Cannot read property component of undefined 这是因为版本问…

AMD Mantle再添新作,引发下代GPU架构猜想

摘要&#xff1a;今年秋天即将发布的《希德梅尔文明&#xff1a;太空》将全面支持AMD Mantle API&#xff0c;如此强大的功能背后离不开强大的CPU、GPU支持。上周AMD爆出了下一代海盗岛R9 300系列&#xff0c;据网友猜测海盗岛家族可能用上速度更快的HBM堆栈式内存。 小伙伴们…

不作35岁的程序员?

程序员三部曲--不作35岁的程序员?摩西2000 在中国&#xff0c;程序员不能超过35岁&#xff0c;似乎已经是不争的事实&#xff0c;软件开发工作就是青春饭&#xff0c;顶多靠毕业这十年的时间&#xff0c;超过这个年龄&#xff0c;要不成功跃身成为管理者&#xff0c;要不转…

linux下使用TC模拟弱网络环境

linux下使用TC模拟弱网络环境 模拟延迟传输简介 netem 与 tc: netem 是 Linux 2.6 及以上内核版本提供的一个网络模拟功能模块。该功能模块可以用来在性能良好的局域网中,模拟出复杂的互联网传输性能,诸如低带宽、传输延迟、丢包等等情 况。使用 Linux 2.6 (或以上) 版本内核…

CDN 是什么 、CDN 引入

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 CDN 的全称是 Content Delivery Network&#xff0c;即内容分发网络。 CDN的基本原理是广泛采用各种缓存服务器&#xff0c;将这些缓存…

长寿的人会有的8个健康理念

长寿的人会有的8个健康理念。年轻的时候总是在挥霍身体健康&#xff0c;吸烟、喝酒没有节制&#xff0c;到老了之后身体会出现各种问题。老年人如果想要身体健康、长寿的话&#xff0c;就要从日常生活习惯做起。下面小编就来介绍长寿的人会有的8个健康理念&#xff1a; 1、少…

Ubuntu下selenium+Chrome的安装使用

Ubuntu下seleniumChrome的安装使用 安装 chrome 官网下载安装包 sudo dpkg -i google-chrome-stable_current_amd64.deb whereis google-chrome 安装selenium pip3 install selenium 下载chromedriver(火狐使用geckodriver)驱动 http://npm.taobao.org/mirrors/chromed…