数据结构顺序表(C语言实现)

news2024/11/25 1:54:15

绪论

        从本章开始就是开始数据结构的开端,本章将会写出数据结构中的顺序表的代码实现,多会以注释的方法来描述一些细节(注释是我们程序员必须常用的工具)。

     

话不多说安全带系好,发车啦(建议电脑观看)。


附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或者其余颜色为次重点;黑色为描述需要


目录

1.线性表

2.顺序表

2.1顺序表的结构:

2.1.1 顺序表的初始化:

2.1.2 顺序表的摧毁

2.1.3 顺序表的放入数据

2.1.4 顺序表的删除数据

2.1.5 打印顺序表的数据       

2.2顺序表的源代码:


1.线性表

知识点:

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串..

细节(注意点):

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组(地址连续)和链式结构(地址不连续)的形式存储。


2.顺序表

知识点:

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储(本质就是一个数组只不过用结构体包装了一下)。

顺序表分为静态顺序表(实现开辟好数组的空间大小)以及动态顺序表(用动态内存管理的方式来进行内存管理)

细节:

我们要实现一个顺序表的话首先我们要知道顺序表的框架

  1. 顺序表的结构体
    1. 数组(a)
    2. 顺序表中的元素个数(size)
    3. 容量(用于动态顺序表,是动态申请的大小,用于和元素个数比较判断申请空间是否足够)
  2. 有了这个结构后,我们就需要实现一个顺序表的基本功能
    1. 将数据放进顺序表中
    2. 将顺序表中的数据删除
    3. 初始化顺序表(主要针对于动态开辟空间提前申请空间)
    4. 归还(摧毁)所借的空间
    5. 将顺序表中的数据打印出来

下面这要讲的是动态的顺序表、如果需要静态的将结构体中的容量去掉,再把数组改一下即可(若有问题的话可以评论我都会看),我们就把顺序表想成一个数组就能很好的理解了


2.1顺序表的结构:

顺序表所要的结构是由数组、元素个数、容量组成的

代码实现如下:

typedef struct SeqList//将结构体名称重命名为
{
	SLDataType* a;//开辟SLDataType(用typedef重定义类型名,这样方便与改变结构体内的数据类型)类型的空间
//数组的本质是指针所以为了更方便理解就直接写成指针的形式SLDataType* a 
	int size;//元素个数
	int capacity;//容量
}SeqList;//重命名为SeqList 这样方便后面使用
//宏定义如下:
//#define SLDataType int 
//使用结构体类型
//SeqList s;即可
//不用写成 struct SeqList s;

2.1.1 顺序表的初始化:

将容量capacity和个数size进行简单的初始化,主要是申请一片空间来给a来存数据

代码如下:

void InitSeqList(SeqList* obj)//将结构体用指针接收
{
	assert(obj);
	obj->capacity = INIT_CAPACITY;//通过指针来访问结构体中的成员,将capacity先初始化为INIT_CAPACITY(用宏来确定capacity的起始大小,这样方便后改变)
	obj->size = 0;//0个成员
	obj->a = (SLDataType*)malloc(sizeof(SLDataType) * obj->capacity);//malloc动态申请结构体大小的capacity个空间
	if (obj->a == NULL)//判断一下是否申请成功
	{
		perror("malloc");//如果失败就报错
		return;
	}
}

//宏
//#define INIT_CAPACITY 4 (这是应该定义在头文件中的)

//调用的方法
//SeqList s;(应该定义在测试test.c文件中)
//InitSeqList(&s);

//而一般的结构的实现又是放在SeqList.c的文件中,这样来进行分源管理

2.1.2 顺序表的摧毁

顺序表的摧毁主要是为了将向操作系统借的空间归还,以及再将容量和元素个数归为0

void DestorySeqList(SeqList* obj)//指针接收结构
{
	assert(obj);//判断结构是否为空,防止访问到NULL指针(这是一个好习惯)
	free(obj->a);//直接释放所借用的空间
	obj->a = NULL;//再将其置为NULL
	obj->capacity = obj->size = 0;//将容量和个都置为0,摧毁了自然就没了
}

//调用方法
//SeqList s;
//DestorySeqList(&s);

2.1.3 顺序表的放入数据

1.从尾部插入数据

尾部插入就比较的简单了,因为顺序表其实是一个数组所以直接在最后位置插入数据就行(此时最后位置的下标就是元素个数

void SeqListBackPush(SeqList* obj, SLDataType x)//将结构体用指针接收通过指针来找到成员,x是所要尾插的数据
{
	assert(obj);//判断结构体是否为NULL

	If_Add_Capacity(obj);//判断数据是否已经把所借的容量填满了

	obj->a[(obj->size)++] = x;//在a的最后位置插入数据,可以发现其实size个数就是最后位置的下标
}

但要注意判断容量是否满足,如果容量已经是满的了(size == capacity)就需要扩容,If_Add_Capacity (判断是否要增容)

void If_Add_Capacity(SeqList* obj)
{
	if (obj->size == obj->capacity)//判断已有成员个数是否等于容量,若等则进去
	{
		SLDataType* ptr = (SLDataType*)realloc(obj->a, sizeof(SLDataType) * obj->capacity * 2);//进来后就说明空间不够了,需要开空间
		//一般多直接开辟比容量大两倍的空间 即 对a开辟结构体大小为原capacity两倍的空间
		if (ptr == NULL)//判断是否申请成功
		{
			perror("realloc");//不成功则报错

			return;
		}
		obj->a = ptr;//因为可能是异地扩容所以还要将ptr赋值给数组a
		obj->capacity *= 2;//容量 乘于 2
		ptr = NULL;//无用的指针置为NULL(好习惯)
	}
}

2.从头部插入

在一个数组中若想从头部插入 你就需要把数据先全部往后挪动一位,再将这个数据存放到第一个位置处。

void SeqListFrontPush(SeqList* obj, SLDataType x)
{
	assert(obj);//判空

	If_Add_Capacity(obj);//判是否满了
	for (int i = obj->size; i > 0; i--)//将所有数据往后移一位
	{
		obj->a[i] = obj->a[i - 1];//此处只要是未满的就能直接就行移位并不会有事
	}
	obj->a[0] = x;//在a[0]位置处添加数据
	obj->size++;//元素个数++,这可别忘了!
}

3.指定位置插入

在满足pos位置是一个正常的位置的前提下,并且同样需要判断是否要扩容, 要在某个位置处插入,其本质其实和头插有些类似,需要把插的位置后的数据全部往后挪一位后,最后再在那个位置插入数据即可。


void SeqListInsert(SeqList* obj, int pos, SLDataType x)//在pos位置处添加数据
{
	assert(obj);//判空
	pos -= 1;//换成下标
	assert(pos >= 0 && pos <= obj->size);//判断这个位置是否有问题
	If_Add_Capacity(obj);//判断是否满了
	int i = obj->size;//和头插的方法几乎一样
	for (i; i > pos; i--)//将从位置处开始的数据全部往后挪一位
	{
		obj->a[i] = obj->a[i - 1];//从尾部开始防止覆盖
	}
	obj->a[i] = x;//在位置处插入数据
	obj->size++;//size++ 别忘了!
}

2.1.4 顺序表的删除数据

1. 从尾部删除

在删除之前我们需要判断一下是否还有数据在顺序表中(assert(obj->size > 0)),对于一个数组来说我们删除时直接对元素个数进行 - - 即可并不会去真正的删除,当下一次插入数据时就直接覆盖了,也不会有什么影响。

void SeqListBackPop(SeqList* obj)
{
	assert(obj);//判空
	assert(obj->size > 0);//为真就过、为假就会报错,若没有数据那就是有问题的

	obj->size--;//此处的尾删并不直接将空间归还,而仅仅只是把元素个数-1这样

	//就不会访问到,即使后面需要再次添加数据也就直接覆盖了,因为要归还空间的成本太高了
}

2.从头部删除

同样我们需要先判断一下顺序表中是否还有数据、对于头部删除来说直接将第一个数据覆盖了就好

void SeqListFrontPop(SeqList* obj)
{
	assert(obj);//判空
	assert(obj->size > 0);//判断是否有数据
	for (int i = 0; i < obj->size - 1; i++)//直接从第2个位置开始往前覆盖掉即可
	{
		obj->a[i] = obj->a[i + 1];
	}
	obj->size--;//注意要 - - 
}

3.指定位置删除

判断pos是否在顺序表中、最后将从pos+1位置开始数据覆盖即可。

void SeqListErase(SeqList* obj, int pos)
{
	assert(obj);//判空
	assert(obj->size > 0);//是否有数据

	pos -= 1;//换成下标
	assert(pos >= 0 && pos <= obj->size);//是否符合要求

	for (int i = pos; i < obj->size - 1; i++)//和头删对应此处就应该是从pos+1位置处开始往前覆盖
	{
		obj->a[i] = obj->a[i + 1];//将pos位置处先覆盖 , 然后以此往后
	}
	obj->size--;//注意 - -
}

2.1.5 打印顺序表的数据       

就和数组的打印一样,直接遍历打印即可

void SeqListPirnt(SeqList* obj)//指针接收结构体
{
	assert(obj);//判空
	for (int i = 0; i < obj->size; i++)//从下标为0的位置处开始往后遍历
	{
		printf("%d ", obj->a[i]);//结构体访问成员:*obj表示结构体 在 .访问 就还能写成 (*obj).a[i] 这两个是等价的一般喜欢用前面方法
	}
	printf("\n");//换行
}

如果有任何问题,欢迎讨论!


2.2顺序表的源代码:

我将全部放在一个里面对于分源内容请自行分开,或者直接合并也行(合并方法将声明以及包含自身的头文件去掉即可直接使用

//SeqList.h
#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>


#define INIT_CAPACITY 4
//sequence

typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;
	int size;
	int capacity;
}SeqList;

void InitSeqList(SeqList * obj);
void DestorySeqList(SeqList* obj);

void SeqListBackPush(SeqList* obj,SLDataType x );

void SeqListBackPop(SeqList* obj);

void SeqListPirnt(SeqList* obj);

void SeqListFrontPush(SeqList* obj, SLDataType x);

void SeqListFrontPop(SeqList* obj);

void SeqListInsert(SeqList* obj, int pos, SLDataType x);

void SeqListErase(SeqList* obj, int pos);

//SeqList.c

 #define _CRT_SECURE_NO_WARNINGS 1

#include"SeqLIst.h"
//sequence 顺序

void If_Add_Capacity(SeqList* obj)
{
	if (obj->size == obj->capacity)
	{
		SLDataType* ptr = (SLDataType*)realloc(obj->a, sizeof(SLDataType) * obj->capacity * 2);
		if (ptr == NULL)
		{
			perror("realloc");

			return;
		}
		obj->a = ptr;
		obj->capacity *= 2;
		ptr = NULL;
	}
	return;
}

void InitSeqList(SeqList* obj)
{
	assert(obj);

	obj->capacity = INIT_CAPACITY;
	obj->size = 0;
	obj->a = (SLDataType*)malloc(sizeof(SLDataType) * obj->capacity);
	if (obj->a == NULL)
	{
		perror("malloc");
		return;
	}
}

void DestorySeqList(SeqList* obj)
{
	assert(obj);

	free(obj->a);
	obj->a = NULL;
	obj->capacity = obj->size = 0;
}

void SeqListBackPush(SeqList* obj, SLDataType x)
{
	assert(obj);
	
	If_Add_Capacity(obj);

	obj->a[(obj->size)++] = x;

}

void SeqListBackPop(SeqList* obj)
{
	assert(obj);
	assert(obj->size > 0);//为真就过、为假就会报错
	
	obj->size--;
}

void SeqListPirnt(SeqList* obj) 
{
	assert(obj);
	for (int i = 0; i < obj->size; i++)
	{
		printf("%d ", obj->a[i]);
	}
	printf("\n");
}


void SeqListFrontPush(SeqList* obj, SLDataType x)
{
	assert(obj);

	If_Add_Capacity(obj);
	for (int i = obj->size; i > 0; i--)
	{
		obj->a[i] = obj->a[i - 1];
	}
	obj->a[0] = x;
	obj->size++;
}

void SeqListFrontPop(SeqList* obj)
{
	assert(obj);
	assert(obj->size > 0);
	for (int i = 0; i < obj->size - 1; i++)
	{
		obj->a[i] = obj->a[i + 1];
	}
	obj->size--;
}

void SeqListInsert(SeqList* obj, int pos, SLDataType x)
{
	assert(obj);
	pos -= 1;//换成下标
	assert(pos >= 0 && pos <= obj->size);
	If_Add_Capacity(obj);
	int i = obj->size;
	for (i; i > pos; i--)//从最后开始将填充数据
	{
		obj->a[i] = obj->a[i - 1];
	}
	obj->a[i] = x;
	obj->size++;
}

void SeqListErase(SeqList* obj, int pos)
{
	assert(obj);
	assert(obj->size > 0);

	pos -= 1;//换成下标
	assert(pos >= 0 && pos <= obj->size);

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

}

//test.c
//测试是否能用

 #define _CRT_SECURE_NO_WARNINGS 1

#include"SeqLIst.h"

int main()
{
	SeqList s;
	InitSeqList(&s);
	SeqListPush(&s, 1);
	SeqListPush(&s, 2);
	SeqListPush(&s, 3);
	SeqListPush(&s, 4);
	SeqListPush(&s, 5);

	SeqListPop(&s);
	SeqListPop(&s);
	SeqListPop(&s);

	SeqListFrontPush(&s, 0);

	SeqListFrontPush(&s, -1);

	SeqListInsert(&s, 1, 3);
	SeqListErase(&s, 2);
	SeqListPirnt(&s);

	DestorySeqList(&s);

	return 0;
}

分源管理时的头文件 :

持续更新大量数据结构细致内容,三连关注哈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/666385.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux多线程之生产者消费者模型1

目录 &#x1f34a;一、什么是生产者消费者模型 &#x1f34a;二、基于BlockingQueue的生产者消费者模型 &#x1f34a;三、生产消费模型的upgrade版本 &#x1f34a; 四、三线程实现生产消费和存储 &#x1f34a;一、什么是生产者消费者模型 生产者消费者模式就是通过一…

SciencePub学术 | 智能计算类重点SCIEEI征稿中

SciencePub学术 刊源推荐: 智能计算类重点SCI&EI征稿中&#xff01;2区闭源正刊&#xff0c;对国人友好&#xff01;信息如下&#xff0c;录满为止&#xff1a; 一、期刊概况&#xff1a; 智能计算类重点SCIE&EI &#x1f4cc;【期刊简介】IF&#xff1a;8.0-8.5&…

FormData 介绍和使用

FormData 是 JavaScript 中用于处理表单数据的接口。它提供了一种简单的方式来构建和发送表单数据&#xff0c;表单数据以键值对的形式向服务器发送&#xff0c;这个过程是浏览器自动完成的。但是有时候&#xff0c;我们希望通过脚本完成这个过程&#xff0c;构造或编辑表单的键…

生态工具箱 | 虚拟机测试工具WasmFuzzer,智能合约安全防火墙

长安链生态工具箱 丰富实用的区块链生态工具不仅可以让开发者部署、开发过程更加得心应手&#xff0c;还可以从能力上扩展区块链应用边界。长安链正在构建强大的生态工具箱以增强在其在各类场景下的应用能力&#xff0c;如智能合约漏洞检测、抗量子多方安全计算、链迁移、密…

CaffeineCache+Redis 接入系统做二层缓存思路实现(借鉴 mybatis 二级缓存、自动装配源码)

本文目录 前言本文术语本文项目地址设计思路开发思路DoubleCacheAble 双缓存注解&#xff08;如何设计&#xff1f;&#xff09;动态条件表达式&#xff1f;例如&#xff1a;#a.id?&#xff08;如何解析&#xff1f;&#xff09;缓存切面&#xff08;如何设计&#xff1f;&…

Linux centos7下漏洞扫描工具 Nessus8.15.9的下载、安装

一、下载Nessus 传送带地址&#xff1a;Download Nessus | Tenable 因为Darren洋的Linux操作系统是Linux Centos7 64 位&#xff0c;大家可以根据自己的选择合适的系统版本&#xff0c;在linux系统中用以下命令即可完成查询系统版本。 cat /etc/redhat-release 二、安装Ness…

Axure8 基本操作记录

参考&#xff1a;黑马产品经理课程 视频资源&#xff1a;day1&day2&#xff0c;Axure部分 文章小结图片 Axure8常用功能 选择/缩放 选择 包含选中&#xff1a;全部选中才有效&#xff08;避免误操作&#xff0c;建议使用这个&#xff09;相交选中&#xff1a;相交即全选中…

同时安装vue-cli2和vue-cli3

同时安装vue-cli2和vue-cli3 发布时间环境安装后的效果安装vue-cli2安装vue-cli3vue-cli3和vue-cli2的区别vue-cli2目录结构vue-cli3目录结构 发布时间 vue版本发布时间Seed.js2013年vue最早版本最初命名为Seedvue-js 0.62013年12月更名为vuevue-js 0.82014年1月对外发布vue-j…

vue2 用watch监听props 失效,解决办法

这个是父组件传递下来的props 这样子好像TCshow的值并没有赋上 必须修改成下面这种&#xff1a;

[golang 微服务] 7. go-micro框架介绍,go-micro脚手架,go-micro结合consul搭建微服务案例

一.go-micro框架 前言 上一节讲解了 GRPC微服务集群 Consul集群 grpc-consul-resolver相关的案例,知道了微服务之间通信采用的 通信协议&#xff0c;如何实现 服务的注册和发现&#xff0c;搭建 服务管理集群&#xff0c;以及服务与服务之间的 RPC通信方式,具体的内容包括: pro…

SpringBoot 如何使用 IOC 容器

SpringBoot 如何使用 IOC 容器 Spring 是一个非常流行的 Java 开发框架&#xff0c;它提供了一个强大的 IoC&#xff08;Inversion of Control&#xff09;容器来管理 Java 对象之间的依赖关系。在 SpringBoot 中&#xff0c;我们可以非常方便地使用这个 IoC 容器来管理我们的…

骨传导耳机音质怎么样,几款解析力度不错的骨传导耳机分享

​骨传导耳机在之前的时候一直是“冷门”的&#xff0c;但是随着技术的进步&#xff0c;现在骨传导耳机也逐渐被大家所熟知。对于喜欢运动和健身的人来说&#xff0c;骨传导耳机可以避免佩戴普通耳机导致耳朵疼痛的情况。因此&#xff0c;目前在市面上很多骨传导耳机都很受欢迎…

Git教程(快速上手,超详细)

文章目录 版本控制Git环境配置Git基本理论Git项目搭建Git文件操作使用码云IDEA集成GitGit分支 版本控制 版本迭代:每次更新就会有新的版本&#xff0c;旧的版本需要保留。所以我们需要一个版本控制工具帮助我们处理这个问题 版本控制&#xff08;Revision control&#xff09;是…

入门学习编码器与自编码器1----包括详细的理论讲解与详细的python程序代码,小白直接看懂!!!纯干货

文章目录 前言--为什么要学习编码器和自编码器&#xff1f;一、编码器与自编码器究竟是什么&#xff1f;二、下面是一个简单的Python实现自编码器的示例三、程序运行结果四、查看模型结构总结 前言–为什么要学习编码器和自编码器&#xff1f; 学习编码器和自编码器可以帮助我…

【数据分享】1929-2022年全球站点的逐月平均风速数据(Shp\Excel\12000个站点)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 对于具体到监测站点的气象数据&#xff0c;之前我们分享过1929-2022年全球气象…

「你将购买的是虚拟内容服务,购买后不支持退订」,真的合理么?

编辑导语&#xff1a;你是否也有见过相似提示&#xff0c;即虚拟内容服务购买之后不予退款&#xff1f;那么你有想过&#xff0c;在这一规定背后&#xff0c;其制约因素都有什么吗&#xff1f;这一规定是合理的吗&#xff1f;用户若真的有退款需求&#xff0c;产品上是否能实现…

卷积计算加速方法--分块卷积1

文章目录 1、大尺寸卷积存在的问题2、分块卷积overlap产生的来源3、分块卷积overlap的计算4、结论及加速效果 1、大尺寸卷积存在的问题 当卷积的输入太大导致内存不够用时&#xff0c;考虑将一大块卷积分成多个小块分别进行卷积&#xff0c;相当于将原始输入分成几个小的输入经…

【C++】C++11:线程库和包装器

C11最后一篇文章 文章目录 前言一、线程库二、包装器和绑定总结 前言 上一篇文章中我们详细讲解了lambda表达式的使用&#xff0c;我们今天所用的线程相关的知识会大量的用到lambda表达式&#xff0c;所以对lambda表达式还模糊不清的可以先将上一篇文章看明白。 一、线程库 在…

域名解析详解

域名解析 记录类型&#xff1a; 提示&#xff1a; 将域名指向云服务器&#xff0c;选择 A&#xff1b; 将域名指向另一个域名&#xff0c;选择 CNAME&#xff1b; 建立邮箱选择 MX&#xff0c;根据邮箱服务商提供的 MX 记录填写。 记录类型解释A用来指定域名的 IPv4 地址&…

燃气管网监测设备:燃气管网压力在线监测

燃气作为一种重要的能源&#xff0c;广泛用于家庭、工业和商业领域。然而&#xff0c;燃气管网系统在运输和分配过程中可能面临压力波动、管道老化、外部破坏等问题&#xff0c;可能导致燃气泄漏和事故发生。燃气管网压力在线监测是保障燃气管网安全运营的重要手段之一。通过燃…