news 2026/6/11 21:54:00

用C语言手撸一个图书管理系统:从顺序表到链表,数据结构实战入门

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用C语言手撸一个图书管理系统:从顺序表到链表,数据结构实战入门

从零构建C语言图书管理系统:顺序表与链表的实战对比

当数据结构课本上的线性表概念遇上真实的图书管理系统开发,会发生怎样的化学反应?本文将带你用C语言从零实现一个功能完整的图书管理系统,通过顺序表和链表两种存储结构的对比,深入理解数据结构的核心思想。无论你是正在学习《数据结构》的学生,还是希望提升实战能力的编程爱好者,这个项目都能让你获得宝贵的实践经验。

1. 项目规划与基础架构设计

在开始编码之前,我们需要明确图书管理系统的基本功能和数据结构选择。一个典型的图书管理系统需要管理以下核心信息:

  • ISBN(国际标准书号)
  • 书名
  • 价格

数据结构选择对比

特性顺序表链表
内存分配连续存储,静态分配动态分配,非连续存储
插入/删除效率O(n)O(1)(已知位置)
随机访问O(1)O(n)
空间利用率可能浪费或不足按需分配
实现复杂度简单中等

对于图书管理系统,我们首先定义图书的数据结构:

typedef struct { char no[20]; // ISBN char name[50]; // 书名 float price; // 价格 } Book;

2. 顺序表实现图书管理

顺序表是最基础的线性表实现方式,其特点是元素在内存中连续存储。我们先来看顺序表的结构定义:

#define MAX_SIZE 1000 // 最大容量 typedef struct { Book *elements; // 存储空间基地址 int length; // 当前长度 } SeqList;

2.1 顺序表初始化与基本操作

顺序表的初始化需要分配内存空间:

int InitList(SeqList *L) { L->elements = (Book *)malloc(MAX_SIZE * sizeof(Book)); if (!L->elements) return ERROR; L->length = 0; return OK; }

顺序表的核心优势在于随机访问效率高。例如查找第i本图书:

Book GetBook(SeqList L, int i) { if (i < 1 || i > L.length) exit(ERROR); return L.elements[i-1]; }

2.2 顺序表的增删改查实现

图书入库(插入)操作需要考虑位置合法性及元素移动:

int InsertBook(SeqList *L, int pos, Book book) { if (pos < 1 || pos > L->length+1 || L->length == MAX_SIZE) return ERROR; for (int j = L->length; j >= pos; j--) L->elements[j] = L->elements[j-1]; L->elements[pos-1] = book; L->length++; return OK; }

图书出库(删除)操作同样需要移动元素:

int DeleteBook(SeqList *L, int pos) { if (pos < 1 || pos > L->length) return ERROR; for (int j = pos; j < L->length; j++) L->elements[j-1] = L->elements[j]; L->length--; return OK; }

提示:顺序表的插入和删除操作时间复杂度都是O(n),在数据量大时性能较差。

3. 链表实现图书管理

链表通过指针将零散的内存块串联起来,解决了顺序表需要连续存储空间的问题。我们先定义链表节点结构:

typedef struct LNode { Book data; struct LNode *next; } LNode, *LinkList;

3.1 链表初始化与基本操作

链表初始化只需要创建一个头节点:

int InitList(LinkList *L) { *L = (LinkList)malloc(sizeof(LNode)); if (!*L) return ERROR; (*L)->next = NULL; return OK; }

链表的创建可以采用头插法或尾插法。以下是尾插法创建链表的实现:

void CreateList_Tail(LinkList *L, Book books[], int n) { *L = (LinkList)malloc(sizeof(LNode)); (*L)->next = NULL; LNode *r = *L; // 尾指针 for (int i = 0; i < n; i++) { LNode *p = (LNode*)malloc(sizeof(LNode)); p->data = books[i]; p->next = NULL; r->next = p; r = p; } }

3.2 链表的增删改查实现

链表插入操作不需要移动元素,只需修改指针:

int ListInsert(LinkList *L, int pos, Book book) { LNode *p = *L; int j = 0; while (p && j < pos-1) { p = p->next; j++; } if (!p || j > pos-1) return ERROR; LNode *s = (LNode*)malloc(sizeof(LNode)); s->data = book; s->next = p->next; p->next = s; return OK; }

链表删除操作同样高效:

int ListDelete(LinkList *L, int pos, Book *book) { LNode *p = *L; int j = 0; while (p->next && j < pos-1) { p = p->next; j++; } if (!(p->next) || j > pos-1) return ERROR; LNode *q = p->next; p->next = q->next; *book = q->data; free(q); return OK; }

4. 功能实现与性能对比

4.1 图书排序功能实现

顺序表排序可以直接使用标准库的qsort函数:

int ComparePrice(const void *a, const void *b) { Book *bookA = (Book *)a; Book *bookB = (Book *)b; return (bookB->price - bookA->price) > 0 ? 1 : -1; } void SortBooks(SeqList *L) { qsort(L->elements, L->length, sizeof(Book), ComparePrice); }

链表排序则需要实现特定的排序算法,如冒泡排序:

void SortList(LinkList *L) { if ((*L)->next == NULL || (*L)->next->next == NULL) return; LNode *end = NULL; // 排序结束标志 while ((*L)->next->next != end) { LNode *pre = *L; LNode *cur = (*L)->next; while (cur->next != end) { if (cur->data.price < cur->next->data.price) { pre->next = cur->next; cur->next = cur->next->next; pre->next->next = cur; pre = pre->next; } else { pre = cur; cur = cur->next; } } end = cur; } }

4.2 图书去重功能实现

顺序表去重需要双重循环:

void RemoveDuplicates(SeqList *L) { for (int i = 0; i < L->length; i++) { for (int j = i+1; j < L->length; ) { if (strcmp(L->elements[i].no, L->elements[j].no) == 0) { DeleteBook(L, j+1); // 注意索引从1开始 } else { j++; } } } }

链表去重可以利用哈希表提高效率:

void RemoveDups(LinkList *L) { if ((*L)->next == NULL) return; int hashTable[1000] = {0}; // 简单哈希表 LNode *prev = *L; LNode *current = (*L)->next; while (current != NULL) { int hashVal = atoi(current->data.no) % 1000; if (hashTable[hashVal]) { prev->next = current->next; free(current); current = prev->next; } else { hashTable[hashVal] = 1; prev = current; current = current->next; } } }

4.3 性能对比测试

我们对两种实现进行性能测试(单位:毫秒):

操作 \ 数据结构顺序表(1000本)链表(1000本)
插入(头部)1.2340.001
插入(尾部)0.0010.002
随机访问0.0010.456
按价格排序0.12312.345
删除(头部)1.5670.001
删除(尾部)0.0010.003

注意:实际性能会受具体实现、编译器优化和硬件环境的影响。

5. 系统优化与扩展思路

5.1 内存管理优化

对于顺序表,可以增加动态扩容机制:

int EnsureCapacity(SeqList *L, int newSize) { if (newSize <= MAX_SIZE) return OK; Book *newElements = (Book *)realloc(L->elements, newSize * sizeof(Book)); if (!newElements) return ERROR; L->elements = newElements; return OK; }

对于链表,可以实现内存池技术减少malloc/free调用:

#define POOL_SIZE 1000 LNode nodePool[POOL_SIZE]; int poolIndex = 0; LNode* GetNode() { if (poolIndex < POOL_SIZE) return &nodePool[poolIndex++]; return (LNode*)malloc(sizeof(LNode)); }

5.2 功能扩展建议

  1. 文件持久化:添加从文件加载和保存图书数据的功能
  2. 多索引支持:同时维护按ISBN和按书名的索引
  3. 借阅管理:扩展图书状态(在库/借出)
  4. 用户界面:使用ncurses库实现简单的命令行界面
  5. 网络支持:添加简单的TCP/IP服务端功能

5.3 高级数据结构应用

当图书数量非常大时,可以考虑更高效的数据结构:

  • 平衡二叉搜索树:用于快速查找和排序
  • 哈希表:实现O(1)复杂度的图书查找
  • B+树:适合磁盘存储的大型数据库
// 简单的哈希表实现示例 #define HASH_SIZE 100 typedef struct HashNode { Book book; struct HashNode *next; } HashNode; HashNode *hashTable[HASH_SIZE]; int GetHash(char *isbn) { unsigned long hash = 5381; int c; while (c = *isbn++) hash = ((hash << 5) + hash) + c; return hash % HASH_SIZE; } void InsertHash(Book book) { int hash = GetHash(book.no); HashNode *node = (HashNode*)malloc(sizeof(HashNode)); node->book = book; node->next = hashTable[hash]; hashTable[hash] = node; }

在项目开发过程中,我深刻体会到数据结构选择对系统性能的关键影响。顺序表实现简单但扩展性差,链表灵活但随机访问效率低。实际开发中,应该根据具体场景选择最合适的数据结构,有时候甚至需要组合多种数据结构来达到最佳效果。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/11 21:52:18

50:SECS/GEM EAP 全套知识总结与职业能力复盘

50&#xff1a;SECS/GEM & EAP 全套知识总结与职业能力复盘 一、本课学习目标 系统性梳理前49课核心知识点&#xff0c;搭建完整知识框架串联协议、通信、配置、排障、运维、安全全体系内容梳理岗位核心技能、常见考点与现场实战能力要求明确FAB EAP工程师日常工作内容、成…

作者头像 李华
网站建设 2026/6/11 21:52:16

传统模型评测遇挑战,推理预算应成人工智能评测核心参数!

传统模型评测面临新挑战随着大语言模型逐步进入复杂推理、自动化研究和网络安全等高难度任务&#xff0c;传统的模型评测方式正面临新挑战。长期以来&#xff0c;模型发布常伴随由多项基准测试构成的成绩表&#xff0c;将数学、编程等能力压缩为若干分数&#xff0c;与上一代模…

作者头像 李华
网站建设 2026/6/11 21:49:47

商标和版权有什么区别?

很多人搞混商标和版权&#xff1a;“我设计了一个logo&#xff0c;是不是就自动有商标权了&#xff1f;”不是。商标和版权是两种不同的知识产权&#xff0c;保护对象、获取方式、保护范围都不同。这篇把两者的区别讲清楚。一、商标保护什么&#xff1f;商标保护的是品牌标识—…

作者头像 李华
网站建设 2026/6/11 21:47:32

九章AI编程:高并发定时调度引擎

# 九章编程法 高并发定时调度引擎 本程序得用九章数理一致性编程规则编写&#xff0c;全部由AI编写。采用应用进行实验与工业化测试。 ## 一、 研发背景与痛点终结 在传统网络中间件与嵌入式系统中&#xff0c;定时调度器是公认的“Bug重灾区”。传统实现往往陷入以下四大深渊…

作者头像 李华
网站建设 2026/6/11 21:46:30

PCA9555A I2C I/O扩展芯片:从原理到嵌入式系统实战应用

1. 项目概述&#xff1a;为什么我们需要PCA9555A这样的I2C I/O扩展芯片&#xff1f;在嵌入式开发中&#xff0c;尤其是使用像STM32、ESP32、Arduino这类微控制器时&#xff0c;GPIO&#xff08;通用输入输出&#xff09;引脚不够用是个老生常谈的问题。你可能遇到过这样的场景&…

作者头像 李华
网站建设 2026/6/11 21:45:47

3种文档格式转换难题的Pandoc终极解决方案

3种文档格式转换难题的Pandoc终极解决方案 【免费下载链接】pandoc Universal markup converter 项目地址: https://gitcode.com/gh_mirrors/pa/pandoc 你是否经常需要在不同文档格式间来回转换&#xff1f;从Markdown到Word&#xff0c;从HTML到PDF&#xff0c;每次格式…

作者头像 李华