数据结构之初识顺序表——数值的增删查改

今天刚在哔站看了比特杭哥的数据结构的网课,也是我第一次开始接触数据结构的知识,给自己做个记录,个人的见解,方便以后忘记后回来看看

什么是线性表?

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

什么是顺序表?

顺序表也是属于线性表的一种,是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。说白了顺序表呢就是数组,但是再数组的基础上,它还要求数据是连续存储的,不能跳跃间隔。下面我们将使用简单的代码加深对顺序表的认识。

代码实现

以下是一个简单的数组增删查改功能的代码,调用了数据结构中顺序表的逻辑思路,让我们先来看看吧!

在VS2019先创建三个文件,分别是SeqList.c、SeqList.h、test.c,分别是函数接口文件,声明文件,测试文件。

方法一:可以采用静态顺序表的方式,不过不推荐使用静态的,静态只做一个对比而已,不是主要代码

SeqList.h

SeqList.c

#include "SeqList.h"


void SeqListInit(SL *ps)
{
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}
void SeqlistPushBack(SL* ps, SLDataType x)
{
	SeqListCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}
void SeqlistPopBack(SL* ps)
{
	if (ps->size > 0)   //防止越界
	{
		ps->size--;
	}
		
}

void SeqlistPushFront(SL* ps, SLDataType x)
{
	SeqListCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}

但是在我写到一半的时候,我意识到了静态存在的误区,就是当我们插入数据满了之后就不让插入了,否则会导致越界访问什么的,而且你得事先考虑要设置好多少容量,那么给多少又不知道,多了浪费,少了不够,所以比较麻烦。那么这个时候动态的顺序顺序表就可以完美解决这个问题了。

动态顺序表

SeqList.h

#pragma once
#include<stdio.h>
#define N 100
typedef int SLDataType;



//静态顺序表
//typedef struct SeqList
//{
//	SLDataType a[N];
//	int size;
//
//}SL;
//
//void SeqListInit(SL* ps, SLDataType x);  //顺序练链表初始化
//void SeqlistPushBack(SL* ps, SLDataType x);
//void SeqlistPopBack(SL* ps, SLDataType x);
//void SeqlistPushFront(SL* ps, SLDataType x);
//void SeqlistPopFront(SL* ps, SLDataType x);

//动态顺序表

typedef struct SeqList
{
	SLDataType* a;
	int size;    //表示数组存储了多少个数据
	int capacity;//数组实际的存储容量

}SL;
//函数接口
void SeqListInit(SL* ps);  //顺序链表初始化
void SeqListCheckCapacity(SL* ps);  //检查增容
void SeqlistPushBack(SL* ps, SLDataType x); //尾部插入
void SeqlistPopBack(SL* ps);   //尾部删除
void SeqlistPushFront(SL* ps, SLDataType x);//头部删除
void SeqlistPopFront(SL* ps);//尾部删除
void SeqListprintf(SL* ps);
void SeqListDestroy(SL* ps);//空间销毁

//查找,在找到后返回x位置的下标,没有找到返回-1
int SeqListFind(SL* ps, SLDataType x);
void SeqListInset(SL* ps, int pos, SLDataType x);//pos指定位置插入
void SeqListErase(SL* ps, int pos);//指定pos位置删除数据

一些说明:

现在.h文件构思好要写的功能函数接口,typedef int SLDataType; 变量重定义名字是为了插入数据能随时更改,在代码多的情况下可以减少工作量,比如我要一个char类型的直接改typedef后面的类型就好,传参传SLDataType就可以了。

创建typedef struct SeqList的顺序表,里面存放的就是SLDataType* a的指针,他会按地址,连续存储我们要的数据,size表示数组存储了多少个数据,capacity表示数组实际的存储容量,其他代码都有注释,大家感兴趣可以仔细看看!

初始化函数void SeqListInit(SL* ps)

 对顺序表进行初始化,相当于一开始没有数据,存储空间为0,函数通过传参,参入的SL*类型的指针,通过地址找到顺序表,将表中的a初始化为空,size和capacity也都初始化为0

检查增容函数void SeqListCheckCapacity(SL* ps)

我们在插入内容的时候,必须要考虑到空间是否足够的问题,不够的情况下我们得进行扩容,防止越界访问,这就是动态顺序表的一个特点;此函数依旧是通过传参,由地址找到内容,执行相应的操作

可想而知,当size存入数据个数和容量capacity相等时,证明空间已满,这个时候需要进行扩容,通过评估,当空间不够的时候每次扩大两倍是最合适的,不会太多也不会太少。扩容需要用到realloc函数,这个函数的使用大家感兴趣的可以上网搜搜,要注意使用了realloc一定要free,不然会导致内存泄漏,后面会有代码

函数void SeqListDestroy(SL* ps)空间销毁

此函数用于free释放掉扩容的空间,防止内存泄漏

void SeqlistPushBack(SL* ps, SLDataType x)尾部插入函数

SL* ps传入地址指向的顺序表,SLDataType x传入要插入的数据,先检查空间是否足够,不够进行扩容,由SeqListCheckCapacity()函数完成这一步,然后在尾部size赋值插入数据x,然后ps->size++表示数据加1,以便下次插入,以下都是这种思路

void SeqlistPopBack(SL* ps)尾部删除

这里的删除比较简单粗暴了,直接减去一个空间,这样相当于没有了一个数据的存在,自然访问不到,不过要注意,得先判断size是否已经减完了,如果size为0,证明已经没有数据了,但是对计算机来说,它可以到达-1的位置,这样子就会导致越界访问的问题了,所以需要加一个判断语句

void SeqlistPushFront(SL* ps, SLDataType x)头部插入

对于头部插入,我们可以理解为,先将原本的最后一个数据依次往后挪动,这样子前面就多出了空位,也就可以插入数据了,这样就可以防止原先的数据被覆盖,挪动完后,在下标为0的位置插入数据,便可达到头部插入的目的,尾部

void SeqlistPopFront(SL* ps)头部删除

头部删除数据的思路,其实和头部插入的数据差不多,就是一个方向相反的问题,相当于把数据依次往前挪动,依次挪动完后,将总的size减一,便可达到目的,逻辑思路都是以此类推,其实只要SeqListInset函数任意插入写好后,直接复用函数更方便,就不需要什么头插尾插了,不过重在一个记录学习过程哈哈哈,接下来就直接上完整代码了,代码有对应的注释,大家可以仔细看看

完整代码

 SeqList.h

#pragma once
#include<stdio.h>
#define N 100
typedef int SLDataType;



//静态顺序表
//typedef struct SeqList
//{
//	SLDataType a[N];
//	int size;
//
//}SL;
//
//void SeqListInit(SL* ps, SLDataType x);  //顺序练链表初始化
//void SeqlistPushBack(SL* ps, SLDataType x);
//void SeqlistPopBack(SL* ps, SLDataType x);
//void SeqlistPushFront(SL* ps, SLDataType x);
//void SeqlistPopFront(SL* ps, SLDataType x);

//动态顺序表

typedef struct SeqList
{
	SLDataType* a;
	int size;    //表示数组存储了多少个数据
	int capacity;//数组实际的存储容量

}SL;
//函数接口
void SeqListInit(SL* ps);  //顺序链表初始化
void SeqListCheckCapacity(SL* ps);  //检查增容
void SeqlistPushBack(SL* ps, SLDataType x); //尾部插入
void SeqlistPopBack(SL* ps);   //尾部删除
void SeqlistPushFront(SL* ps, SLDataType x);//头部删除
void SeqlistPopFront(SL* ps);//尾部删除
void SeqListprintf(SL* ps);
void SeqListDestroy(SL* ps);//空间销毁

//查找,在找到后返回x位置的下标,没有找到返回-1
int SeqListFind(SL* ps, SLDataType x);
void SeqListInset(SL* ps, int pos, SLDataType x);//pos指定位置插入
void SeqListErase(SL* ps, int pos);//指定pos位置删除数据

SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1


#include "SeqList.h"
#include<assert.h>

void SeqListInit(SL *ps)
{
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}
void SeqListCheckCapacity(SL* ps)
{
	//如果空间不够得进行扩容
	if (ps->size == ps->capacity)
	{
		int  newcapacity = ps->capacity == 0 ? 4: ps->capacity*2;//进行空间扩容,先判断是不是最开始的0空间再进行扩容
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		} 
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
}
void SeqListprintf(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
}

void SeqListDestroy(SL* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->size = ps->capacity = 0;
}
void SeqlistPushBack(SL* ps, SLDataType x)
{
	SeqListCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}
void SeqlistPopBack(SL* ps)
{
	if (ps->size > 0)   //防止越界
	{
		ps->size--;
	}
		
}
void SeqlistPushFront(SL* ps, SLDataType x)
{
	SeqListCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}
void SeqlistPopFront(SL* ps)
{
	if (ps->size > 0)   //防止越界
	{
		int begin = 1;
		while (begin < ps->size)
		{
			ps->a[begin - 1] = ps->a[begin];
			begin++;
		}
		ps->size--;
	}
}

//查找,在找到后返回x位置的下标,没有找到返回-1
int SeqListFind(SL* ps, SLDataType x)
{
	int i;
	for (i=0; i < ps->size - 1; i++)
	{
		if (x == ps->a[i])
		{
			return i;
		}
	}

	return -1;
	
}

//pos指定位置插入
void SeqListInset(SL* ps, int pos, SLDataType x)
{
	/*if (pos > ps->size || pos < 0)
		printf("pos invalid\n");
		return;*/

	//判断是否越界,直接断言
	assert(pos >= 0 && pos < ps->size);
	SeqListCheckCapacity(ps);
	//从后往前将数据挪动到后面
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;

}

//指定pos位置删除数据
void SeqListErase(SL* ps, int pos)
{
	assert(pos >= 0 && pos < ps->size);
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"

void TestSeqList1()
{
	SL sl;
	SeqListInit(&sl);
	SeqlistPushBack(&sl, 1);
	SeqlistPushBack(&sl, 2);
	SeqlistPushBack(&sl, 3);
	SeqlistPushBack(&sl, 4);
	SeqlistPushBack(&sl, 5);
	SeqListprintf(&sl);
	printf("\n");

	SeqlistPopBack(&sl);
	SeqlistPopBack(&sl);
	SeqlistPopBack(&sl);
	SeqListprintf(&sl);
	printf("\n");

	SeqlistPushBack(&sl, 10);
	SeqlistPushBack(&sl, 20);
	SeqListprintf(&sl);

	SeqListprintf(&sl);




	SeqListDestroy(&sl);//释放内存
}

void TestSeqList2()//测试头部尾部插入
{
	SL sl;
	SeqListInit(&sl);
	SeqlistPushBack(&sl, 1);
	SeqlistPushBack(&sl, 2);
	SeqlistPushBack(&sl, 3);
	SeqlistPushBack(&sl, 4);
	SeqlistPushBack(&sl, 5);
	SeqListprintf(&sl);
	printf("\n");

	SeqlistPushFront(&sl, 8);
	SeqlistPushFront(&sl, 30);
	SeqListprintf(&sl);
	printf("\n");

	SeqlistPopFront(&sl);
	SeqListDestroy(&sl);

	

}

void TestSeqList3() //测试查找数据
{
	SL sl;
	int findnum = SeqListFind(&sl, 40);
	SeqListInit(&sl);
	SeqlistPushBack(&sl, 1);
	SeqlistPushBack(&sl, 2);
	SeqlistPushBack(&sl, 3);
	SeqlistPushBack(&sl, 4);
	SeqlistPushBack(&sl, 5);
	SeqListprintf(&sl);
	printf("\n");

	//验证查找数据的位置
	int num = SeqListFind(&sl, 40);
	if (num != -1)
	{
		printf("已经找到数据位置在下标%d\n", num);
	}
	else
		printf("找不到数据");

	SeqlistPopFront(&sl);
	SeqListDestroy(&sl);



}

void TestSeqList4()//测试指定pos位置插入
{
	SL sl;
	SeqListInit(&sl);
	SeqlistPushBack(&sl, 1);
	SeqlistPushBack(&sl, 2);
	SeqlistPushBack(&sl, 3);
	SeqlistPushBack(&sl, 4);
	SeqlistPushBack(&sl, 5);
	SeqListprintf(&sl);
	printf("\n");
	SeqListInset(&sl, 3, 30);//pos位置插入
	SeqListprintf(&sl);
	SeqListDestroy(&sl);
}

void TestSeqList5()//测试指定pos位置删除
{
	SL sl;
	SeqListInit(&sl);
	SeqlistPushBack(&sl, 1);
	SeqlistPushBack(&sl, 2);
	SeqlistPushBack(&sl, 3);
	SeqlistPushBack(&sl, 4);
	SeqlistPushBack(&sl, 5);
	SeqListprintf(&sl);
	printf("\n");
	SeqListErase(&sl,1);//pos位置插入
	SeqListprintf(&sl);
	SeqListDestroy(&sl);
}
int main()
{
	//TestSeqList1();
	//TestSeqList2();
	/*TestSeqList3();*/
	//TestSeqList4();
	TestSeqList5();
	return 0;
}

以上就是使用顺序表对数据进行增删查改的内容学习,其实本来可以写一个菜单,依次输入数字实现对应的功能,因为时间原因,且这里旨在做一个记录,就没有那么细节了,望大家见谅!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值