C语言增删查改实战:高效管理顺序表
创建一个顺序表,并从键盘接收数字输入,将输入的正整数按从小到大的顺序插入顺序表,并在输入负整数的时候将其绝对值数据删除。每次输入后,将顺序表的内容打印到屏幕上。
解析: 此题考查顺序表的基本思路,先要设计好顺序表的逻辑表达,再通过对顺序表的插入和删除操作,体会顺序存储中对于插入和删除的不便性。
参考代码:
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>// 顺序表结构体定义
typedef struct {int total_size; // 顺序表总容量int last; // 顺序表最末元素的下标int *data; // 顺序表的存储空间
} sqlist;// 初始化一个空的顺序表
sqlist *init_list(int total_size) {// 检查容量是否合法if (total_size <= 0) {fprintf(stderr, "初始化失败:容量必须为正数\n");return NULL;}sqlist *sq = (sqlist *)malloc(sizeof(sqlist));if (sq != NULL) {sq->total_size = total_size;sq->last = -1; // 用-1表征当前没有元素sq->data = (int *)malloc(sizeof(int) * total_size);// 内存分配失败处理if (sq->data == NULL) {fprintf(stderr, "内存分配失败\n");free(sq);return NULL;}} else {fprintf(stderr, "内存分配失败\n");}return sq;
}// 释放顺序表占用的内存
void destroy_list(sqlist *sq) {if (sq != NULL) {free(sq->data); // 先释放数据区free(sq); // 再释放结构体}
}// 判断顺序表是否为空
bool is_empty(sqlist *sq) {return (sq != NULL) && (sq->last == -1);
}// 判断顺序表是否已满
bool is_full(sqlist *sq) {return (sq != NULL) && (sq->last >= sq->total_size - 1);
}// 在递增的顺序表中找到x应插入的合适位置
int get_pos(sqlist *sq, int x) {if (sq == NULL || is_empty(sq)) {return 0; // 空表时返回0}int pos = 0;while ((pos <= sq->last) && (sq->data[pos] < x)) {pos++;}return pos;
}// 在顺序表sq中,定位元素x(内部使用,不打印信息)
int find_pos(sqlist *sq, int x) {if (sq == NULL || is_empty(sq)) {return -1;}for (int pos = 0; pos <= sq->last; pos++) {if (sq->data[pos] == x) {return pos;}}return -1;
}// 在顺序表sq中,定位元素x(外部使用,打印信息)
int locate(sqlist *sq, int x) {int pos = find_pos(sq, x);if (pos != -1) {printf("找到元素: data[%d] = %d\n", pos, x);} else {printf("未找到元素: %d\n", x);}return pos;
}// 将元素x插入顺序表sq中
bool insert(sqlist *sq, int x) {// 参数检查if (sq == NULL) {fprintf(stderr, "插入失败:顺序表未初始化\n");return false;}// 检查是否已满if (is_full(sq)) {fprintf(stderr, "插入失败:顺序表已满\n");return false;}// 找到插入位置int pos = get_pos(sq, x);// 移动元素for (int i = sq->last; i >= pos; i--) {sq->data[i + 1] = sq->data[i];}// 插入元素sq->data[pos] = x;sq->last++;printf("已插入元素: %d\n", x);return true;
}// 从顺序表中将元素x剔除
bool delete(sqlist *sq, int x) {// 参数检查if (sq == NULL) {fprintf(stderr, "删除失败:顺序表未初始化\n");return false;}// 检查是否为空if (is_empty(sq)) {fprintf(stderr, "删除失败:顺序表为空\n");return false;}// 查找元素位置(直接调用内部函数,避免重复遍历)int pos = find_pos(sq, x);if (pos == -1) {fprintf(stderr, "删除失败:元素 %d 不存在\n", x);return false;}// 移动元素覆盖要删除的位置for (; pos < sq->last; pos++) {sq->data[pos] = sq->data[pos + 1];}sq->last--;printf("已删除元素: %d\n", x);return true;
}// 展示顺序表元素
void show_data(sqlist *sq) {if (sq == NULL) {fprintf(stderr, "顺序表未初始化\n");return;}if (is_empty(sq)) {printf("顺序表为空\n");printf("=======================\n");return;}printf("当前顺序表元素:\n");for (int i = 0; i <= sq->last; i++) {printf("\tdata[%d] = %d\n", i, sq->data[i]);}printf("=======================\n");
}int main(int argc, char *argv[]) {// 初始化一条空的顺序表,容量为10sqlist *sq = init_list(10);if (sq == NULL) {return 1; // 初始化失败则退出}int num;printf("请输入操作(正数插入,负数删除,0退出):\n");while (1) {// 读取输入并验证printf("输入:");if (scanf("%d", &num) != 1) {fprintf(stderr, "输入错误,请输入整数\n");// 清除输入缓冲区while (getchar() != '\n');continue;}if (num > 0) {// 插入元素insert(sq, num);show_data(sq);} else if (num < 0) {// 删除元素(取绝对值)delete(sq, -num);show_data(sq);} else {// 退出程序printf("程序结束\n");break;}}// 释放内存destroy_list(sq);return 0;
}
优缺点总结
顺序存储中,由于逻辑关系是用物理位置来表达的,因此从上述示例代码可以很清楚看到,增删数据都非常困难,需要成片地移动数据。顺序表对数据节点的增删操作是很不友好的。
总结其特点如下:
优点:
- 不需要多余的信息来记录数据间的关系,存储密度高
- 所有数据顺序存储在一片连续的内存中,支持立即访问任意一个随机数据,比如上述顺序表中第i个节点是 s->data[i](访问快)
缺点:
- 插入、删除时需要保持数据的物理位置反映其逻辑关系,一般需要成片移动数据(增删慢)
- 当数据节点数量较多时,需要一整片较大的连续内存空间(需要提前确定大小)
- 当数据节点数量变化剧烈时,内存的释放和分配不灵活(需要整体释放)