[简单易懂]数据结构顺序表——C语言

本文详细介绍了顺序表的概念及其作为线性表的特性,重点讲解了如何使用C语言实现顺序表的动态内存管理、初始化、打印、扩容、尾插尾删、头插头删、插入删除、查找和修改等功能。通过具体代码示例展示了每个操作的过程,包括在结构体定义、内存分配、扩容策略等方面的关键细节。
摘要由CSDN通过智能技术生成

前言💖:

​ 顺序表是线性表的一种,而线性表是n个具有相同特性的数据元素(换种说法,顺序表其实就是数组)的有限序列。线性表是在实际中广泛使用的数据结构,常见的线性表:顺序表,链表,栈,队列,字符串…
​ 线性表在逻辑上是线性结构,也就是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上储存时,通常以数组和链式结构的形式存储。image-20220317164929757

为什么要学习顺序表?

1.连续的物理空间,方便下标随机访问(查找访问)
2.支持排序,二分查找

车站🚗:

代码注释

结构体定义

初始化和打印函数

扩容函数

尾插尾删

头插头删

随机插,删

查和改

清空

最终代码

注释👀:

制作顺序表需要的功能有:增删查改

我们创建两个.c文件,一个.h文件分别实现菜单(test.c),具体代码(SeqList.c),函数声明和头文件(SeqList.h)

实现功能的代码注释:

void SeqListPrint(SeqList* psl); //打印
void SeqListInit(SeqList* psl); //初始化
void SeqListDestroy(SeqList* psl); //清空
void SeqListCheckCapacity(SeqList* psl); //扩容
void SeqListPushBack(SeqList* psl, SLDataType x); //尾加
void SeqListPopBack(SeqList* psl); //尾删
void SeqListPushFront(SeqList* psl, SLDataType x); //头插
void SeqListPopFront(SeqList* psl); //头删
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x); //在pos位置插入
void SeqListErase(SeqList* psl, size_t pos); //删除pos位置的数据
int SeqListFind(SeqList* psl, SLDataType x); //查找
void SeqListModify(SeqList* psl, size_t pos, SLDataType x); //修改

过程🙌:

1.顺序表结构体的定义:

//重定义int和链表名字,增加可读性和维护性
typedef int SLDataType;
typedef struct SeqList {
	SLDataType* a;
	int size; //存储数据的个数
	int capacity; //存数据的大小(扩容大小)
}SeqList;

2.定义完成后,在菜单文件进行结构体命名,再实现初始化和打印函数

//test.c
SeqList s;
SeqListInit(&s); //传入结构体s的地址

//SeqList.h
void SeqListInit(SeqList* psl); //用*psl接收s
void SeqListPrint(SeqList* psl);

//SeqList.c
void SeqListInit(SeqList* psl)
{
	assert(psl); //断言psl不为空
	psl->a = NULL;
	psl->size = 0;
	psl->capacity = 0;
}
void SeqListPrint(SeqList* psl)
{
	assert(psl);
	for (int i = 0; i < psl->size; ++i)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\n");
}

3.在初始化完成后,我们需要思考一个问题,就是在进行增删查改时,a数组的空间该如何分配,如果用宏定义a的空间,那并不好控制空间的动态大小,所以我们实现一个动态内存分配的函数,当我们需要扩容时只需要调用函数即可

//SeqList.h
void SeqListCheckCapacity(SeqList* psl); //扩容

//SeqList.c
void SeqListCheckCapacity(SeqList* psl)
{
	assert(psl);
	if (psl->size == psl->capacity) //当已有大小等于扩容大小时
	{
		size_t newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2; //从4开始扩容,之后每扩一次都是两倍扩
		SLDataType* tmp = realloc(psl->a, sizeof(SLDataType) * newCapacity); //用*tmp接收扩容后的值
		if (tmp == NULL) //当已经无法扩容时退出程序
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else
		{
			psl->a = tmp;
			psl->capacity = newCapacity;
		}
	}
}

4.完成了扩容函数,现在就可以开始实现插入删除了,我们先完成尾插尾删的函数

//test.c
SeqListPopBack(&s);//尾删
SeqListPushBack(&s, x); //尾插x

//SeqList.h
void SeqListPopBack(SeqList* psl);
void SeqListPushBack(SeqList* psl, SLDataType x);

//SeqList.c
void SeqListPopBack(SeqList* psl)
{
    assert(psl);
    if(psl->size > 0)
    {
        psl->size--;
    }
}
void SeqListPushBack(SeqList* psl, SLDataType x)
{
    assert(psl->a);
    SeqListCheckCapacity(psl); //防止内存不够先进行扩容
    psl->a[psl->size] = x; //原本最后一位下标为size-1,插入一个元素为size
    psl->size++; //size个数+1
}

5.之后是头插头删函数

//test.c
SeqListPopFront(&s, x); //在头部插入x
SeqListPushFront(&s);

//SeqList.h
void SeqListPushFront(SeqList*psl,SLDataType x);
void SeqListPopFront(SeqList*psl);

//SeqList.c
void SeqListPushFront(SeqList* psl, SLDataType x)
{
    assert(psl);
    SeqListCheckCapacity(psl); //老样子,先扩容
    int end = psl->size - 1;
    while(end >= 0)
    {
        psl->a[end + 1] = psl->a[end]; //让元素依次往后移
        --end;
    }
    psl->a[0] = x; //在覆盖完成后,x的值就可以赋给第一个元素了
    psl->size++;
}
void SeqListPopFront(SeqList*psl)
{
    assert(psl);
    if(psl->size > 0) //防止size为负
    {
        int begin = 1; //定义下标begin,从第二个元素开始
        while(begin < psl->size) //当begin没有超过size时便会一直覆盖
        {
            psl->a[begin - 1] = psl->a[begin]; //让元素依次往前移
            begin++;
        }
        --psl->size; //size个数-1
    }
}

6.查找位置删除和查找位置之后插入

//test.c
SeqListInsert(&s, pos, x);//在pos位置之前插入x
SeqListInsert(&s, pos); //在pos位置删除

//SeqList.h
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
void SeqListErase(SeqList* psl, size_t pos);

//SeqList.c
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos < psl->size); //防止查找的pos越界
	SeqListCheckCapacity(psl); //扩容
	size_t end = psl->size; //end为最后一个元素的下标
	while (end > pos)
	{
		psl->a[end] = psl->a[end - 1]; //从后往前移动元素,直到遇到pos位置时停下,相当于将pos位置之前的位置进行前移
		--end;
	}
	psl->a[pos] = x;
	psl->size++;
}
void SeqListErase(SeqList* psl, size_t pos)
{
    assert(psl);
    assert(pos < psl->size);
    size_t begin = pos + 1;
    while(begin < psl->size)
    {
        psl->a[begin - 1] = psl->a[begin]; //从后往前移动
        begin++;
    }   
    psl->size--;
}

7.目前为止,我们的增和删就已经完成了,接下来的查和改就很简单了

//test.c
SeqListFind(&s, x);
SeqListModify(&s, pos, x); //将pos位置改为x

//SeqList.h
int SeqListFind(SeqList* psl, SLDataType x);
void SeqListModify(SeqList* psl, size_t pos, SLDataType x);

//SeqList.c
int SeqListFind(SeqList* psl, SLDataType x)
{
    assert(psl);
	for (int i = 0; i < psl->size; ++i)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}
void SeqListModify(SeqList* psl, size_t pos, SLDataType x)
{
    assert(psl);
    assert(pos < psl->size);
    psl->a[pos] = x;
}

8.最后,实现一个清空顺序表的函数

//test.c
SeqListDestroy(&s);

//SeqList.h
void SeqListDestroy(SeqList* psl);

//SeqList.c
void SeqListDestroy(SeqList* psl)
{
	assert(psl);
	free(psl->a); //注意:释放后的指针为野指针,我们还需要将其置空
	psl->a = NULL;
	psl->capacity = psl->size = 0;
}

最终代码

test.c部分


#include "SeqList.h"
int main()
{
	SeqList s;
	SeqListInit(&s); //传入结构体s的地址
	SeqListPushFront(&s, 13);//头插
	SeqListPushFront(&s, 12);
	SeqListPushFront(&s, 11);
	SeqListPushFront(&s, 10);

	SeqListPushBack(&s, 19);//尾插
	SeqListPushBack(&s, 20);
	SeqListPushBack(&s, 21);
	SeqListPushBack(&s, 22);

	SeqListInsert(&s, 3, 100);//在第三个元素后插入100
	SeqListPrint(&s);
	SeqListErase(&s, 3);//删除第三个元素
	SeqListPopFront(&s); //头删
	SeqListPopBack(&s); //尾删
	SeqListPrint(&s);
	int num = SeqListFind(&s, 11); //查找值为11的下标
	printf("下标为:%d\n", num); //打印值为11的下标
	SeqListModify(&s, 3, 200); //修改pos之后的值
	SeqListPrint(&s);
	SeqListDestroy(&s);
}

SeqList.h部分

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;
typedef struct SeqList {
	SLDataType* a;
	int size; //存储数据的个数
	int capacity; //存数据的大小(扩容大小)
}SeqList;
void SeqListPrint(SeqList* psl); //打印
void SeqListInit(SeqList* psl); //初始化
void SeqListDestroy(SeqList* psl); //清空
void SeqListCheckCapacity(SeqList* psl); //扩容
void SeqListPushBack(SeqList* psl, SLDataType x); //尾加
void SeqListPopBack(SeqList* psl); //尾删
void SeqListPushFront(SeqList* psl, SLDataType x); //头插
void SeqListPopFront(SeqList* psl); //头删
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x); //在pos位置插入
void SeqListErase(SeqList* psl, size_t pos); //删除pos位置的数据
int SeqListFind(SeqList* psl, SLDataType x); //查找
void SeqListModify(SeqList* psl, size_t pos, SLDataType x); //修改

SeqList.c部分

#include "SeqList.h"
void SeqListInit(SeqList* psl)
{
	assert(psl); //断言psl不为空
	psl->a = NULL;
	psl->size = 0;
	psl->capacity = 0;
}
void SeqListPrint(SeqList* psl)
{
	assert(psl);
	for (int i = 0; i < psl->size; ++i)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\n");
}
void SeqListCheckCapacity(SeqList* psl)
{
	assert(psl);
	if (psl->size == psl->capacity) //当已有大小等于扩容大小时
	{
		size_t newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2; //从4开始扩容,之后每扩一次都是两倍扩
		SLDataType* tmp = realloc(psl->a, sizeof(SLDataType) * newCapacity); //用*tmp接收扩容后的值
		if (tmp == NULL) //当已经无法扩容时退出程序
		{
			printf("realloc fail\n");
			exit(-1);
		}
		else
		{
			psl->a = tmp;
			psl->capacity = newCapacity;
		}
	}
}
void SeqListPopBack(SeqList* psl)
{
	assert(psl);
	if (psl->size > 0)
	{
		psl->size--;
	}
}
void SeqListPushBack(SeqList* psl, SLDataType x)
{
	assert(psl->a);
	SeqListCheckCapacity(psl); //防止内存不够先进行扩容
	psl->a[psl->size] = x; //原本最后一位下标为size-1,插入一个元素为size
	psl->size++; //size个数+1
}
void SeqListPushFront(SeqList* psl, SLDataType x)
{
	assert(psl);
	SeqListCheckCapacity(psl); //老样子,先扩容
	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end]; //让元素依次往后移
		--end;
	}
	psl->a[0] = x; //在覆盖完成后,x的值就可以赋给第一个元素了
	psl->size++;
}
void SeqListPopFront(SeqList* psl)
{
	assert(psl);
	if (psl->size > 0) //防止size为负
	{
		int begin = 1; //定义下标begin,从第二个元素开始
		while (begin < psl->size) //当begin没有超过size时便会一直覆盖
		{
			psl->a[begin - 1] = psl->a[begin]; //让元素依次往前移
			begin++;
		}
		--psl->size; //size个数-1
	}
}
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos < psl->size); //防止查找的pos越界
	SeqListCheckCapacity(psl); //扩容
	size_t end = psl->size; //end为最后一个元素的下标
	while (end > pos)
	{
		psl->a[end] = psl->a[end - 1]; //从后往前移动元素,直到遇到pos位置时停下,相当于将pos位置之前的位置进行前移
		--end;
	}
	psl->a[pos] = x;
	psl->size++;
}
void SeqListErase(SeqList* psl, size_t pos)
{
	assert(psl);
	assert(pos < psl->size);
	size_t begin = pos + 1;
	while (begin < psl->size)
	{
		psl->a[begin - 1] = psl->a[begin]; //从后往前移动
		begin++;
	}
	psl->size--;
}
int SeqListFind(SeqList* psl, SLDataType x)
{
	assert(psl);
	for (int i = 0; i < psl->size; ++i)
	{
		if (psl->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}
void SeqListModify(SeqList* psl, size_t pos, SLDataType x)
{
	assert(psl);
	assert(pos < psl->size);
	psl->a[pos] = x;
}
void SeqListDestroy(SeqList* psl)
{
	assert(psl);
	free(psl->a); //注意:释放后的指针为野指针,我们还需要将其置空
	psl->a = NULL;
	psl->capacity = psl->size = 0;
	printf("释放完成!\n");
}
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A.A呐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值