当前位置: 首页 > news >正文

数据结构——顺序表(c语言笔记)

1.线性结构的特点

在数据元素的非空有限集中,(1)存在惟一的一个被称做“第一个”的数据元素;(2)存在惟一的一个被称做“最后一个”的数据元素;(3)除第一个之外,集合中的每个数据元素均只有一个前驱;(4)除最后一个之外,集合中每个数据元素均只有一个后继。

2.顺序表定义

抽象数据类型(ADT)线性表定义:

ADT List {
数据对象:D={ aᵢ | aᵢ ∈ ElemSet,i=1,2,…,n,n≥0 }
数据关系:R1={ <aᵢ₋₁,aᵢ> |aᵢ₋₁,aᵢ ∈ D,i=2,…,n }
基本操作:插入、删除等(以下会详细说明)}ADT List

特点说明优点缺点适用场景
存储方式使用 一组连续的存储单元(数组) 按顺序存放元素结构简单,容易实现容量固定(静态),动态扩容需要复制数据,代价较大数据量较小或变化不大
存取方式支持 随机存取,可通过下标 O(1) 时间访问任意元素查找第 i 个元素效率高无法快速找到“值”所在位置(要顺序遍历 O(n))频繁需要按下标访问元素
存储密度每个元素都紧密存放,没有额外存储开销空间利用率高插入、删除需要移动大量元素,效率低元素变动不频繁的场景
容量限制静态:编译时确定大小;动态:需手动扩容(如 realloc)静态:内存管理简单静态:可能浪费或不够用;动态:扩容代价高元素数目可预测,或允许偶尔扩容
内存分布连续存储,局部性好,CPU 缓存命中率高访问速度快必须有足够大的连续内存空间内存较充足时

3.顺序表代码

main.cpp

int main()
{SeqList mylist;               // 定义顺序表变量(具体结构体在其它文件中定义,通常包含数组指针、容量、当前长度等)InitSeqList(&mylist);        // 初始化顺序表,分配内存或设置初始长度为0、容量等ELemType Item;               // 用于存放用户输入的数据元素。int pos;                     // 用于存放位置(索引)输入int select = 1;              // 菜单选项;初始化为1,使得第一次进入 while 循环。选择 0 则退出while (select)               // 主循环:当 select 为 0 时退出(因为 while(0) 为假){/* 打印菜单(多行输出) */printf("**************************************\n");printf("* [1]  push_back    [2]  push_front  *\n"); printf("* [3]  show_list    [4]  pop_back    *\n");printf("* [5]  pop_front    [6]  insert_pos  *\n");printf("* [7]  find         [8]  length      *\n"); printf("* [9]  delete_pos   [10]  delete_val *\n");printf("* [11] sort         [12]  resver     *\n"); printf("* [13] clear        [14*]  destroy   *\n"); /printf("* [0]  quit_system                   *\n");printf("**************************************\n");printf("请选择:>");scanf_s("%d", &select);   // 从 stdin 读取用户选择(注意:scanf_s 在 Visual C 环境下可用;最好检查返回值)if (select == 0)          // 若用户输入 0,退出循环(跳出主菜单)break;switch (select)          // 根据用户输入的选项进入不同分支{case 1:/* push_back:在表尾插入若干元素,直到输入 -1 结束 */printf("请输入要插入的数据(-1结束):>");/* 这里使用了逗号运算符:while (scanf_s("%d", &Item), Item != -1)等价于:scanf_s("%d", &Item); // 调用并丢弃返回值while (Item != -1) { ... }*/while (scanf_s("%d", &Item), Item != -1){push_back(&mylist, Item); // 将 Item 插入到顺序表末尾}break;case 2:/* push_front:在表头插入若干元素,直到输入 -1 结束 */printf("请输入要插入的数据(-1结束):>");while (scanf_s("%d", &Item), Item != -1){push_front(&mylist, Item); // 将 Item 插入到顺序表开头}break;case 3:/* show_list:显示顺序表中的所有元素 */show_list(&mylist);break;case 4:/* pop_back:删除表尾元素 */pop_back(&mylist);break;case 5:/* pop_front:删除表头元素 */pop_front(&mylist);break;case 6:/* insert_pos:在指定位置插入单个元素 */printf("请输入要插入的数据:>");scanf_s("%d", &Item);    // 读取要插入的数据printf("请输入要插入的位置:>");scanf_s("%d", &pos);    insert_pos(&mylist, pos, Item); // 插入函数应包含越界校验(例如 pos 范围)break;case 7:/* find:查找元素并返回下标(没找到可能返回 -1) */printf("请输入要查找的数据:>");scanf_s("%d", &Item);pos = find(&mylist, Item); // find 应返回索引或 -1if (pos == -1){printf("查找的数据%d在顺序表中不存在.\n", Item);}else{printf("查找的数据%d在顺序表中的%d下标位置.\n", Item, pos);}break;case 8:/* length:返回顺序表长度(元素个数) */printf("顺序表长度为:>%d\n", length(&mylist));break;case 9:/* delete_pos:按位置删除元素 */printf("请输入要删除数据的位置:>");scanf_s("%d", &pos);delete_pos(&mylist, pos);break;case 10:/* delete_val:按值删除 */printf("请输入要删除的数据:>");scanf_s("%d", &Item);delete_val(&mylist, Item);break;case 11:/* sort:对顺序表排序 */sort(&mylist);break;case 12:/* resver: 将顺序表反转 */resver(&mylist);break;case 13:/* clear:清空顺序表(置长度为0,但保留分配的内存) */clear(&mylist);break;// case 14://     // destroy(&mylist);//     // break;/* 注意:case 14 被注释掉了,不能让用户去摧毁顺序表,因此放在最后,当用户退出时候直接自动摧毁 */default:printf("输入的选项错误,请重新输入。\n");break;}}destroy(&mylist); // 程序退出前释放顺序表占用的资源(例如 free 动态数组),以防内存泄漏
}

Seqlist.h

#ifndef __SEQLIST_H__
#define __SEQLIST_H__#include<stdio.h>      // 用于 printf/scanf 等 I/O(在头文件中包含 stdio 通常没问题,但建议在实现文件里包含)
#include <malloc.h>    // 提供 malloc/realloc/free 声明
#include <cassert>     // C++ 里的断言头#define SEQLIST_INIT_SIZE 8   /* 初始分配容量(元素个数) */
#define INC_SIZE          3   /* 每次扩容时增加的容量(固定增量策略) */typedef int ELemType;        /* 元素类型定义 */typedef struct SeqList
{ELemType* base;   /* 指向动态数组的指针(由 InitSeqList 分配) */int       capacity;/* 当前数组的总容量(能装多少元素) */int       size;    /* 当前已存放的元素数量(有效元素个数) */
}SeqList;/* 扩容函数功能:确保顺序表至少还能容纳更多元素,通常在 push 时调用。返回:bool(true 表示扩容成功或已有足够空间;false 表示分配失败)
*/
bool Inc(SeqList* list);/* 初始化顺序表*/
void InitSeqList(SeqList* list);/* 在尾部插入元素 x*/
void push_back(SeqList* list, ELemType x);/* 在头部插入元素 x*/
void push_front(SeqList* list, ELemType x);/* 遍历并显示顺序表*/
void show_list(SeqList* list);/* 删除并丢弃尾部元素(pop)*/
void pop_back(SeqList* list);/* 删除并丢弃头部元素*/
void pop_front(SeqList* list);/* 在指定位置 pos 插入元素 x*/
void insert_pos(SeqList* list,int pos, ELemType x);/* 查找值为 key 的元素,返回下标(约定:找不到返回 -1)*/
int find(SeqList* list, ELemType key);/* 返回当前顺序表的长度(元素个数)*/
int length(SeqList* list);/* 按位置删除元素(pos 索引约定同 insert_pos)*/
void delete_pos(SeqList* list,int pos);/* 按值删除(常见有两种实现)*/
void delete_val(SeqList* list, ELemType key);/* 排序(sort)*/
void sort(SeqList* list);/* 反转顺序表(resver)*/
void resver(SeqList* list);/* 清空顺序表(clear)*/
void clear(SeqList* list);/* 销毁顺序表(destroy)- 功能:释放动态分配的内存(free(list->base)),并把 base 置 NULL,capacity 和 size 置 0(或其它哨值)。- 销毁后不要再使用该 SeqList 除非再次 Init。
*/
void destroy(SeqList* list);/* 合并两个顺序表(merge)*/
void merge(SeqList* lt, SeqList* la, SeqList* lb);#endif // __SEQLIST_H__

SeqList.cpp

bool Inc(SeqList* list)
{ELemType* newbase = (ELemType*)realloc(list->base, sizeof(ELemType) * (list->capacity + INC_SIZE));if (newbase == NULL){printf("增配空间失败,内存不足.\n");return false;}list->base = newbase;list->capacity += INC_SIZE;return true;
}
目的

为顺序表增加容量(当前实现为 capacity += INC_SIZE)。

参数

list:目标顺序表指针(要求非 NULL)。

前置条件
list != NULL。list->base 可为 NULL(realloc(NULL, size) 等同 malloc)。

后置条件
若返回 true:list->base 已更新到新地址(可能移动),list->capacity 增加;若 false:内存未改变,原 list->base 仍有效。

实现步骤(当前代码)

调用 realloc(list->base, sizeof(ELemType)*(list->capacity+INC_SIZE)),把返回值存在 newbase。

若 newbase == NULL,打印错误并返回 false(此时原内存未被释放)。

否则将 list->base = newbase 并 list->capacity += INC_SIZE,返回 true。

void InitSeqList(SeqList* list)
{list->base = (ELemType*)malloc(sizeof(ELemType) * SEQLIST_INIT_SIZE);assert(list->base != NULL);list->capacity = SEQLIST_INIT_SIZE;list->size = 0;
}
目的

初始化顺序表结构:分配初始内存、设置 capacity 和 size。

实现步骤

malloc 分配 SEQLIST_INIT_SIZE 个元素的内存。

assert(list->base != NULL)(当前实现)——断言失败会终止程序(仅在调试有效)。

设置 list->capacity = SEQLIST_INIT_SIZE,list->size = 0。

void push_back(SeqList* list, ELemType x)
{if (list->size >= list->capacity && !Inc(list)){printf("顺序表空间已满,不能尾部插入数据.\n");return;}list->base[list->size] = x;list->size++;
}
目的

在顺序表尾部插入元素 x。

实现步骤

检查 list->size >= list->capacity,若是则调用 Inc(list) 扩容;扩容失败时打印错误并返回。

将 list->base[list->size] = x,然后 list->size++。

void push_front(SeqList* list, ELemType x)
{if (list->size >= list->capacity && !Inc(list)){printf("顺序表空间已满,不能尾部插入数据.\n");return;}for (int i = list->size; i > 0; --i){list->base[i] = list->base[i - 1];}list->base[0] = x;list->size++;
}
目的

在顺序表头部插入元素(把现有元素整体右移一位)。

实现步骤

若空间不足,调用 Inc(list) 扩容。

从 i = size 到 1 逐项右移:base[i] = base[i-1]。

base[0] = x,size++。

void show_list(SeqList* list)
{for (int i = 0; i < list->size; ++i){printf("%d", list->base[i]);}printf("\n");
}
目的

把顺序表元素打印到标准输出,供调试或查看用。

实现步骤

遍历 i=0..size-1,printf("%d", base[i]),最后换行。

void pop_back(SeqList *list)
{if (list->size==0){printf("顺序表已空,不能尾部删除数据.\n");return;}list->size--;
}
目的

删除尾元素(不返回该元素)。

实现步骤

若 size == 0,打印“顺序表已空”并返回。

否则 size--(可选择清零 base[size])。

void pop_front(SeqList* list)
{if (list->size == 0){printf("顺序表已空,不能尾部删除数据.\n");return;}for (int i = 0; i < list->size-1; ++i){list->base[i] = list->base[i + 1];}list->size--;
}
目的

删除头元素并将后续元素前移一位。

实现步骤

检查 size 是否为 0。

将 base[1..size-1] 左移到 base[0..size-2],size--。

void insert_pos(SeqList* list, int pos, ELemType x)
{if (pos<0 || pos>list->size){printf("插入数据的位置非法,不能插入数据.\n");return;}for (int i = list->size; i > pos; --i){list->base[i] = list->base[i - 1];}list->base[pos] = x;list->size++;
}
目的

在下标 pos(约定为 0..size)处插入元素 x,插入后 x 的索引为 pos。

实现步骤

检查 pos 是否在合法范围 0 <= pos <= size,否则打印错误并返回。

从 i = size 到 pos+1 右移元素(base[i] = base[i-1])。

base[pos] = x,size++。

int find(SeqList* list, ELemType key)
{for (int i = 0; i < list->size; ++i){if (list->base[i] == key)return i;}return -1;
}
目的

线性查找第一个等于 key 的元素,下标从 0 开始,找不到返回 -1。

实现步骤

遍历 i=0..size-1,比较 base[i] == key,命中返回 i。

int length(SeqList* list)
{return list->size;
}
目的

返回当前元素个数 size。

实现步骤

直接 return list->size;

void delete_pos(SeqList* list, int pos)
{if (pos<0||pos>=list->size){printf("删除数据的位置非法,不能删除数据.\n"); return;}for (int i = pos; i < list->size - 1; ++i){list->base[i] = list->base[i + 1];}list->size--;
}

目的

删除下标为 pos 的元素并把后面的元素左移填补空位。

实现步骤

检查 pos 是否在 0 <= pos < size,否则打印错误并返回。

从 i = pos 到 size - 2 执行 base[i] = base[i+1]。

size--。

void delete_val(SeqList* list, ELemType key)
{int pos = find(list, key);if (pos==-1){printf("要删除的数据不存在.\n"); return;}delete_pos(list, pos);
}
目的

删除第一次出现的值为 key 的元素(如果存在)。

实现步骤

调用 find(list, key) 获取下标 pos。

若 pos == -1 打印不存在并返回。

调用 delete_pos(list, pos)。

void sort(SeqList* list)
{for (int i = 0; i < list->size - 1; ++i){for (int j = 0; j < list->size - i - 1; ++j){if (list->base[j] > list->base[j + 1]){ELemType tmp = list->base[j];list->base[j] = list->base[j + 1];list->base[j + 1] = tmp;}}}
}
目的

将顺序表元素排序(当前实现为冒泡排序,升序)。

实现步骤(当前代码)

嵌套两层循环(冒泡):交换相邻逆序对直到整体有序。

void resver(SeqList* list)
{if (list->size == 0 || list->size == 1)return;int low = 0;int high = list->size - 1;ELemType tmp;while (low < high){tmp = list->base[low];list->base[low] = list->base[high];list->base[high] = tmp;low++;high--;}
}
目的

就地反转顺序表元素顺序。

实现步骤

若 size <= 1 直接返回。

用 low=0, high=size-1,交换 base[low] 和 base[high],low++,high--,直到 low >= high。

void clear(SeqList* list)
{list->size = 0;
}
目的

逻辑上清空顺序表(把 size 置 0),但保留 base 内存以供复用。

实现步骤

list->size = 0;

void destroy(SeqList* list)
{free(list->base);list->base = NULL;list->capacity = 0;list->size = 0;
}
目的

销毁顺序表:释放 base 指向的动态内存并将结构复位。

实现步骤

free(list->base)(如果 base == NULL,free 是安全的)。

list->base = NULL; list->capacity = 0; list->size = 0;

4..双指针合并两个顺序表

main.cpp

#include"SeqList.h"int main()
{SeqList mylist, youlist, list;InitSeqList(&mylist);InitSeqList(&youlist);push_back(&mylist, 1);push_back(&mylist, 3);push_back(&mylist, 5);push_back(&mylist, 7);push_back(&mylist, 9);push_back(&youlist, 2);push_back(&youlist, 4);//push_back(&youlist,6);push_back(&youlist, 8);//push_back(&youlist,10);merge(&list, &mylist, &youlist);show_list(&list);
}
void merge(SeqList* lt, const SeqList* la, const SeqList* lb) {if (!lt || !la || !lb) return;int total = la->size + lb->size;if (lt->base) free(lt->base);         // 先释放已有内存,避免泄露lt->base = malloc(total * sizeof(ELemType));if (!lt->base) { perror("malloc"); lt->capacity = lt->size = 0; return; }lt->capacity = total;int ia=0, ib=0, ic=0;while (ia < la->size && ib < lb->size) {if (la->base[ia] <= lb->base[ib]) lt->base[ic++] = la->base[ia++];else lt->base[ic++] = lb->base[ib++];}while (ia < la->size) lt->base[ic++] = la->base[ia++];while (ib < lb->size) lt->base[ic++] = lb->base[ib++];lt->size = ic;
}
目的

将两个有序顺序表 la、lb 合并为 lt(有序合并),常用于归并排序的合并步骤或合并两个已排序数组。

实现步骤

设 lt->capacity = la->size + lb->size。

lt->base = malloc(...)(没有先释放原 lt->base)。

使用双指针 ia, ib 遍历 la 和 lb,把较小元素依次写入 lt->base[ic++]。

将剩余元素追加。

lt->size = la->size + lb->size;


文章转载自:

http://VT7chqDq.pfkpy.cn
http://q2kBjw1l.pfkpy.cn
http://GsfDkvWN.pfkpy.cn
http://plNmDGpi.pfkpy.cn
http://mbdvRMe5.pfkpy.cn
http://Goss4Ab9.pfkpy.cn
http://Da8vTaic.pfkpy.cn
http://6iHi7o0m.pfkpy.cn
http://VJFKSnPq.pfkpy.cn
http://9bSFAcnY.pfkpy.cn
http://BuVMax2T.pfkpy.cn
http://zMUI7MW2.pfkpy.cn
http://XvlIZbFL.pfkpy.cn
http://meAe2t3y.pfkpy.cn
http://biVQnk1Y.pfkpy.cn
http://UNTXZe9n.pfkpy.cn
http://g1HcjGtC.pfkpy.cn
http://lFYAnDe7.pfkpy.cn
http://K7cjpYBO.pfkpy.cn
http://YKVo788c.pfkpy.cn
http://7njLby5M.pfkpy.cn
http://OYcpiAwM.pfkpy.cn
http://EBzcqRhj.pfkpy.cn
http://TkmTahJ7.pfkpy.cn
http://o5RR2Xm7.pfkpy.cn
http://0hL4hMKh.pfkpy.cn
http://dpJgU1w5.pfkpy.cn
http://nzkFPvWb.pfkpy.cn
http://6n6uyNC9.pfkpy.cn
http://UspPg1ir.pfkpy.cn
http://www.dtcms.com/a/382095.html

相关文章:

  • Java 黑马程序员学习笔记(进阶篇6)
  • Day04 前缀和差分 1109. 航班预订统计 、304. 二维区域和检索 - 矩阵不可变
  • Java 类加载与对象内存分配机制详解
  • 【数据结构——图与邻接矩阵】
  • 再次深入学习深度学习|花书笔记1
  • 信息检索、推荐系统模型排序质量指标:AP@K和MAP@K
  • 详解 OpenCV 形态学操作:从基础到实战(腐蚀、膨胀、开运算、闭运算、梯度、顶帽与黑帽)
  • 《2025年AI产业发展十大趋势报告》五十五
  • 【面试题】RAG优化策略
  • 06 一些常用的概念及符号
  • Oracle事件10200与10201解析:数据库读一致性CR与Undo应用
  • 新手向:C语言、Java、Python 的选择与未来指南
  • 【人工智能通识专栏】第十四讲:语音交互
  • 3.RocketMQ核心源码解读
  • 微信小程序开发教程(十一)
  • [硬件电路-194]:NPN三极管、MOS-N, IGBT比较
  • 零基础学AI大模型之AI大模型常见概念
  • [Dify] 插件节点用法详解:如何将插件整合进工作流
  • 2025年数字公共治理专业重点学什么内容?(详细指南)
  • 如何在 Windows 系统中对硬盘 (SSD) 进行分区
  • 【深耕好论文】
  • Python快速入门专业版(二十八):函数参数进阶:默认参数与可变参数(*args/**kwargs)
  • 残差:从统计学到深度学习的核心概念
  • 华为体检转氨酶高能否入职
  • DeerFlow 实践:华为IPD流程的评审智能体设计
  • AI赋能金融研报自动化生成:智能体系统架构与实现
  • 一、Java 基础入门:从 0 到 1 认识 Java(详细笔记)
  • python123机器学习基础练习1
  • 微信小程序坐标位置使用整理(四)map组件
  • 贪心算法在联邦学习客户端选择问题中的应用