动态顺序表基本功能的实现(超详细c语言版)

本文详细介绍了如何使用C语言实现动态顺序表,包括结构体定义、初始化、尾插、尾删、头插、头删、中间插入、中间删除及查找等功能。代码示例展示了如何通过动态内存分配和数组操作来管理数据元素,并提供了简单的测试用例。
摘要由CSDN通过智能技术生成

一、概念

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存
储。在数组上完成数据的增删查改。
顺序表一般可以分为:
1. 静态顺序表:使用定长数组存储元素。
2. 动态顺序表:使用动态开辟的数组存储。

二、功能

静态顺序表实用性不强,本篇文章不再讨论,以下是动态顺序表的各个功能

1.尾插数据(在数组的尾部插入数据)

2.尾删数据(在数组的尾部删除一个数据)

3.头插数据(在数组的首部插入数据)

4.头删数据(在数组的首部删除一个数据)

5.中间插入(在数组的指定位置插入数据)

6.中间删除(删除数组指定位置的数据)

7.查找数据(搜索指定数据并返回数据的位置)

三、实现

3.1、定义结构体

包含数组a、size记录数据个数、capacity记录开辟空间的大小。

(这里我使用了typedef关键字定义结构为SL,方便后续写代码。

另外使用typedef定义了数据类型为SLDataType,如果后续需要更改储存数据类型,方便代码维护。)

代码:

typedef int SLDataType;//定义数据类型名称,方便代码维护
typedef struct Seqlist //typedef: 定义结构, 方便后续代码简单易懂
{
	SLDataType* a; //指向动态开辟的数组
	int size;      //记录存储多少个有效数据
	int capacity;  //空间容量大小
}SL;
 3.2、结构体初始化

一个形参:结构体地址的形参ps

首先将数组a的地址指向空指针(NULL)、size和capacity的值设置为0。

代码:

void SLInit(SL* ps)
{
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}
3.3、尾插的实现

一个形参:结构体地址的形参ps尾插的实现需要两个步骤,首先应该判断当前开辟的栈的大小是否足够存放数据,在确保能够存放后再插入数据。

3.3.1、空间检查

一个形参:结构体地址的形参ps

空间检查很简单,只需要判断size的大小和capacity的大小是否相等、如果相等则证明当前开辟的栈已经满了,需要扩大。

int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;

这里使用了条件运算符,先判断空间大小是否为0,如果为0则赋值4,如果不为0则capacity乘2。

然后使用realloc函数开辟或扩大栈,开辟栈的大小为sizeof(SLDataType) * NewCapacity,返回的地址为tmp注意要判断tmp是否指向空指针,栈开辟成功后再把tmp的地址赋予a。

代码:

void SLCapacityCheck(SL*ps)//检查栈大小是否够用
{
	if (ps->size == ps->capacity)
	{
		int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//判断空间大小是否为0,如果为0则赋值4,如果不为0则乘2
		SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * NewCapacity);
		if (tmp == NULL)
		{
			printf("perro");
			exit(-1);
		}
		else
		{
			ps->capacity = NewCapacity;
			ps->a = tmp;
		}
	}
}
3.3.2、插入数据

两个形参:结构体地址的形参ps、数据x

在空间检查后,只需要把原来数组大小基础上的加一位赋值为x即可。

size的值相应的加1。

代码:

void SLPushBack(SL* ps, SLDataType x)//尾插
{
	assert(ps);//检查
	SLCapacityCheck(ps);//检查空间
	ps->a[ps->size] = x; //
	ps->size++;
}
3.4、尾删的实现

一个形参:结构体地址的形参ps

首先可能想到的是让数组最后一位的值为0,但是当最后一位的值本来就为0时,好像无效,所以只需要把size的值减一,数组的最后一位省略即可。

最重要的是要判断size的值是否大于0,因为当size的值等于或小于0时,可能不会报错,但是不符合顺序表的结构。

void SLPopBack(SL* ps)//尾删
{
    //ps->a[ps->size - 1]=0;//无效重置
    assert(ps->size>0);//检查
    ps->size--;
}
3.5、头插的实现

两个形参:结构体地址的形参ps、数据x

依旧先检查ps,然后利用以上尾插中实现的空间检查SLCapacityCheck函数检查栈的大小,然后把数组a内的位置往后挪一位,最后把首位赋值x即可。

注意不要忘了size值加一。

代码:

void SLPushFront(SL* ps,SLDataType x)
{
	assert(ps);
	SLCapacityCheck(ps);
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
        --end;
	}
	ps->a[0] = x;
	ps->size++;
}
3.6、头删的实现

一个形参:结构体地址的形参ps

assert检查ps,数组a内每一个数据的位置往前移一位,size的值-1。

void SLPopFront(SL* ps)//首删
{
	assert(ps);//检查
	SLCapacityCheck(ps);
		int begin = 0;
		while (begin <= ps->size - 1)
		{
			ps->a[begin] = ps->a[begin + 1];
			begin++;
		}
		ps->size--;
}
3.7、中间插入的实现

三个形参:结构体地址的形参ps、位置pos、数据x

首先检查ps、pos>=1(数据要插入的位置),pos<=size,确保插入位置在数组a内,并考虑插入位置在首位或者尾部的情况。

然后把将要插入的位置后的数据的位置往后移动一位,最后在该位置赋值x,size值+1即可。

void SLInsert(SL* ps, int pos, SLDataType x)//在pos位置插入数据
{
	assert(ps);
	assert(ps);
	assert(pos >= 1);
	assert(pos <= ps->size);
	SLCapacityCheck;
	int end = ps->size;
	while (end>=pos)
	{
		ps->a[end] = ps->a[end-1];
		end--;
	}
	ps->a[pos - 1] = x;//pos指的是第几个数据,所以用pos-1标记位置
	ps->size++;
}
3.8.指定位置数据删除的实现

三个形参:结构体地址的形参ps、位置pos、数据x

首先检查ps、pos>=1(数据要插入的位置),pos<=size

然后从把删除的位置开始的数据从前往后替换为后一位的数据(往前移一位),size-1即可。

void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 1);
	assert(pos <= ps->size);
	int begin = pos - 1;
	while (begin < ps->size - 1)
	{
		ps->a[begin] = ps->a[begin + 1];
		begin++;
	}
	ps->size--;
}
3.9、查找函数的实现

三个形参:结构体的形参ps、需要查找的数据的形参x、考虑到有从某一位开始查找的需求,我引入了开始查找位置的形参begin。

首先检查ps,然后用for循环在begin到size范围内找第一个x值并返回它的位置。

int SLFind(SL* ps, SLDataType x,int begin)//begin开始查找位置
{
	assert(ps);
	for (int i = begin; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i+1;
		}
	}
	return -1;
}

四、全部代码

(包含以上未提到的打印函数SLPrint和销毁函数SLDestory)

4.1 seqlist.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#pragma once//防指文件重定义
//动态顺序表 -- 按需扩空间
typedef int SLDataType;//定义数据类型名称,方便代码维护
typedef struct Seqlist //typedef: 定义结构, 方便后续代码简单易懂
{
	SLDataType* a; //指向动态开辟的数组
	int size;      //记录存储多少个有效数据
	int capacity;  //空间容量大小
}SL;

void SLInit(SL* ps);
void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPopBack(SL* ps);//尾删
void SLPushFront(SL* ps, SLDataType x);//首差
void SLPopFront(SL* ps);//首删
void SLDestory(SL* ps);//销毁
void SLPrint(SL* ps);//打印
void SLInsert(SL* ps, int pos, SLDataType x);//在pos位置插入数据
void SLErase(SL* ps, int pos);//在pos位置删除数据
int SLFind(SL* ps, SLDataType x,int begin);
4.2 seqlist.c
#include"Seqlist.h"
void SLPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
}
void SLInit(SL* ps)
{
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}
void SLCapacityCheck(SL*ps)//检查栈大小是否够用
{
	if (ps->size == ps->capacity)
	{
		int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//判断空间大小是否为0,如果为0则赋值4,如果不为0则乘2
		SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * NewCapacity);
		if (tmp == NULL)
		{
			printf("perro");
			exit(-1);
		}
		else
		{
			ps->capacity = NewCapacity;
			ps->a = tmp;
		}
	}
}
void SLDestory(SL* ps)
{
	if (ps->a != NULL)
	{
		free(ps->a);
		ps->a = NULL;
		ps->size = 0;
		ps->capacity = 0;
	}
}
void SLPushBack(SL* ps, SLDataType x)//尾插
{
	assert(ps);
	SLCapacityCheck(ps);
	ps->a[ps->size] = x; 
	ps->size++;
}
//原地扩容/异地扩容
void SLPopBack(SL* ps)//尾删
{
	//ps->a[ps->size - 1]=0;//无效重置
	assert(ps->size>0);//检查
	ps->size--;
}
void SLPushFront(SL* ps,SLDataType x)
{
	assert(ps);
	SLCapacityCheck(ps);
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
        --end;
	}
	ps->a[0] = x;
	ps->size++;
}
void SLPopFront(SL* ps)//首删
{
	assert(ps);//检查
	SLCapacityCheck(ps);
		int begin = 0;
		while (begin <= ps->size - 1)
		{
			ps->a[begin] = ps->a[begin + 1];
			begin++;
		}
		ps->size--;
}

void SLInsert(SL* ps, int pos, SLDataType x)//在pos位置插入数据
{
	assert(ps);
	assert(ps);
	assert(pos >= 1);
	assert(pos <= ps->size);
	SLCapacityCheck;
	int end = ps->size;
	while (end>=pos)
	{
		ps->a[end] = ps->a[end-1];
		end--;
	}
	ps->a[pos - 1] = x;//pos指的是第几个数据,所以用pos-1标记位置
	ps->size++;
}
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 1);
	assert(pos <= ps->size);
	int begin = pos - 1;
	while (begin < ps->size - 1)
	{
		ps->a[begin] = ps->a[begin + 1];
		begin++;
	}
	ps->size--;
}
int SLFind(SL* ps, SLDataType x,int begin)//begin开始查找位置
{
	assert(ps);
	for (int i = begin; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i+1;
		}
	}
	return -1;
}
4.3  test.c
void meau()
{
	printf("**********************************************************\n");
	printf("*********1.尾插数据             2.头插数据****************\n");
	printf("*********3.尾删数据             4.头删数据****************\n");
	printf("*********5.中间插入             6.中间删除****************\n");
	printf("*********7.打印                           ****************\n");
	printf("*********-1.退出                          ****************\n");
	printf("**********************************************************\n");
	printf("**********************************************************\n");
}
int main()
{
	SL s;
	SLInit(&s);
	int option = 0;
	do {
		meau();
		printf("请输入命令:>");
		scanf("%d", &option);
		switch (option)
		{
		case 1:
			printf("请依次输入你需要尾插的数据,以-1结束:>");
		SLDataType val;
			scanf("%d",&val);
 			while (val != -1)
			{
				SLPushBack(&s, val);
				scanf("%d",&val);
			}
			break;
		case 2:
			printf("请依次输入你需要头插的数据,以-1结束:>");
			int b;
			scanf("%d", &b);
			while (b != -1)
			{
				SLPushFront(&s, b);
				scanf("%d", &b);
			}
			break;
		case 3:
			SLPopBack(&s);
			printf("尾删成功!\n");
			break;
		case 4:
			SLPopFront(&s);
			printf("头删成功!\n");
			break;
		case 5:
     		printf("请依次输入你需要插入的位置和数据:>");
	    	int pos = 0;
		    int x = 0;
		    scanf("%d", &pos);
		    scanf("%d", &x);
		    SLInsert(&s, pos, x);
			printf("插入成功!\n");
			break;
		case 6:
			printf("请输入你需要删除数据的位置:>");
			int pos2= 0;
			scanf("%d", &pos2);
			SLErase(&s, pos2);
			printf("删除成功!\n");
			break;
		case 7:
			SLPrint(&s);
			printf("\n");
			break;
		default:
			break;
		};
	} while (option != -1);
	printf("已退出!");
	SLDestory(&s);
	return 0;
}

注: test.c只做简单演示,SLFind函数已测试过,但未在test.c中引用,如果有需求自行引用实现功能。

五、代码演示

 


创作不易 本文章如果对你有帮助请点赞支持!

谢谢ღ

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jenbin

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

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

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

打赏作者

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

抵扣说明:

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

余额充值