顺序表的实现

顺序表的内部结构的实现

主要目标:

实现顺序表的基本骨架,为以后实现其它功能做铺垫

思路整理:

  • 创建顺序表,先定义声明一个顺序表。

  • 初始化顺序表,创建销毁顺序表

  • 大致框架形成,便向框架里塞各种各样的功能,如增删查改

条件实现

SqList.h: 实现类型定义和函数声明等的功能

SqList.c: 作为函数的实现

test.c: 对函数进行测试等功能

代码实现操作

SqList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>			//为后面扩容所用的realloc函数引入的头文件
#include<string.h>
#include<assert.h>

typedef int SLDateType;		//用typedef重新定义int,给int重新取名字,是为了在后续更换到其它数据类型的时候,不用将代码里的int改掉,直接改此处即可,从而实现改一,而改全部。

typedef struct SqList {
	SLDateType* a ;		
	int size;		//初步定义的时候不用赋值,定义size是为了记录,数组a的元素个数,当然只是记录,并不会改变a里面的值
	int capacity;	//初步定义的时候不用赋值,定义size是为了检测内存空间,从而判断是否需要扩容等问题
}SL;				//一样用typedef重新声明一个结构体名,方便代码编写,不用处处写SqList.

void SLInit(SL*ps);								//初始化顺序表接口声明
void SLDestory(SL* ps);							//销毁顺序表接口声明
void SLPushBack(SL* ps, SLDateType x);			//后插顺序表接声明
void SLPushFront(SL* ps, SLDateType x);			//前插顺序表声明
void SLPopBack(SL* ps);							//后删顺序表声明
void SLPopFront(SL* ps);						//前删顺序表声明
void SLInsert(SL* ps, int pos, SLDateType x);	//任意位置插入顺序表声明
void SLDelete(SL* ps, int pos);					//任意位置删除顺序表声明
void SLPrint(SL* ps);							//展示顺序表声明
void SLCheck(SL* ps);							//检查顺序表声明

SqList.c:

对其初始化函数:

void SLInit(SL* ps) {
	assert(ps);
	ps->a = NULL;			//犯过错误
	ps->size = 0;
	ps->capacity = 0;
}

对函数进行初始化时,一定要是ps(ps是一个在测试界面是一个struct SL的一个变量 sl传进来的一个指针)指向的a size capacity

不能够自己又在重新定义,就像int* a int size int capacity

eg:

void SLInit(SL* ps) {
	assert(ps);
	int*a = NULL;			//犯过错误
	ps->size = 0;
	ps->capacity = 0;
}

如图所示

因为前面已经定义了struct SqList类型的指针a,size , capacity类型数值了,如果在初始化时不是写成ps->a, ps->size ps->capacity 等数据,而是毫无理由的写成 int* a int size int capacity的话,系统会认为你对其进行了重定义,此刻,特别是int* a就是一个野指针,没有指向,所以会出现上图,当我想进行扩容的时候发生错误。

所以初始化的数据一定要跟定义的数据类型匹配。

对其销毁接口

void SLDestory(SL* ps) {
	assert(ps);
	if (ps->a = NULL) {
		return;
	}
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->size = 0;
}

当然也可以

void SLDestroy(SL* ps)	//销毁主要是判断ps->a是否还存在,存在则释放内存,并且,并且,指针置空,其余赋予初始值
{
	assert(psl);

	if (psl->a != NULL)
	{
		free(psl->a);	//释放空间
		psl->a = NULL;	//置空指针
		psl->size = 0;
		psl->capacity = 0;
	}
}

打印顺序表函数:

void SLPrint(SL* ps)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

后插元素

void SLPushBack(SL* ps, SLDateType x) {
	assert(ps);
	if (ps->size == ps->capacity) {
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
//数据结构没有规定固定要扩容的量,此处的乘以2是因为,每次增长两倍是比较合适的一个量
		SLDateType* tmp = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType));
		if (tmp == NULL) {
			perror("realloc failed");
			return;
		}
//用tmp承担ps->a的扩容任务是因为,扩容可能会失败,一旦扩容失败,将会导致ps->a中的原有数据的丢失,所以先用一个替身来保护真身
		ps->a = tmp;	//归还空间
		ps->capacity = newcapacity;	//这是新的空间含量
	}
	ps->a[ps->size] = x;
	ps->size++;			//新增了数据要++
	
}
正如前边所说,ps->size是用来监测顺序表ps->a的数据,当其ps->size = ps->capacity时,就是说,空间已满,需要扩容。
扩容所需的函数主要是realloc, 特别强调的是,realloc的第二参数是扩容后的大小

进行realloc时,如果一开始空间为0的话,那么此刻,realloc的作用便跟malloc的功能一样了

效果展示:

void test1() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPrint(&sl);
}

在这里插入图片描述

前插元素

void SLPushFront(SL* ps, SLDateType x) {
	assert(ps);
		if (ps->size == ps->capacity) {
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
//数据结构没有规定固定要扩容的量,此处的乘以2是因为,每次增长两倍是比较合适的一个量
		SLDateType* tmp = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType));
		if (tmp == NULL) {
			perror("realloc failed");
			return;
		}
//用tmp承担ps->a的扩容任务是因为,扩容可能会失败,一旦扩容失败,将会导致ps->a中的原有数据的丢失,所以先用一个替身来保护真身
		ps->a = tmp;	//归还空间
		ps->capacity = newcapacity;	//这是新的空间含量
	}
	int end = ps->size - 1;		//由下图可知,size的位置是在最后一个具体元素的下一个位置,所以要找到最后一个元素,需要ps->size-1,所以给end赋值ps->size-1
    
	while (end >= 0) {			//犯过错,将end>0,其要>=0
//如果end最后不加等于0的话,ps->a[end]就无法识别到0号位置,
//导致0号位置的元素无法传递给一号位置,最后导致前插的准备工作失败
        
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}

在这里插入图片描述

根据前插和后插的两个函数的编写,便会发现,其都会在进行插入元素的时候检查空间是否足够,是否要考虑扩容,因此,我们便会将是否需要扩容重新拎出来,单独作为一个函数,以此来增强代码的易读性
void SLCheck(SL* ps) {
	if (ps->size == ps->capacity) {
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLDateType* tmp = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType));
		if (tmp == NULL) {
			perror("realloc failed");
			return;
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
}

void SLPushBack(SL* ps, SLDateType x) {
	assert(ps);
	SLCheck(ps);
	ps->a[ps->size] = x;
	ps->size++;
	
}
void SLPushFront(SL* ps, SLDateType x) {
	assert(ps);
	SLCheck(ps);
	int end = ps->size - 1;
	while (end >= 0) {			//犯过错,将end>0,其要>=0
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}

效果展示

void test1() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPrint(&sl);
	SLPushFront(&sl, 4);
	SLPushFront(&sl, 5);
	SLPushFront(&sl, 6);
	SLPrint(&sl);
}

在这里插入图片描述

任意位置插入函数:

对于任意位置插入函数,可以简单理解为其前插函数的0号位置改为任意位置即可,但需额外增加函数参数

void SLInsert(SL* ps, int pos, SLDateType x) {
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);	//遗漏,就是超过两端后就不能再让他继续增加了,不然数组访问不到,因为size始终只是监测功能

	SLCheck(ps);
	int i = ps->size - 1;
	for (; i >= pos; i--) {
		ps->a[i + 1] = ps->a[i];
	}
	ps->a[pos] = x;
	ps->size++;
}

效果展示

void test1() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPrint(&sl);
	SLPushFront(&sl, 4);
	SLPushFront(&sl, 5);
	SLPushFront(&sl, 6);
	SLPrint(&sl);
	SLInsert(&sl, 2, 5);
	SLInsert(&sl, 4, 6);
	SLInsert(&sl, 5, 7);
	SLPrint(&sl);
}

在这里插入图片描述

后删元素(删除的位置都是数组的位置,而不是元素的位置)## 标题

void SLPopBack(SL* ps) {
	assert(ps);
	assert(ps->size > 0);
	ps->size--;
}

直接把监控size–, 就能将原有的数据隐藏,注意只是隐藏起来,就看不见了

效果展示:

void test1() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPrint(&sl);
	SLPushFront(&sl, 4);
	SLPushFront(&sl, 5);
	SLPushFront(&sl, 6);
	SLPrint(&sl);
	SLInsert(&sl, 2, 5);
	SLInsert(&sl, 4, 6);
	SLInsert(&sl, 5, 7);
	SLPrint(&sl);

	SLPopBack(&sl);
	SLPopBack(&sl);
	SLPrint(&sl);
}

在这里插入图片描述

前删元素

void SLPopFront(SL* ps) {
	assert(ps);
	int end = 1;
	while (end<ps->size) {
		ps->a[end-1] = ps->a[end]; //从前往后,数据一个个往前移动
		end++;
	}
	ps->size--;
}

效果展示:

void test1() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPrint(&sl);
	SLPushFront(&sl, 4);
	SLPushFront(&sl, 5);
	SLPushFront(&sl, 6);
	SLPrint(&sl);
	SLInsert(&sl, 2, 5);
	SLInsert(&sl, 4, 6);
	SLInsert(&sl, 5, 7);
	SLPrint(&sl);

	SLPopBack(&sl);
	SLPopBack(&sl);
	SLPrint(&sl);
	SLPopFront(&sl);
	SLPopFront(&sl);
	SLPrint(&sl);

	SLDestory(&sl);
}

在这里插入图片描述

任意位置删除元素

void SLDelete(SL* ps, int pos) {
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);	//遗漏

	int del = pos;
	for (int i = del; i < ps->size; i++) {
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}

其原理跟前删元素也差不多,在此便不做展示

不要频繁的去调用头插,因为此调用时间复杂太长哩
还有就是,popback(后删)要及时判断,空了就不要删了

以上便是我写的第一篇博客,还望各位大佬提出建议,如果有错误,请大佬们即使斧正,谢谢支持!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值