线性表是最基本、最简单、也是最常用的一种数据结构。线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
线性表的特点
- 除第一个元素外,其他每一个元素有且仅有一个直接前驱。
- 除最后一个元素外,其他每一个元素有且仅有一个直接后继。
- 直接前驱和直接后继描述了结点之间的逻辑关系(即邻接关系)。
顺序表是以数组的形式保存的线性表,将线性表中的元素相继存放在一个连续的存储空间中,使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
顺序表的特点:所有元素的逻辑先后顺序与其物理存放顺序一致。
线性表顺序存储结构的优缺点:
- 优点:可以快速的得到表中任意位置的元素
- 缺点:1.插入和删除操作需要移动大量元素
2.当线性表长度变化较大时,难以确定存储空间容量
顺序表的基本操作:
插入:在表头、表尾和第pos个位置插入数据
删除:删除表头、表尾和第pos个位置的数据
修改:修改表头、表尾和第pos个位置的数据
得到数据:得到表头、表尾和第pos个位置的数据
查找:在顺序表中查找数据p,返回位置
计算长度:返回顺序表的长度
打印
template<class T>
class SeqList {
public:
T *data;
int maxSize{}; //能存储的最大数据量
int last{}; //当前存储的元素个数(并非元素下标)
public:
SeqList(); //构造函数
SeqList(SeqList<T> &list); //拷贝构造函数
~SeqList(); //析构函数
void reSize(); //增加空间大小
void push_first(T p); //头插法,在顺序表头插入数据
void push_last(T p); //尾插法,在顺序表尾插入数据
bool push_pos(int pos, T p); //在顺序表第pos个位置插入数据
void pop_first(); //删除顺序表头数据
void pop_last(); //删除顺序表尾数据
bool pop_pos(int pos); //删除顺序表第pos个数据
void change_first(T p); //修改顺序表头数据
void change_last(T p); //修改顺序表尾数据
bool change_pos(int pos, T p); //修改顺序表第pos个数据
bool get_first(T &p); //得到顺序表头数据
bool get_last(T &p); //得到顺序表为数据
bool get_pos(int pos, T &p); //得到顺序表第pos个数据
int search(T p); //返回顺序表中p相同的第一个元素的下标pos,若不存在则返回-1
bool isEmpty(); //判断顺序表是否为空
int size(); //返回顺序表能存储的最大数据量
int length(); //返回顺序表的长度
void print(); //打印顺序表
void clear(); //清空顺序表
void delete_link(); //摧毁顺序表
};
//构造函数
template<class T>
SeqList<T>::SeqList() {
maxSize = 50; //初始最大存储量为50
last = 0;
data = new T[maxSize]; //给数组分配空间
if (data == NULL) //动态分配失败
{
cerr << "存储分配错误!" << endl;
exit(1);
}
}
//拷贝构造函数
template<class T>
SeqList<T>::SeqList(SeqList<T> &list) {
maxSize = list.maxSize;
last = list.last;
data = new T[maxSize];
if (data == NULL) //动态分配失败
{
cerr << "存储分配错误!" << endl;
exit(1);
}
for (int i = 0; i < maxSize; i++)
data[i] = list.data[i];
}
//析构函数
template<class T>
SeqList<T>::~SeqList() {
delete_link();
}
//判断顺序表是否为空
template<class T>
bool SeqList<T>::isEmpty() {
return last == 0;
}
//返回顺序表能存储的最大数据量
template<class T>
int SeqList<T>::size() {
return maxSize;
}
//清空顺序表
template<class T>
void SeqList<T>::clear() {
delete data;
maxSize = 50;
last = 0;
data = new T[maxSize];
}
//摧毁顺序表
template<class T>
void SeqList<T>::delete_link() {
delete data;
}
打印顺序表中的数据
//打印顺序表
template<class T>
void SeqList<T>::print() {
if (isEmpty()) //空表直接返回
return;
cout << "线性表数据为: ";
for (int i = 0; i < last - 1; i++)
cout << data[i] << " ----> ";
cout << data[last - 1] << endl;
}
当顺序表的空间不足时,调用该函数
//增加空间大小
template<class T>
void SeqList<T>::reSize() {
maxSize += 10;
T *temp = new T[maxSize];
if (data == NULL) //动态分配失败
{
cerr << "存储分配错误!" << endl;
exit(1);
}
for (int i = 0; i < maxSize; i++)
temp[i] = data[i];
delete data;
data = temp;
}
- 判断顺序表是否满了,满了则增加空间大小;
- 顺序表中的所有元素用for循环进行向后移动一位;
- 要插入的元素赋值给数组首元素;
- 顺序表当前元素个数加1( last++ )。
- 判断顺序表是否满了,满了则增加空间大小;
- 要插入的元素赋值给数组第 last 个位置的元素;
- 顺序表当前元素个数加1( last++ )。
- 判断顺序表是否满了,满了则增加空间大小;
- 顺序表中的从第pos-1个位置开始用for循环进行向后移动一位;
- 要插入的元素赋值给数组第 pos-1 个位置的元素;
- 顺序表当前元素个数加1( last++ )。
三种插入法代码:
//头插法,在顺序表头插入数据
template<class T>
void SeqList<T>::push_first(T p) {
if (last + 1 > maxSize) //空间不足
reSize();
last++;
for (int i = last - 1; i > 0; i--) //全部数据后移一位
data[i] = data[i - 1];
data[0] = p;
}
//尾插法,在顺序表尾插入数据
template<class T>
void SeqList<T>::push_last(T p) {
if (last + 1 > maxSize) //空间不足
reSize();
data[last++] = p;
}
//在顺序表第pos个位置插入数据
template<class T>
bool SeqList<T>::push_pos(int pos, T p) {
if (pos > last + 1 || pos <= 0) //不存在第pos个数据
return false;
if (last + 1 > maxSize) //空间不足
reSize();
last++;
for (int i = last - 1; i >= pos; i--) //第pos个数据后面的数据全部后移一位
data[i] = data[i - 1];
data[pos - 1] = p;
return true;
}
- 顺序表不为空;
- 顺序表中的从第 1 个位置的元素开始用for循环进行向前移动一位;
- 顺序表当前元素个数减1( last - - )。
- 顺序表不为空;
- 顺序表当前元素个数减1( last - - ),覆盖最后一个元素。
- 存在第 pos 个数据(即pos <= last && pos > 0);
- 顺序表中的从第 pos-1 个位置的元素开始用for循环进行向前移动一位;
- 顺序表当前元素个数减1( last - - )。
三种删除法的代码:
//删除顺序表头数据
template<class T>
void SeqList<T>::pop_first() {
if (isEmpty()) //空表直接返回
return;
for (int i = 0; i < last - 1; i++) //从第2个数据开始全部前移一位
data[i] = data[i + 1];
last--; //数据个数-1
}
//删除顺序表尾数据
template<class T>
void SeqList<T>::pop_last() {
if (isEmpty()) //空表直接返回
return;
last--; //数据个数-1
}
//删除顺序表第pos个数据
template<class T>
bool SeqList<T>::pop_pos(int pos) {
if (pos > last || pos <= 0) //不存在第pos个数据
return false;
for (int i = pos - 1; i < last - 1; i++) //从第pos个数据开始全部前移一位
data[i] = data[i + 1];
last--; //数据个数-1
return true;
}
- 顺序表不为空;
- 修改表头数据。
- 顺序表不为空;
- 修改表尾数据。
- 存在第 pos 个数据(即pos <= last && pos > 0);
- 修改第pos个数据
三种修改法的代码:
//修改顺序表头数据
template<class T>
void SeqList<T>::change_first(T p) {
if (isEmpty()) //空表直接返回
return;
data[0] = p;
}
//修改顺序表尾数据
template<class T>
void SeqList<T>::change_last(T p) {
if (isEmpty()) //空表直接返回
return;
data[last - 1] = p;
}
//修改顺序表第pos个数据
template<class T>
bool SeqList<T>::change_pos(int pos, T p) {
if (pos > last || pos <= 0) //不存在第pos个数据
return false;
data[pos - 1] = p;
return true;
}
- 顺序表不为空;
- 返回表头数据。
- 顺序表不为空;
- 返回表尾数据。
- 存在第 pos 个数据(即pos <= last && pos > 0);
- 返回第pos个数据。
三种方法的代码:
//得到顺序表头数据
template<class T>
bool SeqList<T>::get_first(T &p) {
if (isEmpty()) //空表直接返回
return false;
p = data[0];
return true;
}
//得到顺序表尾数据
template<class T>
bool SeqList<T>::get_last(T &p) {
if (isEmpty()) //空表直接返回
return false;
p = data[last - 1];
return true;
}
//得到顺序表第pos个数据
template<class T>
bool SeqList<T>::get_pos(int pos, T &p) {
if (pos > last || pos <= 0) //不存在第pos个数据
return false;
p = data[pos - 1];
return true;
}
返回顺序表中与p相同的第一个元素的下标 pos ,若不存在则返回 -1
//返回顺序表中p相同的第一个元素的下标pos,若不存在则返回-1
template<class T>
int SeqList<T>::search(T p) {
for (int i = 0; i < last; i++) {
if (data[i] == p) //与p相同的第一个元素的下标pos
return i;
}
return -1; //不存在则返回-1
}
返回顺序表的长度
//返回顺序表的长度
template<class T>
int SeqList<T>::length() {
return last;
}
C++线性表的顺序存储结构较链式存储结构简单一些,但是顺序存储结构可能会存在较大的空间浪费和数据移动。
C++顺序表的定义和测试代码都push到github上了,需要的朋友可自行下载:C++线性表的顺序存储结构。欢迎大家来我的博客交流。