嵌入式Linux学习笔记 | 数据结构(Day02)顺序表核心功能实现 + 快速排序 + 折半查找 + 作业实战
点赞关注私信领取全部代码资料~顺序表是数据结构中最基础的线性表存储结构本质是用一段连续的物理地址存储数据元素支持随机访问是学习数据结构的入门核心。本文将完整实现顺序表的查找、修改、排序核心功能详解快速排序、折半查找经典算法并配套实战作业代码可直接编译运行适合学习和面试备考。一、前置准备顺序表结构体定义为了让顺序表支持任意数据类型int、char、自定义结构体等采用泛型设计核心包含数据指针、元素大小、有效长度、最大容量。#include stdio.h #include stdlib.h #include string.h // 函数指针类型比较函数适配任意数据类型 // 返回值0 前者大0 相等0 后者大 typedef int (*cmp_t)(const void *, const void *); // 顺序表结构体泛型 typedef struct { void *data; // 存储数据的基地址无类型指针适配任意数据 int elem_size; // 单个元素的大小字节 int length; // 当前有效元素个数 int capacity; // 最大容量 } seqlist_t;通用工具函数顺序表初始化// 初始化顺序表容量capacity单个元素大小elem_size seqlist_t *seqlist_create(int capacity, int elem_size) { seqlist_t *s (seqlist_t *)malloc(sizeof(seqlist_t)); s-data malloc(elem_size * capacity); s-elem_size elem_size; s-length 0; s-capacity capacity; return s; }顺序表插入尾部插入辅助测试// 尾部插入元素 int seqlist_push_back(seqlist_t *s, const void *val) { if (s-length s-capacity) return -1; // 满了返回失败 // 计算插入位置基地址 偏移量 char *pos (char *)s-data s-length * s-elem_size; memcpy(pos, val, s-elem_size); s-length; return 0; }顺序表遍历辅助测试以 int 类型为例// 遍历int类型顺序表 void seqlist_print_int(seqlist_t *s) { for (int i 0; i s-length; i) { int *val (int *)((char *)s-data i * s-elem_size); printf(%d , *val); } printf(\n); }二、顺序表核心功能补充实现a. 查找功能函数原型void *seqlist_search(const seqlist_t *s, const void *key, cmp_t cmp);顺序表查找采用线性遍历思想遍历顺序表中所有有效元素借助自定义比较函数匹配目标关键字若找到匹配元素则返回该元素内存地址未找到则返回空值。该方法通用性极强依托泛型设计能够适配整数、字符串、自定义结构体等多种数据类型是无序顺序表中最常用的查找方式。/** * brief 顺序表查找 * param s 顺序表指针 * param key 查找关键字 * param cmp 比较函数指针 * return 找到返回元素地址未找到返回NULL */ void *seqlist_search(const seqlist_t *s, const void *key, cmp_t cmp) { // 遍历所有有效元素 for (int i 0; i s-length; i) { // 计算当前元素地址 char *elem (char *)s-data i * s-elem_size; // 比较相等返回地址 if (cmp(elem, key) 0) { return elem; } } // 未找到 return NULL; }b. 修改功能功能描述先根据关键字查找元素找到后用new_data替换原数据返回 0 成功-1 失败。函数原型int seqlist_modify(seqlist_t *s, const void *key, cmp_t cmp, const void *new_data);注原命名和查找函数冲突修改为seqlist_modify更符合语义。/** * brief 顺序表修改元素 * param s 顺序表指针 * param key 查找关键字 * param cmp 比较函数指针 * param new_data 新数据 * return 0成功-1未找到/失败 */ int seqlist_modify(seqlist_t *s, const void *key, cmp_t cmp, const void *new_data) { // 1. 查找元素 void *elem seqlist_search(s, key, cmp); if (elem NULL) return -1; // 未找到 // 2. 替换为新数据 memcpy(elem, new_data, s-elem_size); return 0; }c. 插入排序功能描述对顺序表进行插入排序支持任意数据类型通过比较函数指定排序规则。函数原型void seqlist_insert_sort(seqlist_t *s, cmp_t cmp);/** * brief 顺序表插入排序 * param s 顺序表指针 * param cmp 比较函数指针决定升序/降序 */ void seqlist_insert_sort(seqlist_t *s, cmp_t cmp) { int i, j; // 临时变量存储待插入元素 char *temp (char *)malloc(s-elem_size); // 从第二个元素开始遍历 for (i 1; i s-length; i) { // 待插入元素存入temp memcpy(temp, (char *)s-data i * s-elem_size, s-elem_size); // 向前遍历找到插入位置 for (j i - 1; j 0; j--) { char *curr (char *)s-data j * s-elem_size; // 若当前元素 temp升序后移 if (cmp(curr, temp) 0) { memcpy(curr s-elem_size, curr, s-elem_size); } else { break; } } // 插入temp到正确位置 memcpy((char *)s-data (j 1) * s-elem_size, temp, s-elem_size); } free(temp); }三、快速排序核心算法核心思想选择基准元素通常选第一个 / 最后一个 / 中间元素一趟排序完成两件事确定基准元素的最终位置分区基准左侧元素 ≤ 基准基准右侧元素 ≥ 基准递归对左右子序列重复上述步骤直到序列有序。完整实现适配顺序表// 快速排序分区函数找到基准位置并分区 static int _quick_partition(seqlist_t *s, int left, int right, cmp_t cmp) { // 选第一个元素为基准 char *pivot (char *)s-data left * s-elem_size; char *temp (char *)malloc(s-elem_size); while (left right) { // 从右往左找 ≤ 基准的元素 while (left right cmp((char *)s-data right * s-elem_size, pivot) 0) { right--; } // 移到左边 memcpy(temp, (char *)s-data right * s-elem_size, s-elem_size); memcpy((char *)s-data left * s-elem_size, temp, s-elem_size); // 从左往右找 ≥ 基准的元素 while (left right cmp((char *)s-data left * s-elem_size, pivot) 0) { left; } // 移到右边 memcpy(temp, (char *)s-data left * s-elem_size, s-elem_size); memcpy((char *)s-data right * s-elem_size, temp, s-elem_size); } // 基准归位 memcpy((char *)s-data left * s-elem_size, pivot, s-elem_size); free(temp); return left; // 返回基准下标 } // 快速排序递归函数 static void _quick_sort(seqlist_t *s, int left, int right, cmp_t cmp) { if (left right) { // 找到基准位置 int pivot_pos _quick_partition(s, left, right, cmp); // 递归左子序列 _quick_sort(s, left, pivot_pos - 1, cmp); // 递归右子序列 _quick_sort(s, pivot_pos 1, right, cmp); } } // 对外接口顺序表快速排序 void seqlist_quick_sort(seqlist_t *s, cmp_t cmp) { _quick_sort(s, 0, s-length - 1, cmp); }四、折半查找二分查找核心思想前提序列必须有序取有序序列中间元素与查找值比较相等 → 找到查找值 中间元素 → 去左半区间查找查找值 中间元素 → 去右半区间查找重复直到找到元素或区间为空。完整实现/** * brief 折半查找仅适用于有序顺序表 * param s 有序顺序表 * param key 查找关键字 * param cmp 比较函数 * return 找到返回元素地址未找到返回NULL */ void *seqlist_binary_search(const seqlist_t *s, const void *key, cmp_t cmp) { int left 0, right s-length - 1; while (left right) { // 计算中间下标避免溢出 int mid left (right - left) / 2; char *mid_elem (char *)s-data mid * s-elem_size; int ret cmp(mid_elem, key); if (ret 0) { return mid_elem; // 找到 } else if (ret 0) { right mid - 1; // 左半区间 } else { left mid 1; // 右半区间 } } return NULL; // 未找到 }五、实战作业代码实现作业 1将顺序表倒序思路双指针首尾元素交换直到指针相遇。// 顺序表倒序 void seqlist_reverse(seqlist_t *s) { int left 0, right s-length - 1; char *temp (char *)malloc(s-elem_size); while (left right) { char *l_elem (char *)s-data left * s-elem_size; char *r_elem (char *)s-data right * s-elem_size; // 交换首尾元素 memcpy(temp, l_elem, s-elem_size); memcpy(l_elem, r_elem, s-elem_size); memcpy(r_elem, temp, s-elem_size); left; right--; } free(temp); }作业 2求顺序表最大值、最小值参数回填思路遍历顺序表通过指针参数将最大值、最小值回填给调用者。/** * brief 求顺序表最大/最小值参数回填 * param s 顺序表 * param cmp 比较函数 * param max_val 输出最大值地址 * param min_val 输出最小值地址 */ void seqlist_get_max_min(seqlist_t *s, cmp_t cmp, void *max_val, void *min_val) { if (s-length 0) return; // 初始化最大/最小值为第一个元素 memcpy(max_val, s-data, s-elem_size); memcpy(min_val, s-data, s-elem_size); // 遍历所有元素 for (int i 1; i s-length; i) { char *elem (char *)s-data i * s-elem_size; // 更新最大值 if (cmp(elem, max_val) 0) { memcpy(max_val, elem, s-elem_size); } // 更新最小值 if (cmp(elem, min_val) 0) { memcpy(min_val, elem, s-elem_size); } } }六、测试代码可直接运行// int类型比较函数升序 int int_cmp(const void *a, const void *b) { return *(int *)a - *(int *)b; } int main() { // 1. 创建int类型顺序表容量10 seqlist_t *s seqlist_create(10, sizeof(int)); // 2. 插入元素 int arr[] {3, 1, 4, 2, 5}; for (int i 0; i 5; i) { seqlist_push_back(s, arr[i]); } printf(初始顺序表); seqlist_print_int(s); // 3. 查找查找元素2 int key 2; int *find_val seqlist_search(s, key, int_cmp); printf(查找元素2%s值%d\n, find_val ? 成功 : 失败, *find_val); // 4. 修改将2修改为9 int new_val 9; seqlist_modify(s, key, int_cmp, new_val); printf(修改后); seqlist_print_int(s); // 5. 插入排序 seqlist_insert_sort(s, int_cmp); printf(插入排序后); seqlist_print_int(s); // 6. 快速排序 seqlist_quick_sort(s, int_cmp); printf(快速排序后); seqlist_print_int(s); // 7. 折半查找有序表 find_val seqlist_binary_search(s, new_val, int_cmp); printf(折半查找9%s值%d\n, find_val ? 成功 : 失败, *find_val); // 8. 作业1倒序 seqlist_reverse(s); printf(倒序后); seqlist_print_int(s); // 9. 作业2获取最大/最小值 int max, min; seqlist_get_max_min(s, int_cmp, max, min); printf(最大值%d最小值%d\n, max, min); return 0; }七、运行结果初始顺序表3 1 4 2 5 查找元素2成功值2 修改后3 1 4 9 5 插入排序后1 3 4 5 9 快速排序后1 3 4 5 9 折半查找9成功值9 倒序后9 5 4 3 1 最大值9最小值1八、总结拓展数据结构的学习是程序开发进阶的核心基石而顺序表作为线性结构的入门模型其设计思想贯穿后续链表、栈、队列等多种数据结构。本文完整落地了顺序表必备的查找、修改、排序功能结合插入排序、快速排序、折半查找三大核心算法将理论原理与代码实践相结合同时搭配课后实操题目全方位巩固知识点。从实际应用角度分析不同算法拥有各自的适用场景。线性查找无需数据有序容错性强但数据量大时效率低下折半查找运算速度快查询效率极高却严格受制于有序条件插入排序逻辑简单代码易实现适合小批量数据处理快速排序凭借高效的分区递归机制成为大数据排序的主流选择。理解不同算法的优劣与适用场景是数据结构学习的关键不仅能提升代码编写能力更能培养算法思维。同时本文采用的泛型编程设计思路是 C 语言进阶开发的重要技巧。利用void*无类型指针结合内存拷贝函数打破数据类型限制让同一套代码适配多种数据结构极大提升了代码的复用性与拓展性这一设计思路在后续结构体、自定义数据类型开发中具有极高的参考价值。在日常学习与课程练习中我们不能只局限于代码抄写更要深入理解底层逻辑。比如顺序表依托连续内存带来的优势与弊端动态扩容的实现思路排序算法的时间复杂度变化查找算法的优化方向等。扎实掌握顺序表相关知识吃透排序与查找核心逻辑能够为后续复杂数据结构、算法竞赛、项目开发打下坚实基础。日常可以多结合实际案例练习尝试自主拓展功能比如添加顺序表删除、扩容、清空等方法循序渐进积累编程经验真正做到学以致用。