顺序表的基本概念:
【百度百科】顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
总结来说顺序表就是对结构体中的数组进行增删查改,其主要功能也是增删查改。
顺序表分为两种:
1)静态顺序表:使用定长数组存储元素。
2)动态顺序表:使用动态开辟的数组存储。
由于静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组大小定大了,空 间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
动态顺序表代码实现:
文件头及其他声明与定义:
#include<iostream>
using namespace std;
#define eleType char
我们用宏定义了eleType为char类型,之所以用宏定义是因为后续可以方便我们修改顺序表的数据储存类型。比如:可以把上述的char改为int、double、float等。
结构体的定义:
struct SequentialList {
eleType* elements;
int size;
int capacity;
};
此结构体,我们定义了动态数组elements类型是eleType(由于我们对eleType进行了宏定义,所以这个数组是char类型的),顺序表的大小size(实际储存数据的多少),还有顺序表的容量capacity(如果存满了,这个容量是可改变的,这正是动态顺序表的优势)。
顺序表初始化:
void initializeList(SequentialList* list, int capacity) {
list->elements = new eleType[capacity];
list->size = 0;
list->capacity = capacity;
}
我们定义了一个initializeList的函数,其参数为结构体的指针和结构体的容量,来对定义的顺序表进行初始化,为结构体中的数组开辟空间。
顺序表的销毁:
void destroyList(SequentialList* list) {
delete[] list->elements;
list->size = 0;
}
我们使用delete关键字对结构体中的数组进行销毁,从而达到销毁顺序表的目的。
顺序表大小获取:
int size(SequentialList* list) {
return list->size;
}
通过返回结构体中size变量来获取顺序表的大小。
顺序表是否为空:
bool isEmpty(SequentialList* list) {
return list->size == 0;
}
如果size大小为0,顺序表即为空。
顺序表插入;
这里是最难的一部分,也是最重要的部分。
void insert(SequentialList* list, int index, eleType element) {
if (index < 0 || index > list->size) { //如果输入插入的位置不正确(小于零或者大于顺序表的大小
throw std::invalid_argument("Invalid index"); //注意这里插入的位置可以等于零(在头部插入)或者等于顺序表的大小(在尾部插入)
} //如果插入位置错误,则抛出错误
if (list->size == list->capacity) { //这里对顺序表的大小和容量进行判断,如果大小等于容量了,代表此顺序表已经小了,需要扩容
int newCapacity = list->capacity * 2; //扩容操作如下,先定义新容量,一般为旧容量的两倍
eleType* newElements = new eleType[newCapacity];//定义新数组
for (int i = 0; i < list->size; i++) {
newElements[i] = list->elements[i]; //把旧数组中的数据转存到新数组中
}
delete[] list->elements; //销毁旧数组,防止占用内存
list->elements = newElements; //将结构体中的数组指向新数组
list->capacity = newCapacity; //将结构体中的容量进行更新
}
for (int i = list->size; i > index; i--) { //插入工作,把处在将要插入的位置及以后的数据统统往后移一位
list->elements[i] = list->elements[i - 1];
}
list->elements[index] = element; //将数据插入该位置
list->size++; //顺序表大小+1
}
先检查要插入的位置是否合规,再把该位置的数据及后面的数据往后移一位,再插入,最后将顺序表的大小+1。详尽的解释在代码中的注释中。
顺序表删除数据:
void deleteElement(SequentialList* list, int index) {
if (index < 0 || index >= list->size) {
throw std::invalid_argument("Invalid index");
}
for (int i = index; i < list->size - 1; i++) {
list->elements[i] = list->elements[i + 1];
}
list->size--;
}
首先和插入一样先对要插入的位置判断是否合规,有所不同的是,删除位置不能等于顺序表的大小,因为该位置在数组中是没有元素的。
然后从该位置开始循环,使后一位的数据覆盖掉此位置的数据,从而达到删除此位置数据的目的。
顺序表的查找:
int findElement(SequentialList* list, eleType element) {
for (int i = 0; i < list->size; i++) {
if (list->elements[i] == element) {
return i;
}
}
return -1;
}
这里比较简单,就是遍历数组看是否与给出的数值相等,有就返回下表,否则返会-1。
顺序表的数值获取:
eleType getElement(SequentialList* list, int index) {
if (index < 0 || index >= list->size) {
throw std::invalid_argument("Invalid index");
}
return list->elements[index];
}
首先还是对给出的索引位置进行合规判断
然后返回该索引所对应的数据。
顺序表的数据修改:
void updateElement(SequentialList* list, int index, eleType value) {
if (index < 0 || index >= list->size) {
throw std::invalid_argument("Invalid index");
}
list->elements[index] = value;
}
首先还是对给出的索引位置进行合规判断
然后对索引位置的数据修改
结束:
此上就是对顺序表的功能的实现的,接下来我们可以通过main函数对其进行调用,看看是否正确
int main() {
SequentialList myList; //定义一个顺序表
initializeList(&myList, 10); //顺序表初始化
for (int i = 0; i < 10; i++) {
insert(&myList, i, 65+i); //尾部插入数据
}
for (int i = 0; i < size(&myList); i++) {
cout << getElement(&myList, i) << " "; //获取数据
}
cout << endl;
insert(&myList, 3, 100); //中间插入数据
for (int i = 0; i < size(&myList); i++) {
cout << getElement(&myList, i) << " ";
}
cout << endl;
updateElement(&myList, 2, 98); //修改数据
for (int i = 0; i < size(&myList); i++) {
cout << getElement(&myList, i) << " ";
}
cout << endl;
deleteElement(&myList, 0); //删除数据
for (int i = 0; i < size(&myList); i++) {
cout << getElement(&myList, i) << " ";
}
cout << endl;
return 0;
}
我们可以看到输出结果与我们预想的一样,至此用c++手写顺序表就结束了。感谢阅读后续会持续更新数据结构的内容。