数据结构实现(一):动态数组(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;      //数组大小
};

这里为了避免重复设计就可以兼容更多数据类型,引入了 泛型 ,即 模板 的概念。(模板的关键字是 classtypename
这里的 数组容量 表示数组最多可存放空间的大小,而 数组大小 指的是数组实际占用空间的大小。为了保护数据,这两个变量以及 数组数据 都设置为 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();
	void removeElement(T num);
	...
};

同理,在类体内进行删除函数的原型说明。这里包括四个函数:
remove(删除任意位置元素):返回删除元素的值。
removeFirst(删除头部元素):返回删除元素的值。
removeLast(删除尾部元素):返回删除元素的值。
removeElement(删除特定元素):这里我利用下面的 find 函数来实现的,所以删除的是第一个这样的元素,如果想把这样的元素都删掉,可以写一个新的函数来实现。
然后分别去实现它们。

template <class T>
T Array<T>::remove(int index){
	if (index < 0 || index >= m_size){
		cout << "删除位置非法!" << endl;
		return NULL;
	}
	T res = m_data[index];
	m_size--;
	for (int i = index; i < m_size; ++i){
		m_data[i] = m_data[i + 1];
	}
	if (m_size < m_capacity / 4){
		resize(m_capacity / 2);
	}
	return res;
}
template <class T>
T Array<T>::removeFirst(){
	T res = m_data[0];
	remove(0);
	return res;
}
template <class T>
T Array<T>::removeLast(){
	if (m_size == 0){
		cout << "删除位置非法!" << endl;
		return NULL;
	}
	m_size--;
	if (m_size < m_capacity / 4){
		resize(m_capacity / 2);
	}
	return m_data[m_size];
}
template <class T>
void Array<T>::removeElement(T num){
	int flag = find(num);
	if (flag != -1){
		remove(flag);
	}
}

删除时用到了缩容函数,当原空间被利用太少的话,就使用一块新的更小的空间。这里当空间利用率不足 1/4 时,我将内存缩至原空间的 1/2 ,即还有一些空间可以去添加。
这里需要注意的是,不要使用当空间利用率不足 1/2 时,就将内存缩至原空间的 1/2 ,这样可能会导致震荡。
例如当空间恰好被利用完了以后,增加了一个元素,这时空间变为 2 倍,然后又删除了一个元素,空间又降至原大小,然后又增加了一个元素,空间又变为 2 倍,再删除一个元素,空间又降至原大小……循环往复,造成震荡。
这里删除操作的“删除位置非法”后面返回的 NULL 也可以用 throw 抛异常来实现,这里只是为了方便。

2.3 修改操作

template <class T>
class Array{
public:
	...
	//修改操作
	void set(int index, T num);
	...
};

修改操作只有一个函数
set(修改指定位置的值)
同理,在类体内进行删除函数的原型说明,然后在类体外实现。

template <class T>
void Array<T>::set(int index, T num){
	if (index < 0 || index >= m_size){
		cout << "修改位置非法!" << endl;
		return;
	}
	m_data[index] = num;
}

2.4 查找操作

template <class T>
class Array{
public:
	...
	//查找操作
	T get(int index);
	int find(T num);
	bool contains(T num);
	...
};

查找函数有三个:
get(返回特定位置元素)
find(返回第一个特定元素位置)
contains(返回是否包含特定元素)
分别对它们进行实现。

template <class T>
T Array<T>::get(int index){
	if (index < 0 || index >= m_size){
		cout << "访问位置非法!" << endl;
		return NULL;
	}
	return m_data[index];
}
template <class T>
int Array<T>::find(T num){
	for (int i = 0; i < m_size; ++i){
		if (m_data[i] == num){
			return i;
		}
	}
	return -1;
}
template <class T>
bool Array<T>::contains(T num){
	for (int i = 0; i < m_size; ++i){
		if (m_data[i] == num){
			return true;
		}
	}
	return false;
}

同理,这里 get 函数的“访问位置非法”后面返回的 NULL 也可以用 throw 抛异常来实现,这里只是为了方便。

2.5 其他操作

数组还有一些其他的操作,这些函数我在类体内进行了实现。
包括 数组容量数组大小 的查询,还有 数组的打印 等操作。

template <class T>
class Array{
public:
	...
	int capacity(){
		return m_capacity;
	}
	int size(){
		return m_size;
	}
	...
	bool isEmpty(){
		return m_size == 0;
	}
	void print(){
		cout << "Array: ";
		cout << "Capacity = " << m_capacity << ", " << "Size = " << m_size << endl;
		cout << '[';
		for (int i = 0; i < m_size; ++i){
			cout << m_data[i];
			if (i != m_size - 1){
				cout << ',';
			}
		}
		cout << ']' << endl;
	}
	...
};

3. 算法复杂度分析

3.1 增加操作

函数 最坏复杂度 平均复杂度
add O(n+n) = O(n) O(n/2+1) = O(n)
addFirst O(n+n) = O(n) O(n+1) = O(n)
addLast O(1+n) = O(n) O(1+1) = O(1)

add 的最坏复杂度 O(n+n) 中第一个 n 是指元素移动操作,第二个 n 是指 resize 函数,以下同理。
增加可能会引发扩容操作,平均而言,每增加 n 个元素,会扩展一次,会发生 n 个元素的移动,所以平均下来是 O(1)

3.2 删除操作

函数 最坏复杂度 平均复杂度
remove O(n+n) = O(n) O(n/2+1) = O(n)
removeFirst O(n+n) = O(n) O(n+1) = O(n)
removeLast O(1+n) = O(n) O(1+1) = O(1)

同理,删除操作与增加操作类似。

3.3 修改操作

函数 最坏复杂度 平均复杂度
set O(1) O(1)

3.4 查找操作

函数 最坏复杂度 平均复杂度
get O(1) O(1)
find O(n) O(n/2) = O(n)
contains O(n) O(n/2) = O(n)

总体情况:

操作 时间复杂度
O(n)
O(n)
已知索引O(1);未知索引O(n)
已知索引O(1);未知索引O(n)

由此可以看出,数组比较适用于已知索引情况下的数据存放,也就是说,适用于索引有意义的情况。

4. 完整代码

程序完整代码(这里使用了头文件的形式来实现类)如下:

#ifndef __ARRAY_H__
#define __ARRAY_H__

using namespace std;

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;
	}
	int capacity(){
		return m_capacity;
	}
	int size(){
		return m_size;
	}
	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;
	}
	bool isEmpty(){
		return m_size == 0;
	}
	void print(){
		cout << "Array: ";
		cout << "Capacity = " << m_capacity << ", " << "Size = " << m_size << endl;
		cout << '[';
		for (int i = 0; i < m_size; ++i){
			cout << m_data[i];
			if (i != m_size - 1){
				cout << ',';
			}
		}
		cout << ']' << endl;
	}
	//增加操作
	void add(int index, T num);
	void addFirst(T num);
	void addLast(T num);
	//删除操作
	T remove(int index);
	T removeFirst();
	T removeLast();
	void removeElement(T num);
	//修改操作
	void set(int index, T num);
	//查找操作
	T get(int index);
	int find(T num);
	bool contains(T num);
private:
	T *m_data;       //数组数据
	int m_capacity;  //数组容量
	int m_size;      //数组大小
};

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>
T Array<T>::remove(int index){
	if (index < 0 || index >= m_size){
		cout << "删除位置非法!" << endl;
		return NULL;
	}
	T res = m_data[index];
	m_size--;
	for (int i = index; i < m_size; ++i){
		m_data[i] = m_data[i + 1];
	}
	if (m_size < m_capacity / 4){
		resize(m_capacity / 2);
	}
	return res;
}
template <class T>
T Array<T>::removeFirst(){
	T res = m_data[0];
	remove(0);
	return res;
}
template <class T>
T Array<T>::removeLast(){
	if (m_size == 0){
		cout << "删除位置非法!" << endl;
		return NULL;
	}
	m_size--;
	if (m_size < m_capacity / 4){
		resize(m_capacity / 2);
	}
	return m_data[m_size];
}
template <class T>
void Array<T>::removeElement(T num){
	int flag = find(num);
	if (flag != -1){
		remove(flag);
	}
}

template <class T>
void Array<T>::set(int index, T num){
	if (index < 0 || index >= m_size){
		cout << "修改位置非法!" << endl;
		return;
	}
	m_data[index] = num;
}

template <class T>
T Array<T>::get(int index){
	if (index < 0 || index >= m_size){
		cout << "访问位置非法!" << endl;
		return NULL;
	}
	return m_data[index];
}
template <class T>
int Array<T>::find(T num){
	for (int i = 0; i < m_size; ++i){
		if (m_data[i] == num){
			return i;
		}
	}
	return -1;
}
template <class T>
bool Array<T>::contains(T num){
	for (int i = 0; i < m_size; ++i){
		if (m_data[i] == num){
			return true;
		}
	}
	return false;
}

#endif

猜你喜欢

转载自blog.csdn.net/qq_35481167/article/details/83860020