详细代码我会放到最后
数据结构一定要画图!!!
数据结构一定要画图!!!
数据结构一定要画图!!!
目录
顺序表顾名思义就是在内存中连续存在的线性表。
一、创建
为了养成良好的代码习惯,所以和之前在写通讯录的时候一样,我们分模块写代码还是老样子创建三个文件分别是seqlist.h的头文件,seqlist.c的函数接口文件,和test.c的测试文件
二、结构体定义
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致数组定大了,空 间开多了浪费,开少了不够用。
所以现实中基本都是使用动态顺序表,根据需要动态的分配空间 大小,所以下面我们实现动态顺序表。
为了方便后续修改数据类型,我们可以使用 typedef 定义一个新的数据类型,这里我们把它取名为
SLDateType
三、初始化
首先还是在seqlist.h的头文件中写上要用到的库函数和函数的定义详细参考:通讯录
然后引入自己创建的头文件 #include "SeqList.h" ,我们就可以开始动手实现顺序表初始化函数了。
用malloc开辟了动态内存之后记得判断下a是不是空指针,防止开辟失败从而导致程序崩溃
三、增容
后续我们会插入元素,如果空间不够,则使用realloc函数扩容。这里得注意下
重点:realoc这个函数的参数size_t size表示的是增容后的空间大小而不是需要增容多大的空间
tmp是指向这个·新的空间的指针,如果空间已满,则在原来的空间基础上再增加1倍,其实这样其实依然是有浪费的。
四、尾插
尾插其实就是在最后一个元素后面再插入元素
尾插有三种情况:
1.第一种情况是顺序表压根就没有空间。
2.第二种情况就是我们创建的 capacity 空间满了。
3.第三种情况是空间足够,直接插入数据即可。
五、打印
写完函数后我们如果需要测试下函数并且将结果打印到屏幕上的话不妨写下一个打印函数,这样每次测试的时候就可以直接调用了
六、尾删
尾删其实就是删除记录的最后一个元素,一般我们都是直接将记录的元素个数减一,这样就访问不到了。等到下次增容之后再输入数据就会把原数据覆盖,所以也不需要将这个元素置0。
这里有一个最容易出错而且编译器不会报错的问题,那就是这篇内存中所有元素都删除完了,这样的话就会越界访问,但是我们的size是记录了元素的个数所以我们可以用assert断言一下这片内存中还有没有元素。
越界问题:
越界这个问题编译器是不一定会报错的,就像是查酒驾一样,并不像你没被交警抓到就不算违法
这个问题的原因是因为编译器一般采用的是设岗抽查 ,而不是实时监控。
七、头插
头插即是在开头插入一个元素,但是直接插入的话会倒置原数据被覆盖。
所以我们要从最后一个数据开始往后挪动一个位置,这样就不会导致数据中的元素被覆盖的问题。
思路:首先创建一个 end 变量用来指向要移动的数据,因为指向的是数据的下标,所以是 size 要减 1 。随后进入 while 循环,如果 end >= 0 说明还没有移动完,就会进入循环。循环体内利用下标,进行向后移动操作,移动结束后再 end-- ,进行下一个数据的向后移动。挪动数据成功后,就可以插入了。此时顺序表第一个位置就被腾出来了,就可以在下标为0位置插入欲插入的数据 x 了。
八、头删
头插和头删差不多只不过头删是从下标为一的元素开始往前覆盖就行了
和尾删一样为了防止删除过多导致越界,所以我们用assert来断言下size的个数来防止越界。
九、指定位置插入数据
因为顺序表是在内存中连续存放的,所以我们要判断pos下标位置是否在开辟的内存空间内,防止越界访问的问题。
因为这里是指定位置输入我们就可以直接在头插和尾插中调用这个函数
即头插为:
尾插为:
十、指定位置删除数据
和指定位置插入数据一样,我们同样要判断pos是不是在开辟的内存空间中,但是又和指定位置插入数据有些许不同,pos不能等于size,这是因为pos是下标,size是元素个数,所以我们要么就是pos<=size-1,要么就是pos<size。
和指定位置插入一样,指定位置删除也可以直接在头删尾删中调用
头删即:
尾删为:
十一、查找
顺序表是连续存放的,所以我们就依次遍历就能找到需要查找的值了
十二、修改
pos的下标我们可以先用查找函数来寻找你想要修改的原数据的下标 。
十三、排序
排序的方法有很多,我这里用的冒泡排序。
基本思想是:两两比较待排序元素的关键字,发现它们次序相反时即进行有逆序的元素为止。
十四、清空
由于我们创建的时候是用的动态内存来开辟空间的,所以我们最后要清空顺序表。
十五、总代码
seqlist.h(头文件)
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* a;//指向动态数组的指针
int size; //数组内元素的个数
int capacity;//数组内空间的大小(个数)
}SeqList;
//顺序表初始化
void SeqListInit(SeqList* ps);
//清空顺序表
void SeqListDestroy(SeqList* ps);
//打印顺序表
void SeqListPrint(SeqList* ps);
//顺序表尾插
void SeqListPushBack(SeqList* ps, SLDateType x);
//顺序表头插
void SeqListPushFront(SeqList* ps, SLDateType x);
//顺序表头删
void SeqListPopFront(SeqList* ps);
//顺序表尾删
void SeqListPopBack(SeqList* ps);
//顺序表查找
int SeqListFind(SeqList* ps, SLDateType x);
// 顺序表在指定位置插入
void SeqListInsert(SeqList* ps, int pos, SLDateType x);
// 顺序表删除指定位置的值
void SeqListErase(SeqList* ps, int pos);
//顺序表排序
void SeqListBubbleSort(SeqList* ps);
//顺序表修改
void SeqListModify(SeqList* ps, int pos, SLDateType x);
seqlist.c(函数接口文件)
#include"seqlist.h"
void SeqListInit(SeqList* ps)
{
ps->a = (SLDateType*)malloc(sizeof(SLDateType) * 4);
if (ps->a == NULL)
{
perror("malloc");
exit(-1);
}
ps->capacity = 4;
ps->size = 0;
}
void SeqListDestroy(SeqList* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->size = 0;
}
void SeqListPrint(SeqList* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
void SLCheckCapacity(SeqList* ps)
{
if (ps->size == ps->capacity)
{
SLDateType* tmp = (SLDateType*)realloc(ps->a,ps->capacity * 2 * sizeof(SLDateType) );
if (tmp == NULL)
{
perror("realloc");
exit(-1);
}
ps->a = tmp;
ps->capacity *= 2;
}
}
void SeqListPushBack(SeqList* ps, SLDateType x)
{
SLCheckCapacity(ps);//先检查ps内的数据是不是存满了,存满了就增容
//ps->a[ps->size] = x;
//ps->size++;//插入数据后要记得记录元素个数
SeqListInsert(ps, ps->size, x);
}
void SeqListPopBack(SeqList* ps)
{
assert(ps->size > 0);
/*ps->size--;*/
SeqListErase(ps, ps->size-1);
}
void SeqListPushFront(SeqList* ps, SLDateType x)
{
SLCheckCapacity(ps);//先检查ps内的数据是不是存满了,存满了就增容
//int end = ps->size - 1;
//while (end >= 0)
//{
// ps->a[end + 1] = ps->a[end];
// --end;
//}
//ps->a[0] = x;
//ps->size++;//插入数据后要记得记录元素个数
SeqListInsert(ps, 0, x);
}
void SeqListPopFront(SeqList* ps)
{
assert(ps->size > 0);
/*int start = 1;
while (start < ps->size)
{
ps->a[start-1] = ps->a[start];
start++;
}
ps->size--;*/
SeqListErase(ps, 0);
}
int SeqListFind(SeqList* ps, SLDateType x)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
break;
}
}
if (i == ps->size)
{
printf("未找到该元素\n");
return -1;
}
return i;
}
void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{
assert(pos >= 0 && pos <= ps->size);//检查pos是否在开辟的内存空间中
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[pos] = x;
ps->size++;
}
void SeqListErase(SeqList* ps, int pos)
{
assert(pos >= 0 && pos < ps->size);//检查pos是否在开辟的内存空间中
int start = pos;
while (start < ps->size)
{
ps->a[start] = ps->a[start + 1];
start++;
}
ps->size--;
}
void SeqListBubbleSort(SeqList* ps)
{
int i = 0;
int j = 0;
for (i = 0; i < ps->size; i++)
{
for (j = 0; j < ps->size-1; j++)
{
if (ps->a[j] > ps->a[j + 1])
{
int tmp = ps->a[j];
ps->a[j] = ps->a[j + 1];
ps->a[j + 1] = tmp;
}
}
}
}
void SeqListModify(SeqList* ps, int pos, SLDateType x)
{
assert(pos >= 0 && pos < ps->size);
ps->a[pos] = x;
}
测试函数就是用了测试的,看完赶紧去试试吧