一、什么是顺序表?
顺序表是数据结构中最简单的结构之一,其内储存的元素在内存上是连续的。其优点是内存利用率高,检索元素方便(直接根据索引在常数时间内获得元素),缺点是插入和删除元素时消耗资源较大,因此适合存储大量元素并且较少插入和删除元素的程序。其基本操作有插入、删除、容量调整、遍历、访问等。
##二、基本操作的实现:
### 1、构造基本框架
#define DEFAULT_CAPACITY 2
template <typename Type>
class Vector {
private:
int capacity, length;
Type *data;
public:
Vector();//无参构造函数,按照DEFAULT_CAPACITY来申请内存
Vector(int);//含参构造函数,用户输入所需容量
~Vector();//析构函数
void resize(int);//调整容量
void insert(int, Type);//在特定位置插入元素
void remove(int);//移除特定位置的元素
void print();//遍历顺序表并输出
Type &at(int);//获取特定位置元素的引用
};
接下来就一步步来实现这些函数吧
### 2、构造顺序表
Vector() : capacity{DEFAULT_CAPACITY}, length{0} {
data = new Type[DEFAULT_CAPACITY];
} //无参构造函数,按照DEFAULT_CAPACITY来申请内存
Vector(int inputSize) : capacity{inputSize}, length{0} {
data = new Type[inputSize];
} //含参构造函数,用户输入所需容量
~Vector() {
delete[] data;
} //析构函数
这里涉及到最最简单的模板运用,和简单的构造函数重载,若没有输入规定容量则选取默认容量。
### 3、顺序表的容量调整 ###
鉴于插入和删除都有可能要调整容量,所以先实现resize操作。
void resize(int newCapacity) {
Type *newData = new Type[newCapacity];
capacity = newCapacity;
for (int i = 0; i < length; i++) {
newData[i] = data[i];
}
delete[] data;
data = newData;
} //调整容量
### 4、顺序表的插入操作###
首先需要检查目标位置的合法性, 如果位置合法,则把目标位置开始的所有元素往后移一位。
插入结束后检查容量,一种可行的办法是在元素数量达到上限时把容量扩大到原先容量的两倍。
void insert(int index, Type value) {
if (index < 0 || index > length) {
throw out_of_range{};
}
for (int i = length; i > index; i--) {
data[i] = data[i - 1];
}
data[index] = value;
length++;
if (length == capacity) {
resize(2 * capacity);
}
} //在特定位置插入元素
### 5、顺序表的删除操作###
同样要检查目标位置的合法性,如果位置合法,则把其后所有元素向前移一位。
移除结束后也要检查容量,但是在这里是否也类似地采用元素数量等于容量一半时把容量缩小到原先的一半呢?仔细思考以后是不可行的,这样将会导致在某些特定情况下不断调整容量(插入-删除-插入-删除),所以可以在元素数量等于容量1/4时进行缩小操作,能够有效避免震荡式地调整容量。
void remove(int index) {
if (index < 0 || index >= length) {
throw out_of_range{};
}
for (int i = index; i + 1 < length; i++) {
data[i] = data[i + 1];
}
length--;
if (length <= capacity / 4) {
resize(capacity / 2);
}
} //移除特定位置的元素
### 6、顺序表的输出操作
void print() {
cout << "current capacity: " << capacity << endl;
cout << "current length: " << length << endl;
cout << "current data:" << endl;
for (int i = 0; i < length; i++) {
cout << data[i] << " ";
}
cout << endl;
} //遍历顺序表并输出
### 7、访问特定位置
Type &at(int index) {
if (index < 0 || index >= length) {
throw out_of_range{};
}
return data[index];
} //获取特定位置元素的引用
##三、实现更多STL的vector操作
### 1、对[]的重载
虽然可以根据at来实现,但是STL的vector没有这么做 ,我也不知个中缘由,可能是为了让[]访问更接近数组?总之我也不进行范围判断了。
Type &operator[](int index) {
return data[index];
}
### 2、push_back和pop_back的实现
push_back即在表的末尾插入新元素,我直接用了insert操作来实现
同样的pop_back也直接用remove操作
void push_back(int value) {
insert(length, value);
}
void pop_back() {
remove(length - 1);
}
### 3、front和back的实现
Type &front() {
return at(0);
}
Type &back() {
return at(length - 1);
}
4、留作以后可能的补充
##四、完整代码
#include <iostream>
#define DEFAULT_CAPACITY 2
using std::cout;
using std::endl;
using std::out_of_range;
template <typename Type>
class Vector {
private:
int capacity, length;
Type *data;
public:
Vector() : capacity{DEFAULT_CAPACITY}, length{0} {
data = new Type[DEFAULT_CAPACITY];
} //无参构造函数,按照DEFAULT_SIZE来申请内存
Vector(int inputSize) : capacity{inputSize}, length{0} {
data = new Type[inputSize];
} //含参构造函数,用户输入所需容量
~Vector() {
delete[] data;
} //析构函数
void resize(int newCapacity) {
Type *newData = new Type[newCapacity];
capacity = newCapacity;
for (int i = 0; i < length; i++) {
newData[i] = data[i];
}
delete[] data;
data = newData;
} //调整容量
void insert(int index, Type value) {
if (index < 0 || index > length) {
throw out_of_range{"Out of range"};
}
for (int i = length; i > index; i--) {
data[i] = data[i - 1];
}
data[index] = value;
length++;
if (length == capacity) {
resize(2 * capacity);
}
} //在特定位置插入元素
void remove(int index) {
if (index < 0 || index >= length) {
throw out_of_range{"Out of range"};
}
for (int i = index; i + 1 < length; i++) {
data[i] = data[i + 1];
}
length--;
if (length <= capacity / 4) {
resize(capacity / 2);
}
} //移除特定位置的元素
void print() {
cout << "current capacity: " << capacity << endl;
cout << "current length: " << length << endl;
cout << "current data:" << endl;
for (int i = 0; i < length; i++) {
cout << data[i] << " ";
}
cout << endl;
} //遍历顺序表并输出
Type &at(int index) {
if (index < 0 || index >= length) {
throw out_of_range{"Out of range"};
}
return data[index];
} //获取特定位置元素的引用
Type &operator[](int index) {
return data[index];
}
void push_back(int value) {
insert(length, value);
}
void pop_back() {
remove(length - 1);
}
Type &front() {
return at(0);
}
Type &back() {
return at(length - 1);
}
};