C语言实现顺序表

本文详细介绍了顺序表的结构,包括其线性表、栈、数组、队列等概念。通过C语言代码展示了如何初始化、销毁顺序表,以及如何进行头插、尾插、头删、尾删、中间插、中间删、查找和修改等操作。同时,讨论了顺序表的扩容机制和程序测试案例。
摘要由CSDN通过智能技术生成

前言

数据结构常见的结构有线性结构、树型结构、图形结构。

而线性结构又分为线性表、栈、数组、队列,本文讲的是顺序表,是线性表的一种。

 一、项目

1.1 SeqList.h(头文件)

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#define Max_Capacity 10


typedef int SLDateType;
//顺序表
typedef struct SeqList
{
	//顺序表地址q
	SLDateType* a;
	//顺序表的有效长度
	int size;
	//顺序表大小
	int capacity;
}SeqList;

//初始化顺序表
void SeqListInit(SeqList* ps);
//销毁顺序表
void SeqListDestroy(SeqList* ps);


//打印顺序表
void SeqListPrint(SeqList* ps);
//头插
void SeqListPushFront(SeqList* ps, SLDateType x);
//尾插
void SeqListPushBack(SeqList* ps, SLDateType x);
//头删
void SeqListPopFront(SeqList* ps);
//尾删
void SeqListPopBack(SeqList* ps);
//中间插
void SeqListInsert(SeqList* ps, int pos, int x);
//中间删
void SeqListErase(SeqList* ps, int pos);


//检查顺序表是否满了
SLDateType SeqListFull(SeqList* ps);
//顺序表扩容
void SeqListAdd(SeqList* ps);


//顺序表中查数
void SeqListSearch(SeqList* ps, SLDateType x);
//顺序表中改数
void SeqListChange(SeqList* ps, SLDateType x,SLDateType y);

1.2 SeqList.c

#include"SeqList.h"

//初始化顺序表
void SeqListInit(SeqList* ps)
{
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

//打印顺序表
void SeqListPrint(SeqList* ps)
{
	assert(ps);
	if (ps->size == 0)
	{
		printf("顺序表无有效数据\n");
		return;
	}
	for (SLDateType i = 0; i < ps->size; i++)
	{
		printf("%d ", *(ps->a + i));
	}
	printf("\n");
}

//销毁顺序表
void SeqListDestroy(SeqList* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

//判断顺序表是否满了
SLDateType SeqListFull(SeqList* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		return 1;
	}
	return 0;
}

//扩容
void SeqListAdd(SeqList* ps)
{
	assert(ps);
	ps->a = (SLDateType*)realloc(ps->a,ps->capacity*2*sizeof(SLDateType));
	if (ps->a == NULL)
	{
		perror("realloc error");
		exit(-1);
	}
	ps->capacity *= 2;
}

//头插
void SeqListPushFront(SeqList* ps, SLDateType x)
{
	assert(ps);
	if (ps->size < 0)
	{
		ps->size = 0;
	}
	if (SeqListFull(ps))
	{
		SeqListAdd(ps);
	}
	for(SLDateType i = ps->size-1; i >=0; i--)
	{
		ps->a[i + 1] = ps->a[i];
	}
	ps->a[0] = x;
	ps->size++;
}

//尾插
void SeqListPushBack(SeqList* ps, SLDateType x)
{
	assert(ps);
	if (ps->size < 0)
	{
		ps->size = 0;
	}
	if (SeqListFull(ps))
	{
		SeqListAdd(ps);
	}
	ps->a[ps->size] = x;
	ps->size++;
}

//头删
void SeqListPopFront(SeqList* ps)
{
	assert(ps);
	if (ps->size <=0)
	{
		ps->size = 0;
		return;
	}
	for (SLDateType i = 1; i < ps->size; i++)
	{
		ps->a[i - 1] = ps->a[i];
	}
	ps->size--;
}

//尾删
void SeqListPopBack(SeqList* ps)
{
	assert(ps);
	if (ps->size <= 0)
	{
		ps->size = 0;
		return;
	}
	ps->size--;
}

//中间插
void SeqListInsert(SeqList* ps,int pos,int x)
{
	assert(ps);
	if (ps->size <0)
	{
		ps->size = 0;
	}
	if (SeqListFull(ps))
	{
		SeqListAdd(ps);
	}
	if (pos - 1 < ps->size)
	{
		for(SLDateType i = ps->size - 1; i >= pos - 1; i--)
			ps->a[i + 1] = ps->a[i];
		ps->a[pos - 1] = x;
		ps->size++;
	}
	else
		printf("插入无效\n");
}

//中间删
void SeqListErase(SeqList* ps, int pos)
{
	assert(ps);
	if (ps->size <= 0)
	{
		ps->size = 0;
		return;
	}
	if (pos - 1 < ps->size)
	{
		for (SLDateType i = pos; i < ps->size; i++)
			ps->a[i - 1] = ps->a[i];
		ps->size--;
	}
	else
		printf("删除无效\n");
}

//查
void SeqListSearch(SeqList* ps, int pos)
{
	assert(ps);
	if (ps->size <= 0)
	{
		ps->size = 0;
	}
	if (pos - 1 < ps->size)
	{
		printf("%d\n",ps->a[pos - 1]);
		return;
	}
	printf("查找无效\n");
}

//改
void SeqListChange(SeqList* ps, int pos, int x)
{
	assert(ps);
	if (ps->size <= 0)
	{
		ps->size = 0;
		return;
	}
	if (pos - 1 < ps->size)
	{
		ps->a[pos - 1] = x;
	}
	else
		printf("修改无效\n");
}

1.3 test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
SLDateType main()
{
    //创造顺序表
    SeqList sl;
    //初始化
    SeqListInit(&sl);
    sl.capacity = Max_Capacity;
    sl.a = (SLDateType*)realloc(sl.a,sl.capacity * sizeof(SLDateType));
    if (sl.a == NULL)
    {
        perror("realloc error");
        exit(-1);
    }
    //尾插012345
    for (SLDateType i = 0; i < 5; i++)
        SeqListPushBack(&sl, i);
    //打印顺序表
    SeqListPrint(&sl);
    //头插012345
    for (SLDateType i = 0; i < 5; i++)
        SeqListPushFront(&sl, i);
    //再次打印
    SeqListPrint(&sl);
    //尾删3个数
    for (SLDateType i = 0; i < 3; i++)
        SeqListPopBack(&sl);
    SeqListPrint(&sl);
    //头删2个数
    for(SLDateType i=0;i<2;i++)
       SeqListPopFront(&sl);
    SeqListPrint(&sl);
    //中间第三个数插入2
    SeqListInsert(&sl, 3, 2);
    SeqListPrint(&sl);
    //中间第三个数删除
    SeqListErase(&sl, 3);
    SeqListPrint(&sl);
    //查找第3个数
    SeqListSearch(&sl, 3);
    //修改第3个数为100
    SeqListChange(&sl, 3, 100);
    SeqListPrint(&sl);
    //销毁顺序表
    SeqListDestroy(&sl);
    return 0;
}

二、顺序表的实现

2.1 顺序表

typedef int SLDateType;
//顺序表
typedef struct SeqList
{
	//顺序表地址q
	SLDateType* a;
	//顺序表的有效长度
	int size;
	//顺序表大小
	int capacity;
}SeqList;

创建一个顺序表结构体,成员包含顺序表地址、长度、大小,用于创建顺序表变量。

2.2 初始化顺序表

//初始化顺序表
void SeqListInit(SeqList* ps)
{
    ps->a = NULL;
    ps->size = 0;
    ps->capacity = 0;
}

 将顺序表变量的地址传参,通过指针接收对顺序表的顺序表数组初始化为空,长度为0,大小为0。

2.3 打印顺序表

//打印顺序表
void SeqListPrint(SeqList* ps)
{
	assert(ps);
	if (ps->size == 0)
	{
		printf("顺序表无有效数据\n");
		return;
	}
	for (SLDateType i = 0; i < ps->size; i++)
	{
		printf("%d ", *(ps->a + i));
	}
	printf("\n");
}

同样传地址,要先断言指针是否为空,不然会出异常。

然后判断顺序表大小是否为0,为0则代表顺序表中没有有效元素,打印提示,并返回函数,如果大于0,则有元素,从下标0开始,打印size个顺序表元素,并用空格相隔。

2.4 销毁顺序表

//销毁顺序表
void SeqListDestroy(SeqList* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

当我们结束程序时,因为我们是通过动态分配空间给顺序表,所以结束时我们要手动销毁,先断言,防止free空指针,成功释放空间后,将ps->a手动置空,避免产生野指针,并将大小、空间赋0。

三、对顺序表进行操作

3.1 在顺序表的头部插入数据

//头插
void SeqListPushFront(SeqList* ps, SLDateType x)
{
	assert(ps);
	if (ps->size < 0)
	{
		ps->size = 0;
	}
	if (SeqListFull(ps))
	{
		SeqListAdd(ps);
	}
	for(SLDateType i = ps->size-1; i >=0; i--)
	{
		ps->a[i + 1] = ps->a[i];
	}
	ps->a[0] = x;
	ps->size++;
}

在头部插入数据,要先判断容量是否已满,如果已满,需要扩容,还有空间,那么就从下标为ps->size-1开始,到0上的值都要将前一个元素给覆盖,而将要插入的数据覆盖给下标为0的空间,长度加1。

3.2 在顺序表末尾插入数据

//尾插
void SeqListPushBack(SeqList* ps, SLDateType x)
{
	assert(ps);
	if (ps->size < 0)
	{
		ps->size = 0;
	}
	if (SeqListFull(ps))
	{
		SeqListAdd(ps);
	}
	ps->a[ps->size] = x;
	ps->size++;
}

都是插入数据,所以前面的操作和头插一致,但是尾插要比头插看起来方便,因为只要在顺序表后一个空间内插入要插入的数据,不需要移动数据,并将长度+1。

3.3 删除在顺序表头部的数


//头删
void SeqListPopFront(SeqList* ps)
{
	assert(ps);
	if (ps->size <=0)
	{
		ps->size = 0;
        return;
	}
	for (SLDateType i = 1; i < ps->size; i++)
	{
		ps->a[i - 1] = ps->a[i];
	}
	ps->size--;
}

判断部分不需要判断容量是否够,且长度不能为负数。

和头插相反,我们要从下标为1,到ps->size-1,将前一个数覆盖,长度减1。

3.4 删除在顺序表末尾的数

//尾删
void SeqListPopBack(SeqList* ps)
{
	assert(ps);
	if (ps->size <= 0)
	{
		ps->size = 0;
		return;
	}
	ps->size--;
}

判断部分一致,尾删只需将长度减一即可。

3.5 在顺序表中间位置插入数据

//中间插
void SeqListInsert(SeqList* ps,int pos,int x)
{
    assert(ps);
    if (ps->size <0)
    {
        ps->size = 0;
    }
    if (SeqListFull(ps))
    {
        SeqListAdd(ps);
    }
    if (pos - 1 < ps->size)
    {
        for(SLDateType i = ps->size - 1; i >= pos - 1; i--)
            ps->a[i + 1] = ps->a[i];
        ps->a[pos - 1] = x;
        ps->size++;
    }
    else
        printf("插入无效\n");
}

中间插比较麻烦,不仅要断言,判断ps->size是否合理,容量是否够,还要判断所给插入数据下标位置是否在0到ps->size之间的合法区域,所以以上同时合法时, 才能实现插入数据,和头插相似,它们都是先从尾部数据开始,先向右整体覆盖过去,但是头插是到0停止,而中间插是到所给下标停止,并将该下标的数插入为想插入的数据,长度加1。

3.6 在顺序表中间位置删除数据

//中间删
void SeqListErase(SeqList* ps, int pos)
{
	assert(ps);
	if (ps->size <= 0)
	{
		ps->size = 0;
		return;
	}
	if (pos - 1 < ps->size)
	{
		for (SLDateType i = pos; i < ps->size; i++)
			ps->a[i - 1] = ps->a[i];
		ps->size--;
	}
	else
		printf("删除无效\n");
}

和头删相似,将指定下标左边的元素往左移动一个空间,长度减1。

3.7 在顺序表中查找指定下标的值

//查
void SeqListSearch(SeqList* ps, int pos)
{
	assert(ps);
	if (ps->size <= 0)
	{
		ps->size = 0;
	}
	if (pos - 1 < ps->size)
	{
		printf("%d\n",ps->a[pos - 1]);
		return;
	}
	printf("查找无效\n");
}

返回指定下标下的值,比较容易实现。

3.8 将顺序表指定下标的值修改

//改
void SeqListChange(SeqList* ps, int pos, int x)
{
	assert(ps);
	if (ps->size <= 0)
	{
		ps->size = 0;
		return;
	}
	if (pos - 1 < ps->size)
	{
		ps->a[pos - 1] = x;
	}
	else
		printf("修改无效\n");
}

找到指定下标的位置,直接赋给修改的值即可,也很容易实现。

3.9 顺序表的扩容

/判断顺序表是否满了
SLDateType SeqListFull(SeqList* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		return 1;
	}
	return 0;
}

//扩容
void SeqListAdd(SeqList* ps)
{
	assert(ps);
	ps->a = (SLDateType*)realloc(ps->a,ps->capacity*2*sizeof(SLDateType));
	if (ps->a == NULL)
	{
		perror("realloc error");
		exit(-1);
	}
	ps->capacity *= 2;
}

如果ps->size等于容量,代表容量已满,则将容量扩大为原来的两倍,但是可能会出现内存泄漏的情况,严谨一点应该在realloc完后将地址赋给一个新指针,由新指针判断不为空,再赋给顺序表指针,如果扩容失败,程序将会终止。

四、实现程序

#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
SLDateType main()
{
    //创造顺序表
    SeqList sl;
    //初始化
    SeqListInit(&sl);
    sl.capacity = Max_Capacity;
    sl.a = (SLDateType*)realloc(sl.a,sl.capacity * sizeof(SLDateType));
    if (sl.a == NULL)
    {
        perror("realloc error");
        exit(-1);
    }
    //尾插01234
    for (SLDateType i = 0; i < 5; i++)
        SeqListPushBack(&sl, i);
    //打印顺序表
    SeqListPrint(&sl);
    //头插01234
    for (SLDateType i = 0; i < 5; i++)
        SeqListPushFront(&sl, i);
    //再次打印
    SeqListPrint(&sl);
    //尾删3个数
    for (SLDateType i = 0; i < 3; i++)
        SeqListPopBack(&sl);
    SeqListPrint(&sl);
    //头删2个数
    for(SLDateType i=0;i<2;i++)
       SeqListPopFront(&sl);
    SeqListPrint(&sl);
    //中间第三个数插入2
    SeqListInsert(&sl, 3, 2);
    SeqListPrint(&sl);
    //中间第三个数删除
    SeqListErase(&sl, 3);
    SeqListPrint(&sl);
    //查找第3个数
    SeqListSearch(&sl, 3);
    //修改第3个数为100
    SeqListChange(&sl, 3, 100);
    SeqListPrint(&sl);
    //销毁顺序表
    SeqListDestroy(&sl);
    return 0;
}

用SeqList.c的函数实现我们对顺序表sl的一系列操作,结束时销毁顺序表。

打印结果如下

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值