C语言顺序表

顺序表简述

顺序表是用一段物理地址连续的储存单元依次存储数据元素的线性结构,一般情况采用数组存储。

在数组上完成增删查改。

顺序表分类与实现

静态顺序表

使用定长的数组存储元素

静态实现

1
2
3
4
5
6
7
8
#define N 100
typedef int ListData; //类型重定义
//要存什么类型的数据就定义什么类型
typedef struct SeqList
{
ListData* data[N]; //数据指针
int size; //数据数量
}SL;

点击并拖拽以移动

静态顺序表只能存储一定量的数据,结构也比较简单。

容量(N)有时给小了不够用,给大了会有浪费,不好控制。

所以就提出了动态的顺序表。

静态顺序表的基本操作和动态差不多,这里重点描述动态顺序表,静态就不再赘述了。

动态顺序表

使用动态开辟的数组存储元素

动态实现

1
2
3
4
5
6
7
8
typedef int ListData;

typedef struct SeqList
{
ListData* data; //数据内存指针
int size; //数据数量
int capacity; //数据容量
}SL;

点击并拖拽以移动

动态顺序表能根据数据量调整存储数据容量的大小

动态顺序表的基本操作

基本操作:增删查改

增,有头插,尾插

删,有头删,尾删

查,就是一般操作

改,需要自己手动配置

初始化顺序表

1
2
3
4
5
6
7
8
9
10
void InitSeqList(SL* SL)
{
assert(SL);
SL->size = 0;
SL->capacity = 4;

ListData* NewList = (ListData*)malloc(sizeof(ListData) * SL->capacity);
assert(NewList); //检查malloc是否成功执行
SL->data = NewList;
}

点击并拖拽以移动

销毁顺序表

1
2
3
4
5
6
7
8
9
10
void DestroySeqList(SL* SL)
{
assert(SL);
SL->size = 0;
SL->capacity = 0;

assert(SL->data); //检查data是否不为NULL
free(SL->data);
SL->data = NULL; //不要忘了,指针置空防止野指针
}

点击并拖拽以移动

在顺序表中增加元素

在增加元素时,我们要先考虑在增加元素时,有可能会超过当前顺序表的容量,这个时候我们就要进行扩容操作,防止数据溢出。

1
2
3
4
5
6
7
8
9
10
11
void CheckCapacity(SL* psl)
{
if (psl->size < psl->capacity) //判断是否满容量
return;

psl->capacity *= 2; //容量乘2

ListData* NewList = (ListData*)realloc(psl->data, sizeof(ListData) * psl->capacity);
assert(NewList); //检查realloc是否成功执行
psl->data = NewList;
}

点击并拖拽以移动

增加元素

我们应该在哪增加元素,在内存块的中间?前面?后面?

我们并不清楚实际中会在哪插入元素。

如果是在顺序表中的其他位置(pos),我们则需要去这个位置进行插入元素(x)

在顺序表pos位置插入元素

1
2
3
4
5
6
7
8
9
10
11
12
13
void SeqListInsert(SL* psl, int pos, ListData x)
{
assert(psl);
assert(pos < (psl->size + 1)); //检查参数是否错误
CheckCapacity(psl); //检查扩容

for (int i = psl->size; i > pos; i--) //把pos位置和之后的数据往后移动
{
psl->data[i] = psl->data[i--];
}
psl->data[pos] = x; //pos位置赋值
psl->size++;
}

点击并拖拽以移动

在顺序表中删除元素

同样的在顺序表pos位置删除元素

1
2
3
4
5
6
7
8
9
10
void SeqListErase(SL* psl, int pos)
{
assert(psl);

for (int i = pos; i < psl->size - 1; i++) //把pos位置和之后的数据往前移动
{
psl->data[i] = psl->data[i + 1];
}
psl->size--;
}

点击并拖拽以移动

在顺序表中查找元素x,找到了返回下标

1
2
3
4
5
6
7
8
9
10
11
int SeqListFind(SL* psl, ListData x)
{
for (int i = 0; i < psl->size; i++) //遍历一遍数组,查找元素x
{
if (psl->data[i] == x)
{
return i;
}
}
return -1; //没找到元素x就返回-1
}

点击并拖拽以移动

头删头插,尾删尾插

这些都可以通过调用之前的在pos位置插入函数和删除pos位置函数来进行实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void SeqListPushFront(SL* psl, ListData x)
{
SeqListInsert(psl, 0, x); //直接调用插入函数,pos位置为0
}

void SeqListPushBack(SL* psl, ListData x)
{
SeqListInsert(psl, psl->size, x); //直接调用插入函数,最后的pos位置为数组长度
} //在最后一个元素后面插入新的元素

void SeqListPopFront(SL* psl)
{
SeqListErase(psl, 0); //直接调用删除函数
}

void SeqListPopBack(SL* psl)
{
SeqListErase(psl, psl->size - 1); //直接调用删除函数,删除最后一个元素
}

点击并拖拽以移动

补充操作

打印

1
2
3
4
5
6
7
8
void SeqListPrint(SL* psl, int n)
{
for (int i = 0; i < n; i++)
{
printf("%d ", psl->data[i]);
}
printf("\n");
}

点击并拖拽以移动

求元素个数

1
2
3
4
int SeqListSize(SL* psl)
{
return psl->size;
}

点击并拖拽以移动

顺序表对改的要求不是很高,我就做个示范,大家看看就好

1
2
3
4
5
6
7
8
9
10
int n = SeqListSize(&s);
while (n--)
{
int pos = SeqListFind(&s, 2);
if (pos != -1)
{
SeqListInsert(&s, pos, 3);
s.size--; //Insert函数会让size++,但是元素没有增加,所以要手动size--
}
}

点击并拖拽以移动

验证代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int main()
{
SL s;
InitSeqList(&s);

SeqListPushFront(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 2);

printf("验证插入元素:");
SeqListPrint(&s, SeqListSize(&s));


//SeqListPopBack(&s);
SeqListPopFront(&s);

printf("验证删除元素:");
SeqListPrint(&s, SeqListSize(&s));


printf("验证查找元素下标:");
printf("%d\n", SeqListFind(&s, 1));//查找


//改
int n = SeqListSize(&s);
while (n--)
{
int pos = SeqListFind(&s, 2);
if (pos != -1)
{
SeqListInsert(&s, pos, 3);
s.size--; //Insert函数会让size++,但是元素没有增加,所以要手动size--
}
}

printf("验证修改元素:");
SeqListPrint(&s, SeqListSize(&s));


DestroySeqList(&s);
return 0;
}

点击并拖拽以移动

谢谢观看,如果感觉还行可以点个赞,谢谢啦。QWQ