一、顺序表的定义
- 在计算机内存中,顺序表可以以数组的形式来保存,也可以分配一段地址连续的存储单元来保存数据。
- 可以采用动态分配和静态分配的方式为顺序表分配空间。
(一)顺序表的存储
- 存储行为:一段连续的存储单元
- 存储位置:栈,堆,数据区,代码区 注意:
- 顺序表存储在栈上时,由操作系统和编译器来分配空间,不能由程序员手动控制,不支持扩容。栈的生命周期:函数内,函数返回后,栈中的内容就会消失。
- 顺序表存储在堆上时,由程序员使用malloc申请空间,free释放空间,支持扩容。堆的生命周期:由malloc开始到free结束。
- 那顺序表该定义在栈上还是堆上呢? 取决于你的需求。如果你想支持扩容,就定义在堆上。如果你想不支持扩容,就定义在栈上。
(二)顺序表中的结构
如图所示,在定义顺序表时,需要定义一个表头。表头里要定义一个data指针,用来存储数据空间首地址。还要定义两个变量pos和capacity;pos指向顺序表中待插入位置;capacity表示顺序表的最大容量。如果pos等于capacity,这时顺序表已满,如果继续插入元素,就需要扩容了。
二、顺序表的操作
(一)定义顺序表表头
typedef int Element;
typedef struct {
Element *data; //存储数据空间首地址
int pos; //指向待插入的位置
int capacity; //顺序表的最大存储容量
}seqTable;
(二)创建顺序表
/*在测试函数中调用createSeqTable函数,是为了创建一个顺序表,
然后拿这个表去进行其他操作,所以返回值应是seqTable.
参数n决定数据空间有多少个元素。
*/
seqTable* createSeqTable(int n){
//先定义一个空表头
seqTable *table = NULL;
table = malloc(sizeof(seqTable));
if(table == NULL){
printf("malloc failed\n");
return NULL;
}
//申请数据空间
table->data = malloc(sizeof(Element)*n);
if(table->data == NULL){
printf("data malloc failed\n");
free(table);
return NULL;
}
table->capacity = n;
table->pos = 0;
return table;
}
(三)顺序表的插入
//在尾插法和指定位置插入时,可能会多次扩容,可以单独将其定义为一个函数enlarger
static int enlarger(seqTable *table){
//1.申请需要扩容的空间,可以是两倍扩容:table->capacity*2 ,三倍扩容:table->capacity*3
seqTable *tmp = malloc(sizeof(Element)* table->capacity *2);
//2.检查申请的新空间是否申请成功
if(tmp == NULL){
printf("tmp malloc failed\n");
return -1;
}
//3.将老空间里的内容拷贝到新空间
memcpy(tmp,table,sizeof(Element)*table->pos);
//4.释放掉老空间,修改表头里的capacity和数据空间
free(table);
table->data = tmp;
table->capacity *= 2;
return 0;
}
//尾插法
int pushbackSeqTable(seqTable *table, Element value) {
//1.可以先判断一下传进来的顺序表是否为空
if(table == NULL){
printf("table is null\n");
return -1;
}
//2.插入之前,要考虑顺序表空间是否足够,不够的话需要进行扩容
if(table->pos >= table->capacity){
if(enlarger(table)){
printf("enlarger failed\n");
}
}
//3.为新空间赋值
table->data[table->pos] = value;
++table->pos;
return 0;
}
//指定位置插入
int insertPosSeqTable(seqTable *table, int index, Element value) {
//1.判断下标index是否越界
if(index < 0 || index > table->capacity){
printf("index overstep the boundary\n");
return -1;
}
//2.在插入元素之前,检查一下是否需要扩容空间
if(index >= table->capacity && enlarger(&table)){
printf("enlarger table failed\n");
return -1;
}
//3.任意位置插入时,把要插入位置后面的元素从后往前一个一个移动。
// 注意:表头中的pos指向的是待插入位置,此时pos位置是没有数据的,pos-1处才有数据,所以应从pos-1处开始往后搬
for(int i = table->pos-1;i >= index;--i){
table->data[i+1] = table->data[i];
}
//4.更改插入元素位置的值
table->data[index] = value;
++table->pos;
return 0;
}
(四) 顺序表的删除
int findIndex(const seqTable *table,Element value){
for(int i = 0;i<table->pos;i++){
if(table->data[i] == value){
return i;
}
}
return -1;
}
//按值删除元素
int deleteSeqTable(seqTable *table, Element value) {
//1.查找待删除的数据value在数据空间的位置
int index = findIndex(table,value);
if(index == -1){
printf("not find %d element\n",value);
return -1;
}
//2.删除value值,删除方法:从index+1 开始到pos,从前往后一个一个的移动
for(int i = index+1;i<= table->pos;i++){
table->data[i-1] = table->data[i];
}
--table->pos;
return 0;
}
(五) 顺序表表头的释放
void releaseSeqTable(seqTable *table) {
if(table){
if(table->data){
free(table->data);
}
free(table);
}
}
(六)显示顺序表
void showSeqTable(const seqTable *table) {
//1.检查table顺序表是否为空
if(table == NULL){
return ;
}
for(int i = 0;i<table->pos;i++){
printf("the element is %d\t",table->data[i]);
}
}