1.线性表
定义:n个具有相同特性的数据元素的有限序列。
常见的线性表:顺序表,链表,栈,队列,字符串。
线性表在逻辑上是线性结构,但在物理上不一定是连续的,线性表在屋里上存储时,
通常以数组和链式结构的形式储存。
2.顺序表
定义:顺序表时一段物理地址连续的储存单元一次存储数据元素的线性结构,
一般情况下采用数组存储。在数组上完成增删查改。
类似于数组。
3.分类
静态顺序表
动态顺序表
静态顺序表缺点:开小了不够用,开大了浪费空间。
使用动态顺序表是较优解。
4.接口实现
建立结构体变量
- typedef int SeqListDataType;//方便改数据类型
- //动态顺序表
- typedef struct SeqList
- {
- SeqListDataType* a;//指针数组
- int size;//大小
- int capacity;//容量
- }SeqList;
初始化
- void SeqListInit(SeqList* psl)
- {
- psl->a = NULL;
- psl->capacity = psl->size = 0;
- }
动态扩容
- void SeqListCheck(SeqList* psl)
- {
- assert(psl);
- //考虑空间不够
- if (psl->size == psl->capacity)
- {
- int newcapacity = psl->capacity == 0 ? 4 : psl->capacity;
- //realloc扩容
- SeqListDataType* tmp = (SeqListDataType*)realloc(psl->a, sizeof(SeqList) * newcapacity);
- if (tmp == NULL)//扩容失败
- {
- printf("realloc fail\n");
- exit(-1);//终止程序
- }
- else
- {
- psl->a = tmp;
- psl->capacity = newcapacity;//由于capacity = 0 乘2失效,所以初始capacity必须>0
- }
- }
- }
尾插
由于数组下标从0开始,可以得知size指向的是顺序表的下一位。
- void SeqListPushBack(SeqList* psl, SeqListDataType x)
- {
- assert(psl);
- SeqListCheck(psl);//检测空间是否能够尾插,不够就自动扩容
-
- //尾插
- //由于数组下标的特殊性,size指向的是顺序表的下一位
- psl->a[psl->size] = x;
- psl->size++;
- }
尾删
一般情况下都涉及采取覆盖处理,在删除的过程中必须注意越界访问的极端情况。
当出现这个情况后,结束尾删操作。即size = 0结束循环。
- void SeqListPopBack(SeqList* psl)
- {
- //考虑越界访问问题,这里size>=0是必须的
- if(psl->size>0)
- //为方便处理,采用覆盖方式处理
- psl->size--;
- }
头插
基本思路:将size个数据向后移动一位,a[0] = x
坑:1.断言指针是否为空指针
2.插入数据考虑扩容,以防越界访问。
3.判断循环终止条件
4.数据应该是从后往前一个一个后移,以防止覆盖处理。
5.如果顺序表本就没有数据,即size = 0,此时需要额外考虑,直接赋值就行。
终止状况:end < 0 时结束循环1
- void SeqListInsert(SeqList* psl, int pos, int x)
- {
- assert(psl);
-
- if (pos >= psl->size)
- {
- printf("pos 越界\n");
- return;
- }
- int end = psl->size - 1;
- SeqListCheck(psl);
- while (pos <= end)
- {
- psl->a[end + 1] = psl->a[end];
- end--;
- }
- psl->a[pos] = x;
- psl->size++;
-
- }
头删
基本思路:将size-1个数据向前移动一位,覆盖a[0]的数据,实现删除。
坑点:1.顺序表本身就为空
2.指针引用问题
3.多次删除可能导致越界访问问题
当cur = size时跳出循环
注意当size>0才能运行
- void SeqListPopFront(SeqList* psl)
- {
- assert(psl);
- if (psl->size > 0)
- {
- int cur = 1;
- while (cur < psl->size )
- {
- psl->a[cur - 1] = psl->a[cur];//数据前移
- cur++;
- }
- psl->size--;
- }
- else
- {
- printf("顺序表已空\n");
- return;
- }
- }
找到pos的位置
基本思路:遍历顺序表,找到就返回下标,找不到就返回-1
坑点:判断传递的指针是否为空
- int SeqListFind(SeqList* psl,SeqListDataType pos)
- {
- assert(psl);
- for (int i = 0; i < psl->size; i++)//遍历
- {
- if (psl->a[i] == pos)
- {
- return i;
- }
- }
- return -1;
- }
删掉pos前的值
基本思路:仿照头删,将pos后的数据向前移动一位,实现覆盖删除。
坑点:1.断言
2.pos值的合法性,是否会出现越界现象。
3.与头删代码风格相似,注意循环结束的判读条件。
4.注意size--;
- void SeqListErase(SeqList* psl, SeqListDataType pos)
- {
- assert(psl);
- int begin = pos + 1;
- while (begin < psl->size)//类似于头删
- {
- psl->a[begin - 1] = psl->a[begin];
- begin++;
- }
- psl->size--;
- }
程序结束销毁动态开辟的顺序表
基本思路:常规操作,将capacity和size置为0,将a置空
坑点:1.断言,指针传参可能传递了一个空指针或者野指针。
2.根据规范而言,使用完的空间必须释放。
- void SeqListDestroy(SeqList* psl)
- {
- assert(psl);//断言
- psl->capacity = 0;
- psl->size = 0;
- free(psl->a);//释放空间
- psl->a = NULL;//防止指针被随意使用,造成野指针。
- }