线性表顺序存储的优缺点

优点
  1. 无需为表中的逻辑关系增加额外的存储空间,利用连续的内存单元存储数据,存储密度高。
  2. 支持 随机访问,通过下标可在 O(1) 时间复杂度内定位元素(如数组按索引取值),查询效率稳定。
缺点
  1. 插入 / 删除低效:若操作位置非表尾,需移动大量元素(如插入到第 i 位,需移动后续 n−i 个元素 ),时间复杂度为 O(n) ,数据量大时性能差。
  2. 空间灵活性不足
    • 静态分配(如固定长度数组)易因数据超容量 “溢出”,或因预分配过多造成内存浪费;
    • 动态扩容(如编程语言中列表的自动扩容)需重新申请内存、拷贝数据,会带来额外开销。

顺序表操作函数

SeqList.h

// 条件编译:防止头文件被重复包含
// 如果没有定义 _SEQLIST_H_ 这个宏,就执行下面的代码,直到 #endif
#ifndef _SEQLIST_H_
#define _SEQLIST_H_       // 定义这个宏,标记头文件已被包含// 1. 定义人员信息结构体,并起别名为 DATATYPE(数据类型)
typedef struct person
{char name[32];  // 姓名:字符数组,最多存31个字符(含结束符'\0')char sex;       // 性别:单个字符(如 'M' 表示男,'F' 表示女)int age;        // 年龄:整数类型int score;      // 分数:整数类型(如考试成绩)
} DATATYPE;  // 别名 DATATYPE,后续可直接用 DATATYPE 代替 struct person// 2. 定义顺序表结构体,用于管理一组 DATATYPE 类型的数据
typedef struct list
{DATATYPE *head;  // 数组指针:指向存储人员信息的数组的首地址int tlen;        // total length:数组的总容量(最多能存多少个元素)int clen;        // current length:当前已存储的有效元素个数
} SeqList;  // 别名 SeqList,后续可直接用 SeqList 代替 struct list// 3. 函数声明:告诉编译器这些函数的存在、参数和返回值,具体实现在 .c 文件中// 创建顺序表
// 参数:len 为顺序表的初始容量
// 返回值:成功返回创建好的顺序表指针(SeqList*),失败返回 NULL
SeqList *CreateSeqList(int len);// 显示顺序表中的所有数据
// 参数:list 为要显示的顺序表指针
// 返回值:0 表示成功
int ShowSeqList(SeqList *list);// 向顺序表的尾部插入元素
// 参数:list 为目标顺序表,data 为要插入的人员信息(DATATYPE 指针)
// 返回值:0 表示成功,1 表示失败(如表已满)
int InsertTailSeqList(SeqList *list, DATATYPE *data);// 判断顺序表是否已满
// 参数:list 为目标顺序表
// 返回值:1 表示已满,0 表示未满
int IsFullSeqList(SeqList *list);// 判断顺序表是否为空
// 参数:list 为目标顺序表
// 返回值:1 表示为空,0 表示非空
int IsEmptySeqList(SeqList *list);// 按指定位置插入元素
// 参数:list 为目标顺序表,data 为要插入的信息,pos 为插入的下标位置
// 返回值:0 表示成功,1 表示失败(如位置无效或表已满)
int InsertPosSeqList(SeqList *list, DATATYPE *data, int pos);// 按姓名查找元素在顺序表中的位置
// 参数:list 为目标顺序表,name 为要查找的姓名
// 返回值:成功返回元素下标(0 开始),失败返回 -1
int FindSeqList(SeqList *list, char *name);// 按姓名修改顺序表中的元素
// 参数:list 为目标顺序表,old 为要修改的姓名,newdata 为新的信息
// 返回值:0 表示成功,1 表示失败(如未找到姓名)
int ModifySeqList(SeqList *list, char *old, DATATYPE *newdata);// 清空顺序表(仅重置有效元素个数,不释放内存)
// 参数:list 为目标顺序表
// 返回值:0 表示成功
int ClearSeqList(SeqList *list);// 销毁顺序表(释放所有分配的内存)
// 参数:list 为目标顺序表
// 返回值:0 表示成功
int DestroySeqList(SeqList *list);// 按姓名删除顺序表中的元素
// 参数:list 为目标顺序表,name 为要删除的姓名
// 返回值:0 表示成功,1 表示失败(如未找到姓名或表为空)
int DeleteSeqList(SeqList *list, char *name);// 获取顺序表的有效元素个数
// 参数:list 为目标顺序表
// 返回值:当前有效元素的个数(clen)
int GetSizeSeqList(SeqList *list);// 获取顺序表中指定位置的元素
// 参数:list 为目标顺序表,pos 为要获取的元素下标
// 返回值:成功返回元素的指针(DATATYPE*),失败返回 NULL
DATATYPE* GetItemSeqlist(SeqList* list, int pos);#endif  // 结束条件编译,与开头的 #if !defined(_SEQLIST_H_) 对应

SeqList.c

#include "seqlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>/*** @brief 创建一个顺序表* @param len 顺序表的初始容量(最多能存储的元素个数)* @return 成功返回顺序表指针,失败返回NULL*/
SeqList *CreateSeqList(int len)
{// 1. 为顺序表结构体本身分配内存(管理用的"文件夹")SeqList *sl = malloc(sizeof(SeqList));if (NULL == sl)  // 判断内存分配是否失败{perror("CreateSeqList malloc error\n");  // 打印错误信息return NULL;}// 2. 为顺序表中的数组分配内存(存储数据的"表格")// 数组元素类型是DATATYPE,共分配len个元素的空间sl->head = malloc(sizeof(DATATYPE) * len);if (NULL == sl->head)  // 判断数组内存分配是否失败{perror("CreateSeqList malloc2 error\n");free(sl);  // 先释放已分配的结构体内存,避免内存泄漏return NULL;}// 3. 初始化顺序表的属性sl->tlen = len;  // 总容量设为用户指定的lensl->clen = 0;    // 初始有效元素个数为0(空表)return sl;  // 返回创建好的顺序表
}/*** @brief 向顺序表尾部插入元素* @param list 目标顺序表* @param data 要插入的元素(人员信息)* @return 0表示成功,1表示失败*/
int InsertTailSeqList(SeqList *list, DATATYPE *data)
{// 1. 先判断顺序表是否已满if (IsFullSeqList(list)){printf("seqlist is full\n");return 1;}// 2. 把数据复制到数组的末尾(当前有效元素的下一个位置)// list->clen是当前有效元素个数,也是下一个元素的下标memcpy(&list->head[list->clen], data, sizeof(DATATYPE));// 3. 有效元素个数加1list->clen++;return 0;  // 插入成功
}/*** @brief 判断顺序表是否已满* @param list 目标顺序表* @return 1表示已满,0表示未满*/
int IsFullSeqList(SeqList *list)
{// 有效元素个数等于总容量时,表已满return list->clen == list->tlen;
}/*** @brief 打印顺序表中的所有元素* @param list 目标顺序表* @return 0表示成功*/
int ShowSeqList(SeqList *list)
{int len = GetSizeSeqList(list);  // 获取有效元素个数int i = 0;// 遍历所有有效元素并打印for (i = 0; i < len; ++i){printf("name:%s sex:%c age:%d score:%d\n", list->head[i].name,    // 姓名list->head[i].sex,     // 性别list->head[i].age,     // 年龄list->head[i].score);  // 分数};return 0;
}/*** @brief 获取顺序表的有效元素个数* @param list 被查询的顺序表* @return 顺序表中有效元素的个数*/
int GetSizeSeqList(SeqList *list)
{return list->clen;  // 直接返回当前有效元素个数
}/*** @brief 判断顺序表是否为空* @param list 目标顺序表* @return 1表示为空,0表示非空*/
int IsEmptySeqList(SeqList *list)
{return 0 == list->clen;  // 有效元素个数为0时为空
}/*** @brief 按位置插入元素(在指定下标处插入)* @param list 待插入的顺序表* @param data 需要插入的数据* @param pos 插入的位置(数组下标)* @return 0表示成功,1表示失败*/
int InsertPosSeqList(SeqList *list, DATATYPE *data, int pos)
{// 1. 检查顺序表是否已满if (IsFullSeqList(list)){printf("seqlist is full\n");return 1;}// 2. 检查插入位置是否合法int len = GetSizeSeqList(list);// 合法位置:0 <= pos <= len(可以插在末尾)if (pos < 0 || pos > len){printf("pos is incorrect\n");return 1;}// 3. 从最后一个元素开始,依次向后移动一位,给新元素腾出位置// (从后往前移,避免覆盖还没移动的元素)for (int i = list->clen; i > pos; i--){memcpy(&list->head[i], &list->head[i - 1], sizeof(DATATYPE));}// 4. 把新元素插入到pos位置memcpy(&list->head[pos], data, sizeof(DATATYPE));// 5. 有效元素个数加1list->clen++;return 0;
}/*** @brief 按名字查找元素在顺序表中的位置* @param list 待查询的顺序表* @param name 要查询的姓名* @return 成功返回元素下标(0~len-1),失败返回-1*/
int FindSeqList(SeqList *list, char *name)
{int len = GetSizeSeqList(list);// 遍历所有元素,对比姓名(字符串比较用strcmp)for (int i = 0; i < len; i++){// strcmp返回0表示两个字符串相等if (0 == strcmp(list->head[i].name, name)){return i;  // 找到,返回下标}}return -1;  // 没找到
}/*** @brief 获取顺序表中指定位置的元素* @param list 目标顺序表* @param pos 要获取的元素位置(下标)* @return 成功返回元素的指针,失败返回NULL*/
DATATYPE* GetItemSeqlist(SeqList* list, int pos)
{int len = GetSizeSeqList(list);// 检查位置是否合法(0 <= pos < len)if (pos < 0 || pos >= len)  // 注意这里是>=,因为最大下标是len-1{printf("pos is incorrect\n");return NULL;}// 返回pos位置元素的地址return &list->head[pos];
}/*** @brief 按姓名修改顺序表中的元素* @param list 目标顺序表* @param oldname 要修改的元素的姓名* @param newdata 新的元素数据* @return 0表示成功,1表示失败*/
int ModifySeqList(SeqList *list, char *oldname, DATATYPE *newdata)
{// 1. 先查找旧元素的位置int ret = FindSeqList(list, oldname);if(-1 == ret)  // 没找到{printf("modify failure...\n");return 1;}// 2. 用新数据覆盖旧数据memcpy(&list->head[ret], newdata, sizeof(DATATYPE));return 0;
}/*** @brief 清空顺序表(仅清空数据,不释放内存)* @param list 目标顺序表* @return 0表示成功*/
int ClearSeqList(SeqList *list)
{// 直接将有效元素个数设为0,原数据会被后续插入覆盖list->clen = 0 ;return 0;
}/*** @brief 销毁顺序表(释放所有分配的内存)* @param list 目标顺序表* @return 0表示成功*/
int DestroySeqList(SeqList *list)
{// 先释放存储数据的数组内存free(list->head);// 再释放顺序表结构体本身的内存free(list);return 0;
}/*** @brief 按姓名删除顺序表中的元素* @param list 目标顺序表* @param name 要删除的元素的姓名* @return 0表示成功,1表示失败(未找到或表为空)*/
int DeleteByNameSeqList(SeqList *list, char *name)
{// 1. 先判断表是否为空,空表无需删除if (IsEmptySeqList(list)){printf("seqlist is empty, cannot delete\n");return 1;}// 2. 查找要删除的元素位置(调用已有的FindSeqList函数)int pos = FindSeqList(list, name);if (pos == -1)  // 未找到对应姓名的元素{printf("name '%s' not found, delete failed\n", name);return 1;}// 3. 从删除位置的下一个元素开始,依次向前移动一位,覆盖要删除的元素// (从前往后移,避免覆盖还未移动的元素)for (int i = pos; i < list->clen - 1; i++){// 用后一个元素覆盖当前元素(复制整个DATATYPE结构体)memcpy(&list->head[i], &list->head[i + 1], sizeof(DATATYPE));}// 4. 有效元素个数减1(表的长度减1)list
->clen--;printf("delete '%s' successfully\n", name);return 0;
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include "seqlist.h"int main(int argc, char** argv)
{// 1. 创建容量为5的顺序表SeqList* sl = CreateSeqList(5);if (sl == NULL)  // 增加创建失败的判断{printf("创建顺序表失败,程序退出\n");return 1;  // 非0表示程序异常退出}// 2. 定义测试数据(5个人员信息,与顺序表容量匹配)DATATYPE data[] = {{"zhangsan", 'm', 20, 80},   // 下标0{"lisi", 'f', 23, 84},       // 下标1{"wangmaizi", 'f', 32, 90},  // 下标2{"guanerge", 'm', 50, 91},   // 下标3{"liubei", 'm', 51, 92}      // 下标4};// 3. 尾插3个元素(zhangsan、lisi、wangmaizi)printf("-----初始尾插3个元素后-----\n");InsertTailSeqList(sl, &data[0]);InsertTailSeqList(sl, &data[1]);InsertTailSeqList(sl, &data[2]);ShowSeqList(sl);  // 显示当前表中数据// 4. 在位置1插入guanerge(原lisi的位置将后移)printf("\n-----在位置1插入guanerge后-----\n");int insert_ret = InsertPosSeqList(sl, &data[3], 1);if (insert_ret != 0){printf("插入失败!\n");}else{ShowSeqList(sl);  // 此时表中应有4个元素}// 5. 查找不存在的姓名(li1si)printf("\n-----查找不存在的姓名li1si-----\n");char want_name[50] = "li1si";int find_ret = FindSeqList(sl, want_name);if (find_ret == -1){printf("未找到姓名为 %s 的人员\n", want_name);}else{DATATYPE* tmp = GetItemSeqlist(sl, find_ret);printf("找到人员:%s,分数:%d\n", tmp->name, tmp->score);}// 6. 查找存在的姓名(lisi)printf("\n-----查找存在的姓名lisi-----\n");strcpy(want_name, "lisi");  // 修改要查找的姓名find_ret = FindSeqList(sl, want_name);if (find_ret == -1){printf("未找到姓名为 %s 的人员\n", want_name);}else{DATATYPE* tmp = GetItemSeqlist(sl, find_ret);printf("找到人员:%s,年龄:%d,分数:%d\n", tmp->name, tmp->age, tmp->score);}// 7. 修改lisi的信息为liubeiprintf("\n-----修改lisi为liubei后-----\n");int modify_ret = ModifySeqList(sl, "lisi", &data[4]);if (modify_ret != 0){printf("修改失败!\n");}else{ShowSeqList(sl);  // 查看修改后的结果}// 8. 新增:删除姓名为zhangsan的元素printf("\n-----删除zhangsan后-----\n");int delete_ret = DeleteSeqList(sl, "zhangsan");if (delete_ret != 0){printf("删除失败!\n");}else{ShowSeqList(sl);  // 查看删除后的结果}// 9. 销毁顺序表,释放内存DestroySeqList(sl);sl = NULL;  // 避免野指针(销毁后指针置空)// 调试用:暂停程序查看输出(Windows系统)// system("pause");return 0;
}

运行结果

-----初始尾插3个元素后-----
name:zhangsan sex:m age:20 score:80
name:lisi sex:f age:23 score:84
name:wangmaizi sex:f age:32 score:90-----在位置1插入guanerge后-----
name:zhangsan sex:m age:20 score:80
name:guanerge sex:m age:50 score:91
name:lisi sex:f age:23 score:84
name:wangmaizi sex:f age:32 score:90-----查找不存在的姓名li1si-----
未找到姓名为 li1si 的人员-----查找存在的姓名lisi-----
找到人员:lisi,年龄:23,分数:84-----修改lisi为liubei后-----
name:zhangsan sex:m age:20 score:80
name:guanerge sex:m age:50 score:91
name:liubei sex:m age:51 score:92
name:wangmaizi sex:f age:32 score:90-----删除zhangsan后-----
name:guanerge sex:m age:50 score:91
name:liubei sex:m age:51 score:92
name:wangmaizi sex:f age:32 score:90

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

反向代理实现服务器联网

下载脚本&#xff1a;https://gitee.com/995770513/ssh-reverse-socket然后解压到 D:\Download在本机运行 cd D:\Download\ssh-reverse-socket-master\ssh-reverse-socket-master python socket5_proxy.py --ssh_cmd "xaserver10.150.10.51 -p 22" --socket5_port 78…

C语言关于函数传参和返回值的一些想法2(参数可修改的特殊情况)

我最近写了一篇文章名为“C语言关于函数传参和返回值的一些想法”&#xff08;C语言关于函数传参和返回值的一些想法-CSDN博客&#xff09;&#xff0c;里面提到了一种观点就是传参的参数在函数体内部是只读的&#xff0c;不能写它&#xff0c;因为如果写了&#xff0c;也就是污…

前端AI对话功能实现攻略

一、对话内容渲染 在前端页面的 AI 对话场景中&#xff0c;对话内容的渲染效果直接影响用户的阅读体验和交互效率。合理选择对话格式、优化流式对话呈现、嵌入自定义内容以及实现语音播报等功能&#xff0c;是提升整体体验的关键。 对话格式选择 MarkDown 作为一种轻量级标记语…

深入理解Redis持久化:让你的数据永不丢失

1 Redis持久化概述 1.1 什么是Redis持久化 Redis作为一个高性能的内存数据库,默认情况下数据存储在内存中,这意味着一旦服务器重启或发生故障,内存中的数据将会丢失。为了保证数据的持久性和可靠性,Redis提供了持久化机制,将内存中的数据保存到磁盘中。 持久化是Redis实…

IC验证 AHB-RAM 项目(二)——接口与事务代码的编写

目录准备工作接口相关代码编写事务相关代码编写准备工作 DVT&#xff08;Design and Verification Tools&#xff09;是一款专门为 IC 验证打造的 IDE 插件&#xff0c;可以理解为智能的 Verilog/System Verilog 编辑器&#xff0c;在 VS Code、Eclipse 软件中使用。 接口相关…

基于Spring Boot的智能民宿预订与游玩系统设计与实现 民宿管理系统 民宿预订系统 民宿订房系统

&#x1f525;作者&#xff1a;it毕设实战小研&#x1f525; &#x1f496;简介&#xff1a;java、微信小程序、安卓&#xff1b;定制开发&#xff0c;远程调试 代码讲解&#xff0c;文档指导&#xff0c;ppt制作&#x1f496; 精彩专栏推荐订阅&#xff1a;在下方专栏&#x1…

大模型的底层运算线性代数

深度学习的本质是用数学语言描述并处理真实世界中的信息&#xff0c;而线性代数正是这门语言的基石。它不仅提供了高效的数值计算工具&#xff0c;更在根本上定义了如何以可计算、可组合、可度量的方式表示和变换数据。 1 如何描述世界&#x1f4ca; 真实世界的数据&#xff08…

Rust 中 i32 与 *i32 的深度解析

Rust 中 &i32 与 *i32 的深度解析 在 Rust 中&#xff0c;&i32 和 *i32 是两种完全不同的指针类型&#xff0c;它们在安全性、所有权和使用方式上有本质区别。以下是详细对比&#xff1a; 核心区别概览 #mermaid-svg-rCa8lLmHB7MK9P6K {font-family:"trebuchet ms…

【PyTorch项目实战】OpenNMT本地机器翻译框架 —— 支持本地部署和自定义训练

文章目录一、OpenNMT&#xff08;Neural Machine Translation&#xff0c;NMT&#xff09;1. 概述2. 核心特性3. 系统架构4. 与其他翻译工具的区别二、基于 OpenNMT-py 的机器翻译框架1. 环境配置&#xff08;以OpenNMT-py版本为例&#xff09;&#xff08;1&#xff09;pip安装…

基于prompt的生物信息学:多组学分析的新界面

以前总以为综述/评论是假大空&#xff0c;最近在朋友的影响下才发现&#xff0c;大佬的综述/评论内容的确很值得一读&#xff0c;也值得分享的。比如这篇讲我比较感兴趣的AI辅助生信分析的&#xff0c;相信大家都是已经实践中用上了&#xff0c;看看大佬的评论&#xff0c;拓宽…

Nacos-8--分析一下nacos中的AP和CP模式

Nacos支持两种模式来满足不同场景下的需求&#xff1a;AP模式&#xff08;强调可用性&#xff09;和CP模式&#xff08;强调一致性&#xff09;。 这两种模式的选择主要基于CAP理论&#xff0c;该理论指出在一个分布式系统中&#xff0c;无法同时保证一致性&#xff08;Consist…

水闸安全监测的主要核心内容

水闸安全监测是指通过一系列技术手段和管理措施&#xff0c;对水闸的结构状态、运行性能及环境条件进行实时或定期的观测与评估&#xff0c;以确保水闸在设计寿命期内的安全性和可靠性。其核心目标是及时发现潜在的安全隐患&#xff0c;防止事故发生&#xff0c;保障水利工程的…

嵌入式系统学习Day19(数据结构)

数据结构的概念&#xff1a; 相互之间存在一种或多种特定关系的数据元素的集合。数据之间关系&#xff1a;逻辑关系&#xff1a;集合&#xff0c;线性&#xff08;1对1&#xff0c;中间位置的值有且仅有一个前驱&#xff0c;一个后继&#xff09;&#xff0c;树&#xff08;1对…

Pandas中数据清理、连接数据以及合并多个数据集的方法

一、简介1.数据清理的重要性&#xff1a;在进行数据分析前&#xff0c;需进行数据清理&#xff0c;使每个观测值成一行、每个变量成一列、每种观测单元构成一张表格。2.数据组合的必要性&#xff1a;数据整理好后&#xff0c;可能需要将多张表格组合才能进行某些分析&#xff0…

JavaSSM框架从入门到精通!第二天(MyBatis(一))!

一、 Mybatis 框架1. Mybatis 框架简介Mybatis 是 apache 的一个开源项目&#xff0c;名叫 iBatis &#xff0c;2010 年这个项目由 apache 迁移到了 google&#xff0c;并命名为 Mybatis&#xff0c;2013 年迁移到了 GitHub&#xff0c;可以在 GitHub 下载源码。2. Mybatis 的下…

Linux下Mysql命令,创建mysql,删除mysql

在 Linux 系统下&#xff0c;您可以通过命令行来创建和删除 MySQL 数据库。以下是详细的操作步骤&#xff0c;包括创建和删除数据库、用户&#xff0c;以及常见的相关管理命令。1. 登录 MySQL在执行任何 MySQL 操作之前&#xff0c;需要先登录 MySQL。1.1 使用 root 用户登录 M…

假设检验的原理

假设检验是统计学中用于判断样本数据是否支持某个特定假设的方法。其核心思想是通过样本数据对总体参数或分布提出假设&#xff0c;并利用统计量来判断这些假设的合理性。假设检验的基本步骤如下&#xff1a;1. 假设&#xff08;Hypothesis&#xff09;在统计学中&#xff0c;假…

信号、内存共享等实现

信号&#xff08;signal&#xff09;#include <signal.h> #include <stdio.h> #include <unistd.h>void handler(int sig) {printf("收到信号: %d\n", sig); }int main() {signal(SIGUSR1, handler); // 注册用户自定义信号printf("进程 PI…

《从日常到前沿:AI 在教育、医疗、制造业的真实落地案例》文章提纲

引言&#xff1a;AI 落地的多元图景​简述 AI 从实验室走向实际应用的发展趋势​说明选择教育、医疗、制造业的原因 —— 覆盖民生与基础产业&#xff0c;落地场景具有代表性​AI 在教育领域的落地案例​个性化学习&#xff1a;如某在线教育平台利用 AI 分析学生学习数据&#…

决策树(1)

一、树模型与决策树基础决策树概念&#xff1a;从根节点开始一步步走到叶子节点得出决策&#xff0c;所有数据最终都会落到叶子节点&#xff0c;既可用于分类&#xff0c;也可用于回归。树的组成根节点&#xff1a;第一个选择点。非叶子节点与分支&#xff1a;中间决策过程。叶…