C语言基础学习:抽象数据类型(ADT)
基础概念
抽象数据类型(ADT)是一种数据类型,它定义了一组数据以及可以在这组数据上执行的操作,但隐藏了数据的具体存储方式和实现细节。在C语言中,抽象数据类型(ADT)是一种非常重要的概念,它允许程序员定义和操作自定义的数据结构,而无需关心其底层实现细节。通过ADT可以创建出既安全又高效的数据管理方案,为复杂问题的解决提供有力支持。
使用ADT的优点:
封装性:隐藏数据表示和实现细节,只暴露操作接口,提高了代码的安全性和可维护性。
复用性:ADT可以作为独立的模块进行开发和测试,方便在不同项目中复用。
抽象性:通过ADT,我们可以更关注于数据操作的逻辑,而不是数据的具体存储方式。
ADT由以下两部分组成:
数据表示:定义数据的存储结构,通常使用结构体来封装数据成员。
操作接口:定义可以在数据上执行的操作,如创建、销毁、访问、修改等,这些操作通过函数来实现。
基于链表的ADT实现数据封装
这里使用基于链表的ADT实现数据封装来进行展示,数据封装是一种把数据和操作数据的函数捆绑在一起的机制,在C语言中,可以通过结构体和函数来实现数据封装,结构体用于存储数据,而函数则用于操作这些数据。
操作步骤如下:
1.定义链表节点的结构体:包含数据域和指针域。
2.定义链表ADT的操作函数:如初始化链表、在链表末尾添加元素、清空链表等。
3.实现这些操作函数:通过函数来操作链表,隐藏链表的具体实现细节。
#include <stdio.h>
#include <stdlib.h>// 定义链表节点的结构体
typedef struct ListNode {int data;struct ListNode* next;
} ListNode;// 定义链表ADT的操作函数
typedef struct {ListNode* head;
} List;// 初始化链表
void initList(List* list) {list->head = NULL;
}// 在链表末尾添加一个元素
void appendToList(List* list, int value) {ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));newNode->data = value;newNode->next = NULL;if (list->head == NULL) {list->head = newNode;} else {ListNode* current = list->head;while (current->next != NULL) {current = current->next;}current->next = newNode;}
}// 清空链表
void clearList(List* list) {ListNode* current = list->head;while (current != NULL) {ListNode* next = current->next;free(current);current = next;}list->head = NULL;
}// 打印链表
void printList(List* list) {ListNode* current = list->head;while (current != NULL) {printf("%d -> ", current->data);current = current->next;}printf("NULL\n");
}int main() {List myList;initList(&myList);appendToList(&myList, 1);appendToList(&myList, 2);appendToList(&myList, 3);printList(&myList);clearList(&myList);printList(&myList);return 0;
}
运行后在终端显示以下内容

接口实现
接口是ADT与用户之间的桥梁。它规定了用户可以如何访问和操作ADT中的数据,而不涉及数据的内部表示。在C语言中,接口通常通过头文件(.h文件)来定义,其中包含了数据类型的声明和函数原型的声明。实现接口意味着为ADT定义具体的操作。这些操作在C语言中通过函数来实现。函数的定义通常放在源文件(.c文件)中,并且这些函数会操作ADT的内部数据。
这里通过定义一个栈的ADT来实现数据封装,并通过接口来访问栈的操作。
步骤如下:
定义栈的ADT:在头文件中声明栈的结构体和函数原型。
实现栈的操作:在源文件中定义栈的操作函数。
使用栈的ADT:在主程序中通过接口来操作栈。
先定义一个栈操作的头文件
// stack.h
#ifndef STACK_H
#define STACK_H// 定义栈的ADT
typedef struct {int* data;int top;int capacity;
} Stack;// 栈的操作函数原型
void initStack(Stack* stack);
int isStackEmpty(Stack* stack);
void push(Stack* stack, int value);
int pop(Stack* stack);
int peek(Stack* stack);
void clearStack(Stack* stack);#endif
在编写一个用来实现栈操作的C语言文件
// stack.h
#include <stdio.h>
#include <stdlib.h>
#include "stack.h"// 栈的内部表示
struct Stack {int* data;int top;int capacity;
};// 初始化栈
void initStack(Stack* stack) {stack->data = (int*)malloc(100 * sizeof(int));stack->top = -1;stack->capacity = 100;
}// 检查栈是否为空
int isStackEmpty(Stack* stack) {return stack->top == -1;
}// 压栈
void push(Stack* stack, int value) {if (stack->top < stack->capacity - 1) {stack->data[++stack->top] = value;} else {// 如果栈满,输出提示printf("被填满了\n");}
}// 出栈
int pop(Stack* stack) {if (!isStackEmpty(stack)) {return stack->data[stack->top--];} else {// 栈空,处理栈下溢,返回特殊值表示错误return -1; // -1不是栈中的有效值}
}// 查看栈顶元素
int peek(Stack* stack) {if (!isStackEmpty(stack)) {return stack->data[stack->top];} else {return -1; }
}// 清空栈
void clearStack(Stack* stack) {free(stack->data);stack->top = -1;stack->capacity = 0;
}
最后编写出主文件
// main.c
#include <stdio.h>
#include "stack.h"int main() {Stack myStack;initStack(&myStack);push(&myStack, 10);push(&myStack, 20);push(&myStack, 30);printf("栈顶部的元素是: %d\n", peek(&myStack));while (!isStackEmpty(&myStack)) {printf("弹出的元素是: %d\n", pop(&myStack));}clearStack(&myStack);return 0;
}
代码运行后在终端输出以下内容:

队列ADT
队列是一种先进先出(FIFO)的线性表,它只允许在表的一端进行插入(队尾),在另一端进行删除(队头),任务按照它们被添加到队列中的顺序被调度执行。
队列ADT的操作步骤如下:
入队(EnQueue):
将一个元素添加到队列的末尾(队尾)。
这是队列的核心操作之一,用于在队列中插入新元素。
出队(DeQueue):
从队列的开头(队头)移除一个元素,并返回该元素的值。
出队操作遵循先进先出(FIFO)的原则,即最先入队的元素最先被移除。
判空(QueueEmpty):
检查队列是否为空。
这是一个常用的辅助操作,用于确定队列中是否还有元素。
获取队头元素(GetHead 或 Front):
返回队列开头元素的值,但不移除该元素。
这允许用户查看队列的当前状态,而不改变队列的内容。
队列长度(QueueLength):
返回队列中元素的数量。
这个操作对于需要知道队列大小的情况非常有用。
清空队列(ClearQueue):
移除队列中的所有元素,使队列变为空。
这在需要重置队列或释放内存时很有用。
销毁队列(DestroyQueue):
释放队列所占用的所有资源,包括内存。
这是在队列不再需要时进行的清理操作。
以下代码运行后程序会提示用户输入队列元素的数量,然后输入具体的元素。接着程序会遍历队列、出队一个元素、获取队头元素、显示队列长度,并判断队列是否为空。最后销毁队列。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>typedef int QElemType;typedef struct QNode {QElemType data;struct QNode* next;
} QNode, *QueuePtr;typedef struct {QueuePtr front;QueuePtr rear;
} LinkQueue;// 初始化队列
bool InitQueue(LinkQueue* q) {if (!q) {printf("队列不存在!\n");return false;}q->front = q->rear = (QueuePtr)malloc(sizeof(QNode));if (!q->front) {printf("内存分配失败!\n");return false;}q->front->next = NULL;return true;
}// 销毁队列
bool DestroyQueue(LinkQueue* queue) {if (!queue) {printf("队列不存在!\n");return false;}while (queue->front != NULL) {queue->rear = queue->front->next;free(queue->front);queue->front = queue->rear;}return true;
}// 清空队列
bool ClearQueue(LinkQueue* q) {if (!q) {printf("队列不存在!\n");return false;}QueuePtr p = q->front->next, tmp;while (p) {tmp = p->next;free(p);p = tmp;}q->front = q->rear;return true;
}// 判断队列是否为空
bool QueueEmpty(LinkQueue q) {if (!&q) {printf("空队列!\n");return false;}if (q.front == q.rear) {return true;}return false;
}// 插入元素e为q的新队尾元素
bool EnQueue(LinkQueue* q, QElemType e) {QueuePtr p = (QueuePtr)malloc(sizeof(QNode));if (!p) {printf("内存分配失败!\n");return false;}p->data = e;p->next = NULL;q->rear->next = p;q->rear = p;return true;
}// 出队
bool DeQueue(LinkQueue* q, QElemType* e) {if (!q) {printf("队列不存在!\n");return false;}if (QueueEmpty(*q)) {printf("空队列!\n");return false;}QueuePtr p = q->front->next;*e = p->data;q->front->next = p->next;if (q->front == q->rear) {q->rear = q->front;}free(p);return true;
}// 获取队首元素
bool GetHead(LinkQueue q, QElemType* e) {if (!(&q)) {printf("队列不存在!\n");return false;}if (QueueEmpty(q)) {printf("空队列!\n");return false;}*e = q.front->next->data;return true;
}// 队列长度
int QueueLength(LinkQueue q) {if (!&q) {printf("队列不存在!\n");return 0;}int len = 0;QueuePtr p = q.front->next;while (p) {len++;p = p->next;}return len;
}// 遍历队列
void QueueTraverse(LinkQueue q) {if (!(&q)) {printf("队列不存在!\n");return;}if (QueueEmpty(q)) {printf("队列为空!\n");return;}QueuePtr p = q.front->next;while (p) {printf("%d ", p->data);p = p->next;}printf("\n");
}int main() {LinkQueue que;QElemType data;int n;if (!InitQueue(&que)) {return 1;}printf("输入队列元素数量:\n");scanf("%d", &n);printf("输入队列中元素:\n");while (n--) {scanf("%d", &data);EnQueue(&que, data);}printf("遍历队列:\n");QueueTraverse(que);if (DeQueue(&que, &data)) {printf("出队元素: %d\n", data);}if (GetHead(que, &data)) {printf("队头元素: %d\n", data);}printf("队列长度: %d\n", QueueLength(que));if (QueueEmpty(que)) {printf("队列为空!\n");} else {printf("队列不为空!\n");}DestroyQueue(&que);return 0;
}
代码运行后显示:

相关文章:
C语言基础学习:抽象数据类型(ADT)
基础概念 抽象数据类型(ADT)是一种数据类型,它定义了一组数据以及可以在这组数据上执行的操作,但隐藏了数据的具体存储方式和实现细节。在C语言中,抽象数据类型(ADT)是一种非常重要的概念&…...
提升性能测试效率与准确性:深入解析JMeter中的各类定时器
在软件性能测试领域,Apache JMeter是一款广泛使用的开源工具,它允许开发者模拟大量用户对应用程序进行并发访问,从而评估系统的性能和稳定性。在进行性能测试时,合理地设置请求之间的延迟时间对于模拟真实用户行为、避免服务器过载…...
施密特正交化与单位化的情形
在考研数学的线性代数部分,施密特正交化和单位化是两种不同的处理向量的方法,它们在特定的情况下被使用。以下是详细说明: 施密特正交化的应用场景 施密特正交化(Gram-Schmidt Orthogonalization)是一种从线性无关向…...
ROS机器视觉入门:从基础到人脸识别与目标检测
前言 从本文开始,我们将开始学习ROS机器视觉处理,刚开始先学习一部分外围的知识,为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本,系统采用Ubuntu20.04,ROS采用noetic。 颜…...
2024 APMCM亚太数学建模C题 - 宠物行业及相关产业的发展分析和策略(详细解题思路)
在当下, 日益发展的时代,宠物的数量应该均为稳步上升,在美国出现了下降的趋势, 中国 2019-2020 年也下降,这部分变化可能与疫情相关。需要对该部分进行必要的解释说明。 问题 1: 基于附件 1 中的数据及您的团队收集的…...
C#里怎么样访问文件时间
C#里怎么样访问文件时间 文件时间也是一个关键信息, 因为很多数据处理需要时间来判断数据的有效性,比如股票中的股价, 它是的权重,是随着时间递减的。 一般来说,超过5年以上的数据,都是可以删除掉了。 或者说超过三年的数据,就需要压缩保存了,这样可以省掉很多磁盘空…...
Cesium教程01_认识View
Cesium 地图视图组件 目录 一、引言二、功能说明三、代码实现 1. 模板结构2. 脚本逻辑3. 样式设计 四、总结 一、引言 在三维地球可视化中,Cesium 是一个强大的开源 JavaScript 库,它能够展示精美的地球和地图应用。本示例展示了如何使用 Vue 组件化…...
【SQL Server】华中农业大学空间数据库实验报告 实验八 存储过程
1.实验目的 通过实验课程与理论课的学习深入理解掌握的存储过程的原理、创建、修改、删除、基本的使用方法、主要用途,并且可以在练习的基础上,熟练使用存储过程来进行数据库的应用程序的设计;深入学习深刻理解与存储过程相关的T-SQL语句的编…...
ArcMap 处理栅格数据的分辨率功能操作
ArcMap 处理栅格数据的分辨率功能操作 一、统一多分辨率栅格数据 1、查看两个栅格数据的分辨率 1)raster1 点击属性 2) raster2 2、统一像元大小 1)点击环境 展示和填写 处理范围 栅格分析 点击确定 3、重采样 让raster1和..2保持一致,即…...
redis7.x源码分析:(4) ae事件处理器(一)
ae模块是redis实现的Reactor模型的封装。它的主要代码实现集中在 ae.c 中,另外还提供了平台相关的io多路复用的封装,它们都实现了一套相同的poll接口,就类似于C中提供了一个接口基类,由针对不同平台的派生类去实现。 // 创建平台…...
【React】React Router:深入理解前端路由的工作原理
🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 💫个人格言: "如无必要,勿增实体" 文章目录 React Router:深入理解前端路由的工作原理路由的演进历程传统多页面…...
51单片机-独立按键与数码管联动
独立键盘和矩阵键盘检测原理及实现 键盘的分类:编码键盘和非编码键盘 键盘上闭合键的识别由专用的硬件编码器实现,并产生键编码号或键值的称为编码键盘,如:计算机键盘。靠软件编程识别的称为非编码键盘;在单片机组成…...
visual studio 2005的MFC各种线程函数之间的调用关系
在 Visual Studio 2005 的 MFC 程序中的函数和消息机制涉及线程间通信、消息处理以及与窗口消息的交互。接下来我将详细分析以下每个函数的作用、如何使用它们以及它们之间的调用关系。 1. PostThreadMessage(m_iThOpID, MSG_OP_OVER, 0, (LPARAM)iLparm); 函数用途࿱…...
网页中调用系统的EXE文件,如打开QQ
遇到一个实际的问题,需要在网页中打开本地的某个工业软件。 通过点击exe文件就可以调用到程序。 比如双击qq的exe就可以启动qq的程序。 那么问题就变成了如何加载exe程序呢? 可以通过Java的 Process process Runtime.getRuntime().exec(command);通过…...
【单点知识】基于PyTorch讲解自动编码器(Autoencoder)
文章目录 0. 前言1. 自动编码器的基本概念1.1 定义1.2 目标1.3 结构 2. PyTorch实现自动编码器2.1 导入必要的库2.2 定义自动编码器模型2.3 加载数据2.4 训练自动编码器 3. 自动编码器的意义4. 自动编码器的应用4.1 图像处理4.2自然语言处理:4.3推荐系统:…...
Halo 正式开源: 使用可穿戴设备进行开源健康追踪
在飞速发展的可穿戴技术领域,我们正处于一个十字路口——市场上充斥着各式时尚、功能丰富的设备,声称能够彻底改变我们对健康和健身的方式。 然而,在这些光鲜的外观和营销宣传背后,隐藏着一个令人担忧的现实:大多数这些…...
summernote富文本批量上传音频,视频等附件
普通项目,HTML的summernote富文本批量上传音频,视频等附件(其他附件同理) JS和CSS的引入 <head><th:block th:include"include :: summernote-css" /> </head> <body><th:block th:include"include :: summernote-js" /> …...
IDEA如何设置编码格式,字符编码,全局编码和项目编码格式
前言 大家好,我是小徐啊。我们在开发Java项目(Springboot)的时候,一般都是会设置好对应的编码格式的。如果设置的不恰当,容易造成乱码的问题,这是要避免的。今天,小徐就来介绍下我们如何在IDEA…...
【计算机网络实验】之静态路由配置
【计算机网络实验】之静态路由配置 实验题目实验目的实验任务实验设备实验环境实验步骤路由器配置设置静态路由测试路由器之间的连通性配置主机PC的IP测试 实验题目 静态路由协议的配置 实验目的 熟悉路由器工作原理和机制;巩固静态路由理论;设计简单…...
十五届蓝桥杯赛题-c/c++ 大学b组
握手问题 很简单,相互牵手即可,但是要注意,第一个人只能与其他49个人牵手,所以开头是加上49 #include <iostream> using namespace std; int main() {int cnt0;for(int i49;i>7;i--){cnti;//cout<<i<<&quo…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
