初级数据结构(一)——顺序表

文中代码源文件已上传:数据结构源码

1、顺序表的特点

1.1、数组

        现实中数据记录一般都记录在表格中,如进货单、菜单等,它们的最大特点就是有序。表述中可以用第一项、第二项、第 n 项来描述表格中某个数据或者某串数据。在 C 语言中,数组的特点恰好匹配此功能。

        由于数组在内存中的储存方式就如同列表依序排布,对数组可以用 arr[n] 或者 *(arr+n) 来迅速获得第 n-1 项数据。再加上而且数组是 C 语言的原生类型,创建数组极其便利,作为有序数据的载体着实是不二之选。

1.2、结构

        顺序表为了方便使用,除了用数组作为数据载体外,一般还包含记录数组空间大小和开辟空间大小的两个变量。常以结构体将这三个变量作为成员变量进行囊括。主要有两种创建方式:

        柔性数组顺序表( Sequence table with flexible array ):

#include <stdlib.h>

//重定义数据类型
typedef int DATA;

//创建并重定义结构体类型
typedef struct SeqTab
{
    int size;           //数据长度
    int capacity;       //数据空间大小
    DATA arr[];         //数据载体柔性数组
}SeqTab;

int main()
{
    //开辟结构体空间
    SeqTab* sqList = (SeqTab*)malloc(sizeof(SeqTab) + sizeof(DATA)*4);
    //初始化数据长度及空间大小
    sqList->size = 0;
    sqList->capacity = 4;

    return 0;
}

        数组指针顺序表( Sequence table with array pointer ):

#include <stdlib.h>

//重定义数据类型
typedef int DATA;

//创建并重定义结构体类型
typedef struct SeqTab
{
    int size;           //数据长度
    int capacity;       //数据空间大小
    DATA* arr;          //数据载体数组指针
}SeqTab;

int main()
{
    //创建结构体变量
    SeqTab sqList;
    //开辟数据载体数组空间
    sqList.arr = (DATA*)malloc(sizeof(DATA)*4);
    //初始化数据长度及空间大小
    sqList.size = 0;
    sqList.capacity = 4;

    return 0;
}

2、顺序表创建

        接下来以数组指针顺序表为例进行演示。

2.1、文件结构

        seqTab.h :用于创建结构体类型及声明函数;

        sqFunction.c :用于创建顺序表初始化及增删改查的函数;

        main.c :仅创建 main 函数,用作测试。

2.2、前期工作

        在 seqTab.h 中先写入以下内容:

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

//用于初始化顺序表时开辟空间
#define INIT_SIZE 4

//顺序表数据类型,方便顺序表储存数据类型修改
typedef int DATATYPE;

//创建顺序表结构体类型
typedef struct SeqTab
{
	DATATYPE* data;
	int size;
	int capacity;
}SeqTab;

//---------------------函数声明---------------------
//初始化顺序表
extern void DataInit(SeqTab*);

//销毁顺序表
extern void DataDestory(SeqTab*);

//打印顺序表
extern void DataPrint(const SeqTab*);

         在 sqFunction.c 中包含 seqTab.h 并分别创建一个初始化顺序表和销毁顺序表的函数:

#include "seqTab.h"

//初始化顺序表
void DataInit(SeqTab* sq)
{
    //断言确保结构体指针不为空
	assert(sq);
    //为顺序表开辟空间
	sq->data = (DATATYPE*)malloc(INIT_SIZE * sizeof(DATATYPE));
    //加入判断防止空间开辟失败
	if (sq->data == NULL)
	{
		perror("Malloc Fail");
		return;
	}
    //初始化记录数据长度及开辟空间长度的变量
	sq->size = 0;
	sq->capacity = INIT_SIZE;
}

//销毁顺序表
void DataDestory(SeqTab* sq)
{
    //断言确保结构体指针不为空
	assert(sq);
    //释放顺序表数据空间
	free(sq->data);
    //数组指针置空
	sq->data = NULL;
    //数组长度及空间尺寸全部置0
	sq->size = 0;
	sq->capacity = 0;
}

//打印顺序表
void DataPrint(const SeqTab* sq)
{
    //断言确保结构体指针不为空
	assert(sq);
    //遍历顺序表并逐个数据打印
	for (int i = 0; i < sq->size; i++)
	{
		printf("%d ", sq->data[i]);
	}
	printf("\n");
}

        最后在 main.c 中包含 seqTab.h,并创建一个顺序表及初始化:

#include "seqTab.h"

int main()
{
	//创建顺序表
	SeqTab sqList;

    //初始化顺序表
	DataInit(&sqList);

    return 0;
}

        至此,前期工作准备完毕,之后便是对顺序表的数据进行增删改查。

3、顺序表操作

3.1、增

        插入数据一般为头部插入数据、尾部插入数据及指定位置插入数据。插入数据时除了写入数据到数组中,还需时刻判断开辟的空间尺寸是否足以容纳已有数据。

        先将以下三个函数声明添加到 seqTab.h 中:

//指定位置插入数据
extern void DataInsert(SeqTab*, int, DATATYPE);
//头部插入数据
extern void DataPushHead(SeqTab*, DATATYPE);
//尾部插入数据
extern void DataPushTail(SeqTab*, DATATYPE);

        然后便是 DataInsert 函数。如图:

        据此可以轻松写出其代码。以下写入 sqFunction.c 中: 

void DataInsert(SeqTab* sq, int pos, DATATYPE data)
{
    //数据有效性判断
	assert(sq);
	if (pos < 0 || pos > sq->size)
	{
		printf("Illegal Position : %d\n", pos);
		return;
	}
    //空间不足则创建空间
	if (sq->size + 1 >= sq->capacity)
	{
        //申请新空间
		DATATYPE* ptr_newSqData = (DATATYPE*)realloc(sq->data, sizeof(DATATYPE) * (sq->capacity * 2));
        //空间申请结果判断
		if (ptr_newSqData == NULL)
		{
			perror("Realloc Fail");
			return;
		}
        //赋予新空间地址
		sq->data = ptr_newSqData;
        //空间大小记录翻倍
		sq->capacity *= 2;
	}
    //数据后移直至腾出 pos 位置的空间
	for (int i = sq->size; i > pos; i--)
	{
		sq->data[i] = sq->data[i - 1];
	}
    //写入数据
	*(sq->data + pos) = data;
    //数据长度+1
	sq->size++;
}

        至于头插尾插数据,只不过是上述函数 pos 位置的区别。因此:

//pos = 0 便是头插
void DataPushHead(SeqTab* sq, DATATYPE data)
{
	DataInsert(sq, 0, data);
}
//pos = 数据尺寸便是尾插
void DataPushTail(SeqTab* sq, DATATYPE data)
{
	DataInsert(sq, sq->size, data);
}

        在 main 函数里写入下列代码验证一下:

	DataInsert(&sqList, 10, 32);   //报错
	DataPushTail(&sqList, 10);
	DataPrint(&sqList);            //打印 10
	DataPushHead(&sqList, 20);
	DataPrint(&sqList);            //打印 20 10
	DataPushHead(&sqList, 3);
	DataPushTail(&sqList, 6);
	DataPushHead(&sqList, 8);
	DataPushHead(&sqList, 7);
	DataPushHead(&sqList, 2);
	DataPushTail(&sqList, 100);
	DataPushTail(&sqList, 432);
	DataPrint(&sqList);            //打印 2 7 8 3 20 10 6 10 432

        结果与预期无误。至此插入功能便已完成。

3.2、删

        正如插入数据分为头插、尾插及指定插,删除也分头删及任意位删。与插入相反,删除数据需要在数据删除结束后关注数据长度与开辟的数据空间,当空间分配过大时,对多余空间进行回收。

        同样先将以下三个函数声明添加到 seqTab.h 中:

//指定位置删除数据
extern void DataRemove(SeqTab*, int);
//删除头部数据
extern void DataPopHead(SeqTab*);
//删除尾部数据
extern void DataPopTail(SeqTab*);

         DataRemove 函数流程图如下:

        根据图中逻辑在 sqFunction.c 中写入以下:

void DataRemove(SeqTab* sq, int pos)
{
    //数据有效性判断
	assert(sq);
	if (pos < 0 || pos > sq->size - 1)
	{
		printf("Illegal Position : %d\n", pos);
		return;
	}
    //列表不为空则执行
	if (sq->size - 1 >= 0)
	{
        //由 pos 位开始,之后所有数据前移 1 位
		for (int i = pos; i < sq->size - 1; i++)
		{
			sq->data[i] = sq->data[i + 1];
		}
        //数据长度-1
		sq->size--;
	}
    //开辟空间过大则执行
	if (sq->size < sq->capacity / 2)
	{
        //申请新空间
		DATATYPE* ptr_newSqData = (DATATYPE*)realloc(sq->data, sizeof(DATATYPE) * (sq->capacity / 2));
        //空间申请结果判断
		if (ptr_newSqData == NULL)
		{
			perror("Realloc Fail");
			return;
		}
        //赋予新空间地址
		sq->data = ptr_newSqData;
        //空间大小记录减半
		sq->capacity /= 2;
	}
}

         至于头删尾删数据,也同样是上述函数 pos 位置的区别:

//头删 pos = 0
void DataPopHead(SeqTab* sq)
{
	DataRemove(sq, 0);
}
//尾删 pos = 数据长度-1
void DataPopTail(SeqTab* sq)
{
	DataRemove(sq, sq->size - 1);
}

        之后同样是验证,将以下代码写到之前测试代码之后:

	DataRemove(&sqList, 100);    //报错
	DataRemove(&sqList, 3);
	DataPrint(&sqList);          //打印 2 7 8 20 10 6 100 432
	DataPopHead(&sqList);
	DataPrint(&sqList);          //打印 7 8 20 10 6 100 432
	DataPopTail(&sqList);
	DataPrint(&sqList);          //打印 7 8 20 10 6 100
	DataPopTail(&sqList);
	DataPopTail(&sqList);
	DataPopTail(&sqList);
	DataPopTail(&sqList);
	DataPopTail(&sqList);
	DataPrint(&sqList);          //打印 7
	DataPopTail(&sqList);
	DataPopTail(&sqList);
	DataPrint(&sqList);          //因为 size = 0 ,pos = -1 , 报错

         删除数据功能完毕。

3.3、改

        改数据的功能实现起来,逻辑上毫无难度可言。以下函数声明添加到 seqTab.h 中:

//修改指定位置数据
extern void DataModify(SeqTab*, int, DATATYPE);

         在 sqFunction.c 中写入:

void DataModify(SeqTab* sq, int pos, DATATYPE data)
{
    //数据有效性判断
	assert(sq);
	if (pos < 0 || pos > sq->size - 1)
	{
		printf("Illegal Position : %d\n", pos);
		return;
	}
    //修改数据
	sq->data[pos] = data;
}

        在 main 函数中追加以下代码验证:

	for (int i = 0; i < 10; i++)
	{
		DataPushTail(&sqList, i);
	}
	DataPrint(&sqList);            //打印 0 1 2 3 4 5 6 7 8 9
	DataModify(&sqList, 100, 30);  //报错
	DataModify(&sqList, 3, 30);
	DataPrint(&sqList);            //打印 0 1 2 30 4 5 6 7 8 9

         完毕。

3.4、查

        查到数据返回该数据的位置,查不到返回 -1 。同样很简单。 seqTab.h 中写入声明:

//在表中查找数据
extern int DataSearch(SeqTab*, DATATYPE);

        然后是 sqFunction.c 中写入:

int DataSearch(SeqTab* sq, DATATYPE data)
{
    //数据有效性判断
	assert(sq);
    //遍历数组
	for (int i = 0; i < sq->size; i++)
	{
        //如果找到数据则返回下标
		if (sq->data[i] == data)
		{
			return i;
		}
	}
    //遍历完毕仍找不到数据返回 -1
	return -1;
}

        main 函数中验证:

	int num = 1200;
	int pos = DataSearch(&sqList, num);
	if (pos == -1)
	{
		printf("The number \"%d\" is not exist!\n", num);
	}
	else
	{
		printf("The position of number \"%d\" is %d\n", num, pos);
	}//打印不存在

	num = 9;
	pos = DataSearch(&sqList, num);
	if (pos == -1)
	{
		printf("The number \"%d\" is not exist!\n", num);
	}
	else
	{
		printf("The position of number \"%d\" is %d\n", num, pos);
	}//打印 9

	DataModify(&sqList, DataSearch(&sqList, 8), 11001);
	DataPrint(&sqList);    //打印 0 1 2 30 4 5 6 7 11001 9

        至此增删改查功能实现均已完毕。除此之外,还有排序、截断等其他一系列可以自定的操作。总之,操作顺序表就是操作数组,实现起来难度几乎为 0 。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp基于u-grid-item九宫格实现uCharts秋云图表展示

uniapp基于uView的UI组件u-grid-item九宫格实现uCharts秋云可视化图表展示 这里使用uView的u-grid-item九宫格组件去显示图标排列 九宫格可以做成多列&#xff0c;移动设备上可以通过左右滑动进行展示 <template><div><div style"text-align: center;font…

报错:Parsed mapper file: ‘file mapper.xml

报错 &#xff1a; Logging initialized using class org.apache.ibatis.logging.stdout.StdOutImpl adapter. Registered plugin: com.github.yulichang.interceptor.MPJInterceptor3b2c8bda Parsed mapper file: file [/Mapper.xml] application无法启动 我这边产生原因是项…

python socket编程7 - 使用PyQt6 开发UI界面新增实现UDP server和client单机通讯的例子

在第五篇中&#xff0c;简单实现了命令行下的 TCP/UDP server和client的单机通讯。 在第六篇中&#xff0c;实现了PyQt6开发界面&#xff0c;TCP协议实现的单机server和client的通讯功能。 这一篇&#xff0c;在第六篇的基础上&#xff0c;增加了UDP server和client的单机通讯功…

我在USC南加大学游戏:真实经历/录取作品集_RoSSo艺术留学

近日&#xff0c;美国Common App最新早申统计数据&#xff1a;早申人数与疫情前相比增加了41%&#xff01;专注于国际艺术教育的RoSSo也发现&#xff0c;2022-2023申请季提交早申的学生中&#xff0c;各类热门院校以及艺术留学专业申请人数均是“涨”声一片&#xff01; 图源官…

30、pytest入门内容回顾

整体结构 解读与实操 pytest30讲主要从四个方面由浅入深的进行解读&#xff0c; 开始 讲解了pytest的概述&#xff0c;安装前的准备工作&#xff08;python,pycharm,pytest&#xff09;&#xff0c;运行方式&#xff08;命令行&#xff09;&#xff0c;断言&#xff08;assert…

Linux(统信UOS) 发布.Net Core,并开启Https,绑定证书

实际开发中&#xff0c;有时会需要为小程序或者需要使用https的应用提供API接口服务&#xff0c;这就需要为.Net Core 配置https&#xff0c;配置起来很简单&#xff0c;只需要在配置文件appsettings.json中添加下面的内容即可 "Kestrel": {"Endpoints": …

nodejs+vue+微信小程序+python+PHP天天网站书城管理系统的设计与实现-计算机毕业设计推荐

本项目主要分为前台模块与后台模块2个部分&#xff0c;详细描述如下&#xff1a;   &#xff08;1&#xff09;前台模块 首页: 首页可以起到导航的作用&#xff0c;用户想要了解网站 &#xff0c;网站首页为用户可以深入了解网站提供了一个平台&#xff0c;它就向一个“导游”…

Bionorica成功完成SAP S/4HANA升级 提升医药制造业务效率

企业如何成功地将其现有的ERP ECC系统转换升级到SAP S/4HANA&#xff0c; 并挖掘相关潜力来推动其数字化战略&#xff1f;Bionorica应用SNP软件实施了实时ERP套件&#xff0c;为进一步的增长和未来的创新奠定了基础。 草药市场的领导者&#xff1a;Bionorica Bionorica是世界领…

数据挖掘 分类模型选择

选择的模型有&#xff1a; 决策树、朴素贝叶斯、K近邻、感知机 调用的头文件有&#xff1a; import numpy as np import pandas as pd from matplotlib import pyplot as plt from sklearn.linear_model import Perceptron from sklearn.naive_bayes import GaussianNB from s…

软件测试面试题解析--什么题是必问的?

设计测试用例的主要方法有哪些&#xff1f;简述一下缺陷的生命周期&#xff1f;测试流程&#xff1f;项目流程&#xff1f;验收测试中和β测试区别&#xff1f;如何维护测试用例&#xff1f;每天测多少用例怎么分配的测试的一天能找多少bug你在上一家公司&#xff0c;写没写过测…

github首次将文件合到远端分支,发现名字不是master,而是main

暂存区和本地仓库的信息都存储在.git目录中其中 其中&#xff0c;暂存区和本地仓库的信息都存储在.git目录中 在自己的github上实践 1、刚开始&#xff0c;git clone gitgithub.com:lingze8678/my_github.git到本地 2、在克隆后的代码中加入一个pdf文件 3、在git bash中操作…

基本网络安全概述:保护您的数字生活

数字时代给我们的生活带来了无与伦比的连通性和便利&#xff0c;但也带来了新的威胁和漏洞。随着我们越来越依赖技术&#xff0c;网络安全概述的重要性怎么强调都不为过。在这篇文章中&#xff0c;我们将深入探讨网络安全的重要性、其关键组成部分、最佳实践、常见威胁以及该领…

WeakMap

WeakMap简介 作为es6一种新的数据结构&#xff0c;他是一种键值对的集合。与Map最大的区别有两个 1. 是其中的键必须是对象或非全局注册的符号。 全局注册的符号 const s1 Symbol.for(mySymbol) 非全局注册的符号 const s1 Symbol(mySymbol)了解Symbol.for 2. 不会创建对它…

Ansys Zemax | 手机镜头设计 - 第 3 部分:使用 STAR 模块和 ZOS-API 进行 STOP 分析

附件下载 联系工作人员获取附件 本文是 3 篇系列文章的一部分&#xff0c;该系列文章将讨论智能手机镜头模组设计的挑战&#xff0c;从概念、设计到制造和结构变形的分析。本文是三部分系列的第三部分。它涵盖了使用 Ansys Zemax OpticStudio Enterprise 版本提供的 STAR 技术…

ATECLOUD电源自动测试系统打破传统 助力新能源汽车电源测试

随着新能源汽车市场的逐步扩大&#xff0c;技术不断完善提升&#xff0c;新能源汽车测试变得越来越复杂&#xff0c;测试要求也越来越严格。作为新能源汽车的关键部件之一&#xff0c;电源为各个器件和整个电路提供稳定的电源&#xff0c;满足需求&#xff0c;确保新能源汽车的…

Homework 3: Higher-Order Functions, Self Reference, Recursion, Tree Recursion

Q1: Compose 编写一个高阶函数composer&#xff0c;它返回两个函数func和func_adder。 func是一个单参数函数&#xff0c;它应用到目前为止已经组合的所有函数。这些函数将首先应用最新的函数&#xff08;参见doctests和示例&#xff09;。 func_adder用于向我们的组合添加更多…

ESP32-Web-Server编程-在网页中插入图片

ESP32-Web-Server编程-在网页中插入图片 概述 图胜与言&#xff0c;在网页端显示含义清晰的图片&#xff0c;可以使得内容更容易理解。 需求及功能解析 本节演示在 ESP32 Web 服务器上插入若干图片。在插入图片时还可以对图片设置一个超链接&#xff0c;用户点击该图片时&a…

统计项目代码行数轻松搞定:使用 Node.js 脚本自动统计代码量

说在前面 在软件开发领域&#xff0c;了解项目的代码规模和复杂度对于项目管理、团队协作以及技术评估都至关重要。通过统计项目代码行数&#xff0c;我们能够更好地把握项目的整体情况&#xff0c;包括但不限于代码量的大小、不同类型文件的分布情况以及项目的结构和复杂度。这…

数据结构之选择排序

目录 直接选择排序 选择排序的时间复杂度 堆排序 向上调整算法 向下调整算法 向上调整算法建立堆 向下调整算法建立堆 堆排序整体代码 堆排序的时间复杂度 直接选择排序 在之前讲插入排序时&#xff0c;我们讲了这样的一个应用场景&#xff0c;我们在斗地主摸牌时&…

CoreDNS实战(十一)-分流与重定向

本文主要介绍了目前CoreDNS服务在外部域名递归结果过程中出现的一些问题以及使用dnsredir插件进行分流和alternate插件进行重试优化的操作。 1 自建DNS服务现状 一般来说&#xff0c;无论是bind9、coredns、dnsmasq、pdns哪类dns服务器&#xff0c;我们自建的监听在UDP53端口…