数据结构实现 1.1:动态数组(C++版)
1. 概念及基本框架
数组 是一种 线性结构 ,而且存储上属于 顺序存储(即内存的物理空间是连续的),也就是线性表中的 顺序表 。数组结构如下图所示:
下面以一个我实现的一个简单的数组类来进一步理解数组。
const int initialLen = 10;
template <class T>
class Array{
public:
Array(int len = initialLen){
T *p = new T[len];
m_data = p;
m_capacity = len;
m_size = 0;
}
...
private:
T *m_data; //数组数据
int m_capacity; //数组容量
int m_size; //数组大小
};
这里为了避免重复设计就可以兼容更多数据类型,引入了 泛型 ,即 模板 的概念。(模板的关键字是 class 或 typename)
这里的 数组容量 表示数组最多可存放空间的大小,而 数组大小 指的是数组实际占用空间的大小。为了保护数据,这两个变量以及 数组数据 都设置为 private 。
构造数组时,可以初始化数组的数组容量。(默认是10)
那么就会出现一个问题,如果这块内存被用完了怎么办?因为数组的物理空间必须连续,所以我们必须另外申请一块更大的内存,先把当前数组复制到新的内存上,然后释放掉旧的内存空间。同理,如果很多的内存都没有被利用,我们也可以适当缩小数组容量。所以,我们需要一个这样的 扩容(缩容)函数 去动态的实现数组。代码如下:
template <class T>
class Array{
public:
...
void resize(int len){
T *p = new T[len];
for (int i = 0; i < m_size; ++i){
p[i] = m_data[i];
}
delete[] m_data;
m_data = p;
m_capacity = len;
}
...
};
实现了前面的程序之后,接下来就是一个数组的增、删、改、查以及一些其他基本操作,接下来利用代码去实现。
2. 基本操作程序实现
2.1 增加操作
template <class T>
class Array{
public:
...
//增加操作
void add(int index, T num);
void addFirst(T num);
void addLast(T num);
...
};
首先,在类体内进行增加操作函数的原型说明。这里包括三个函数:
add(添加到任意位置)
addFirst(添加到头部)
addLast(添加到尾部)
然后分别去实现它们。
template <typename T>
void Array<T>::add(int index, T num){
if (index < 0 || index > m_size){
cout << "添加位置非法!" << endl;
return;
//throw 0; //这里可以使用抛异常,下面也可以
}
if (m_size >= m_capacity){
resize(2 * m_capacity);
}
for (int i = m_size; i > index; --i){
m_data[i] = m_data[i - 1];
}
m_data[index] = num;
m_size++;
}
template <class T>
void Array<T>::addFirst(T num){
add(0, num);
}
template <class T>
void Array<T>::addLast(T num){
if (m_size >= m_capacity){
resize(2 * m_capacity);
}
m_data[m_size] = num;
m_size++;
}
由于这些函数在类体外,所以每个函数头部必须添加一行代码:
template <class T>
表示该函数使用模板,下面同理。
增加元素时可能会用到了扩容函数,当原空间已经使用完的时候进行扩容操作。这里我每次将容量扩展到原来的 2 倍,其实也可以扩展到原来的 1.5 倍。
2.2 删除操作
template <class T>
class Array{
public:
...
//删除操作
T remove(int index);
T removeFirst();
T removeLast()