一、线性表之顺序表
线性表是一种含有n个相同元素的数据的有序数列。在数据结构中,数据是以线性表的存储方式进行存储的。常见的线性表有:顺序表,链表,站和对流,字符串等等。顺序表是线性表的一种。
在内存中是以数组的存储方式进行存储。
二、顺序表的逻辑及实现
顺序表的存储实际上是数组的存储方式进行的,那么,我们存储数据时应该怎么定义数组的大小合适呢?这就谈到了静态顺序表和动态顺序表。静态顺序表的概念是定义一个比存储数据大一些或者大得多的数组进行数据的存储,这样的话确实数据存放到了数组中,但相对来说也造成了存储空间的浪费。动态顺序表则是灵活的申请空间,当数组空间不够存放数据时,向内存申请额外的空间来存放数据,虽然也会有一定程度的浪费,但和静态对比就少了很多。以下实现的是动态顺序表的实现方式。
1、结构体的创建
在数据存放时,我们需要知道什么时候需要申请内存来给数据提供存储空间。这个时候我们就需要把存放的数据存储在结构体中,方便观察是否需要申请空间来存储。这个结构体除了定义存放内存的指针变量,还需要有一个数组的长度和一个数组的空间,来做为达到申请空间的标准。
2、顺序表初始化
将结构体内的成员初始化,方便后面存放数据和管理数据。
3、顺序表释放
当我们不需要这个顺序表时,我们需要对这一块空间进行释放,避免造成空间占用和浪费,还有可能会指向别的未知的地址形成野指针,造成无法估量的后果。
4、顺序表打印
当我们初始化,释放,以及插入删除数据时,是否操作成功,我们就可以打印一下看看。
assert是断言操作,假设表为空则报错提示。
5、内存不够时申请空间
当存放数据已经到最后一位仍有数据时,那么我们就需要申请内存了。这里我是申请的原内存的两倍内存,数据大时会造成空间浪费,申请的内存少的话则会造成多次申请操作,大家自己掂量就好。申请后的内存可能是原地址,数据多则会改变原来的地址,数据不变。
6、顺序表尾部插入数据
插入时应检查表是不是空。插入一个数据,相应的数组计算数据的大小也要加一。
7、顺序表头部插入数据
插入时应检查表是不是空。插入一个数据,相应的数组计算数据的大小也要加一。
后面的数据依次覆盖后一个数据。
8、顺序表尾部删除数据
删除时应检查表是不是空。删除一个数据,相应的数组计算数据的大小也要减一。
9、顺序表头部删除数据
删除时应检查表是不是空。删除一个数据,相应的数组计算数据的大小也要减一。
后面的数据依次覆盖前一个数据。
10、顺序表查找数据
查找时应检查表是不是空。不为空直接遍历一遍表格即可。
11、顺序表在当前位置插入数据
插入时应检查表是不是空。插入一个数据,相应的数组计算数据的大小也要加一。
后面的数据依次覆盖后一个数据。
12、顺序表在当前位置删除数据
删除时应检查表是不是空。删除一个数据,相应的数组计算数据的大小也要减一。
后面的数据依次覆盖前一个数据。
三、源码分享
void SeqListInit(SeqList *ps)
{
ps ->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
void SeqListDestroy(SeqList* ps)
{
//if(ps->a != NULL)
if (ps->a )
{
free(ps->a);
ps->a = 0;
ps->size = ps->capacity = 0;
}
}
void SeqListPrint(SeqList* ps)
{
assert(ps);
for (int i = 0; i < ps->size ; ++i)
{
printf("%d\n", ps->a[i]);
}
}
void CheckCapacity(SeqList* ps)
{
if (ps->size == ps->capacity)
{
int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDateType* tmp = (SLDateType*)realloc(ps->a, NewCapacity * sizeof(SLDateType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = NewCapacity;
}
}
void SeqListPushBack(SeqList* ps, SLDateType x)
{
assert(ps);
/*if (ps->size == ps->capacity)
{
int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDateType* tmp = (SLDateType*)realloc(ps->a,NewCapacity * sizeof( SLDateType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = NewCapacity;
}*/
CheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
void SeqListPopBack(SeqList* ps)
{
assert(ps);
// 温柔的检查
/*if (ps->size == 0)
{
return;
}*/
// 暴力的检查
assert(ps->size > 0);
//ps->a[ps->size - 1] = 0;
ps->size--;
}
void SeqListPushFront(SeqList* ps, SLDateType x)
{
assert(ps);
CheckCapacity(ps);
/*if (ps->size == ps->capacity)
{
int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDateType* tmp = (SLDateType*)realloc(ps->a, NewCapacity * sizeof(SLDateType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = NewCapacity;
}*/
/*int i = 0;*/
for ( int i = ps->size -1; i >= 0; --i)
{
ps->a[i+1] = ps->a[i ];
}
ps->a[0] = x;
ps->size++;
}
void SeqListPopFront(SeqList* ps)
{
assert(ps);
/*if (ps->size == 0)
{
return;
}*/
assert(ps->size > 0);
for (int i = 0; i <ps->size; ++i)
{
ps->a[i ] = ps->a[i+1];
}
ps->size--;
}
int SeqListFind(SeqList* ps, SLDateType x)
{
for (int i = 0; i < ps->size; ++i)
{
if (ps->a[i] == x)
{
printf("找到了,在第%d个位置\n",i);
printf("%d\n", ps->a);
return i;
}
}
printf("表中没有这个数字\n");
return -1;
}
void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{
assert(ps);
CheckCapacity(ps);
if (pos == ps->size +1)
{
ps->a[pos -1] = x;
ps->size++;
}
else
{
for (int i = ps->size - 1; i >= pos - 1; --i)
{
ps->a[i + 1] = ps->a[i];
}
ps->a[pos - 1] = x;
ps->size++;
}
}
void SeqListErase(SeqList* ps, int pos)
{
assert(ps);
if (pos == ps->size)
{
assert(ps->size > 0);
ps->size--;
}
else
{
for (int i = pos -1 ; i < ps->size; ++i)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
}
四、关于顺序表存在的一些问题
开头讲到的,无论如何,顺序表的存储都会有一定程度上的内存浪费,当数据相当大时,查找数据和插入删除数据,都会增加相当大的工作量,不利于大量的数据存储。
本次分享就到这里啦,写的不好的还请多多指正,一起加油吧!