目录
一.线性表的概念
- 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
- 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
- 图示
二.顺序表
(1)概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
(2)分类
1. 静态顺序表(使用定长数组存储元素)
#define N 7
typedef int SLDataType;
typedef struct SeqList
{
SLDataType arr[N];//定长数组
size_t size;//有效数据的个数
}SeqList;
- 图示
缺点:静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。
2.动态顺序表 (使用动态开辟的数组存储数据)
#define N 7
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* arr;//指向动态开辟的数组
size_t size;//元素的个数
size_t capacity;//容量的大小
}SeqList;
- 图示
(3)接口实现
1.初始化顺序表
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->num = 0;
ps->capacity = 0;
}
2.销毁顺序表
void SLDestroy(SL* ps)
{
if (ps->arr != NULL)
{
free(ps->arr);
ps->arr = NULL;
ps->num = 0;
ps->capacity = 0;
}
}
3.顺序表容量检查
void SLCheckCapacity(SL* ps)
{
if (ps->num == ps->capacity)
{
int newcapacity =ps->capacity== 0 ? 4 : ps->capacity * 2;
//避免扩容失败导致原来的空间也被覆盖,数据损失
SLDataType* ptr = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * newcapacity);
if (ptr == NULL)
{
perror("realloc fail");
return;
}
ps->arr = ptr;
ps->capacity = newcapacity;
}
}
4.顺序表尾插法插入数据
void SLPushBack(SL* ps, SLDataType x)
{
SLCheckCapacity(ps);
ps->arr[ps->num] = x;//num为顺序表中元素个数,比元素的下标位置大1
//因此正好指向最后一个元素的下一个位置
ps->num++;
}
5. 顺序表头插法插入数据
void SLPushFront(SL* ps, SLDataType x)
{
SLCheckCapacity(ps);
SLDataType end = ps->num - 1;//指向最后一个元素
//挪动数据
while (end >= 0)
{
ps->arr[end + 1] = ps->arr[end];
--end;
}
//插入数据
ps->arr[0] = x;
ps->num++;
}
6.顺序表尾删法删除数据
void SLPopBack(SL* ps, SLDataType x)
{
ps->num--;//相当于改变了有效数据的个数
}
- 存在问题
假设我们已经插入了6个数据,但是我们调用了7次尾删法函数:
此时num(顺序表中元素个数)等于-1,但由于我们此时并没有访问-1的位置,所以编译器并没有报错。
但是我们此时如果想在插入新的数据比如重新插入数据1,2,3:
可以发现最开始插入的数据1确缺失了。因此我们要规避顺序表空了还继续删除的问题:
因此要对顺序表中元素个数进行检查:使用断言assert()暴力检查。
7.顺序表头删法删除数据
- 定义变量begin=1
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->num > 0);
int begin = 1;
while (begin < ps->num)
{
ps->arr[begin - 1] = ps->arr[begin];
++begin;
}
ps->num--;
}
- 定义变量begin=0
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->num >= 0);
int begin = 0;
while (begin <ps->num - 1)
{
ps->arr[begin]=ps->arr[begin + 1];
++begin;
}
ps->num--;
}
8.任意位置下标的元素的插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->num);//等于num相当于尾插
SLCheckCapacity(ps);
int end = ps->num - 1;
//挪动从pos开始及它以后的数据
while (end >= pos)
{
ps->arr[end + 1]=ps->arr[end];
--end;
}
//在pos处插入数据
ps->arr[pos] = x;
ps->num++;
}
9.任意位置下标的元素的删除
void SLErase(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->num);
SLCheckCapacity(ps);
int begin = pos + 1;//参考头删法
while (begin < ps->num)
{
ps->arr[begin - 1] = ps->arr[begin];
++begin;
}
ps->num--;
}