本人数据结构编写规则
① 上数据结构课的时候,老师谈到函数传参时反复强调:当结构体需要被修改时才传结构体的地址。例如:
SqList S;
InitSqList(&S);
PrintSqList(S);
我认为这是欠妥的,如果传参没有传结构体的地址,在函数中系统将会开辟一块结构体的临时拷贝,因此会占用多余的内存空间,而传结构体的地址则能避免这个问题。 ② 在函数中判断结构体指针是否为空时采用assert函数,而不是用if语句判断。 ③ 函数的命名规则遵循:操作+结构类型名 的规则。例如 InitSqList 与DestroySqList。
正文
线性表是一个相当灵活的数据结构,其长度可以根据需要而增加或缩短。线性表的顺序表示式指用一段地址连续的存储单元依次存储线性表的数据元素,这种表示也称作线性表的顺序存储结构或顺序影像。这种存储结构的线性表称为顺序表(Sequential List)。特点是,逻辑上相邻的数据元素,物理次序上也是相邻的。
上面这段话摘自严蔚敏老师的数据结构一书。线性表中顺序表是最基本的一种数据结构,本章将对顺序表的原理进行介绍和具体实现。
我们定义的是动态的顺序表,相较于静态顺序表的优点是:空间能够灵活处理。我们需要多少空间就用多少空间,而不是像开辟定长数组一样把长度定死。
结构体定义
typedef int SLDataType;
typedef struct SL
{
SLDataType* SLa;
int size;
int capacity;
}SqList;
头文件中定义如是。其中定义SLDataType便于后续修改元素类型。SLa表示供动态使用的数组(指针),定义一个size和capacity分别表示当前数据个数和顺序表的容量。
功能实现 有了这个结构体,我们便可以对顺序表进行基本的操作:增删查改。
函数声明
void InitSqList(SqList* ps);
void DestroySqList(SqList* ps);
void FrontInsertSqList(SqList* ps, SLDataType i);
void FrontDeleteSqList(SqList* ps);
void BackInsertSqList(SqList* ps, SLDataType i);
void BackDeleteSqList(SqList* ps);
void PrintSqList(SqList* ps);
void CheckCapacitySqList(SqList* ps);
void InsertAtSqList(SqList* ps, int pos, SLDataType i);
void DeleteAtSqList(SqList* ps, int pos);
int SearchSqList(SqList* ps, SLDataType i);
咱们一个个来实现。
初始化
void InitSqList(SqList* ps)
{
assert(ps);
ps->SLa = NULL;
ps->size = 0;
ps->capacity = 0;
}
头部插入
void FrontInsertSqList(SqList* ps,SLDataType i)
{
assert(ps);
CheckCapacitySqList(ps);
for (int end = ps->size - 1; end >= 0; end--)
{
ps->SLa[end + 1] = ps->SLa[end];
}
ps->SLa[0] = i;
ps->size++;
}
这里检查容量封装成一个函数,因为在各种插入中均需要调用。
检查容量
void CheckCapacitySqList(SqList* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
size_t NewCapacity = ps->capacity == 0 ? SqListMaxNum : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->SLa, sizeof(SLDataType) * NewCapacity);
if (tmp == NULL)
{
printf("扩容失败!\n");
exit(-1);
}
else
{
ps->SLa = tmp;
ps->capacity *= 2;
}
}
}
这里使用realloc而不是malloc是因为当第一个参数是空指针时,realloc可以当malloc用,且realloc比malloc使用更加方便,realloc会自己判断空间应该在哪开辟不会与其他内存空间冲突,而malloc只是单纯找一块空间之后开辟。
头部删除
void FrontDeleteSqList(SqList *ps)
{
assert(ps);
if (ps->size > 0)
{
for (int begin = 0;begin < ps->size - 1; begin++)
{
ps->SLa[begin] = ps->SLa[begin + 1];
}
ps->size--;
}
else
{
printf("表空,表头删除失败!\n");
return;
}
}
将第一个元素开始后面的元素往前覆盖即可,最后size-1。
尾部插入
void BackInsertSqList(SqList* ps, SLDataType i)
{
assert(ps);
CheckCapacitySqList(ps);
ps->SLa[ps->size] = i;
ps->size++;
}
尾部删除
void BackDeleteSqList(SqList* ps)
{
assert(ps);
if (ps->size > 0)
ps->size--;
else
{
printf("表空,表尾删除失败!\n");
return;
}
}
这里只把size-1,尾部的元素不去修改因为不知道要如何赋值,因为不知道要赋值什么类型,也不知道要赋的具体值。
指定位置插入
void InsertAtSqList(SqList* ps, int pos, SLDataType i)
{
assert(ps);
CheckCapacitySqList(ps);
if (pos > ps->size + 1 || pos <= 0)
{
printf("插入位置越界,表当前大小为:%d!\n",ps->size);
return;
}
else
{
for (int end = ps->size - 1;end >= pos - 1;end--)
{
ps->SLa[end + 1] = ps->SLa[end];
}
ps->SLa[pos - 1] = i;
ps->size++;
}
}
此处我写的是在元素的第pos个位置插入,pos指位置而不是下标。 可以理解成位置=下标+1;
指定位置删除
void DeleteAtSqList(SqList* ps, int pos)
{
assert(ps);
if (ps->size > 0)
{
if (pos > ps->size || pos <= 0)
{
printf("删除位置越界,表当前大小为:%d!\n", ps->size);
return;
}
for (int begin = pos; begin <= ps->size - 1; begin++)
{
ps->SLa[begin - 1] = ps->SLa[begin];
}
ps->size--;
}
else
{
printf("表空,删除失败!\n");
return;
}
}
查找表中元素
int SearchSqList(SqList* ps,SLDataType i)
{
assert(ps);
for (int begin = 0; begin < ps->size; begin++)
{
if (ps->SLa[begin] == i)
return begin;
}
return -1;
}
如果找到了该元素,返回的是该元素的下标,而不是位置。
效果展示
结语
线性表的效果跟数组有相似之处,可以说是改造于数组。实现线性表也是在学习完C语言之后对C语言掌握程度和理解的一次检验。 顺序表是学习数据结构的开端,实现较为简单,之后的数据结构会越来越复杂。 继续加油吧ヾ(?°?°?)ノ゙
|