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

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)

基础概念 抽象数据类型&#xff08;ADT&#xff09;是一种数据类型&#xff0c;它定义了一组数据以及可以在这组数据上执行的操作&#xff0c;但隐藏了数据的具体存储方式和实现细节。在C语言中&#xff0c;抽象数据类型&#xff08;ADT&#xff09;是一种非常重要的概念&…...

提升性能测试效率与准确性:深入解析JMeter中的各类定时器

在软件性能测试领域&#xff0c;Apache JMeter是一款广泛使用的开源工具&#xff0c;它允许开发者模拟大量用户对应用程序进行并发访问&#xff0c;从而评估系统的性能和稳定性。在进行性能测试时&#xff0c;合理地设置请求之间的延迟时间对于模拟真实用户行为、避免服务器过载…...

施密特正交化与单位化的情形

在考研数学的线性代数部分&#xff0c;施密特正交化和单位化是两种不同的处理向量的方法&#xff0c;它们在特定的情况下被使用。以下是详细说明&#xff1a; 施密特正交化的应用场景 施密特正交化&#xff08;Gram-Schmidt Orthogonalization&#xff09;是一种从线性无关向…...

ROS机器视觉入门:从基础到人脸识别与目标检测

前言 从本文开始&#xff0c;我们将开始学习ROS机器视觉处理&#xff0c;刚开始先学习一部分外围的知识&#xff0c;为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本&#xff0c;系统采用Ubuntu20.04&#xff0c;ROS采用noetic。 颜…...

2024 APMCM亚太数学建模C题 - 宠物行业及相关产业的发展分析和策略(详细解题思路)

在当下&#xff0c; 日益发展的时代&#xff0c;宠物的数量应该均为稳步上升&#xff0c;在美国出现了下降的趋势&#xff0c; 中国 2019-2020 年也下降&#xff0c;这部分变化可能与疫情相关。需要对该部分进行必要的解释说明。 问题 1: 基于附件 1 中的数据及您的团队收集的…...

C#里怎么样访问文件时间

C#里怎么样访问文件时间 文件时间也是一个关键信息, 因为很多数据处理需要时间来判断数据的有效性,比如股票中的股价, 它是的权重,是随着时间递减的。 一般来说,超过5年以上的数据,都是可以删除掉了。 或者说超过三年的数据,就需要压缩保存了,这样可以省掉很多磁盘空…...

Cesium教程01_认识View

Cesium 地图视图组件 目录 一、引言二、功能说明三、代码实现 1. 模板结构2. 脚本逻辑3. 样式设计 四、总结 一、引言 在三维地球可视化中&#xff0c;Cesium 是一个强大的开源 JavaScript 库&#xff0c;它能够展示精美的地球和地图应用。本示例展示了如何使用 Vue 组件化…...

【SQL Server】华中农业大学空间数据库实验报告 实验八 存储过程

1.实验目的 通过实验课程与理论课的学习深入理解掌握的存储过程的原理、创建、修改、删除、基本的使用方法、主要用途&#xff0c;并且可以在练习的基础上&#xff0c;熟练使用存储过程来进行数据库的应用程序的设计&#xff1b;深入学习深刻理解与存储过程相关的T-SQL语句的编…...

ArcMap 处理栅格数据的分辨率功能操作

ArcMap 处理栅格数据的分辨率功能操作 一、统一多分辨率栅格数据 1、查看两个栅格数据的分辨率 1&#xff09;raster1 点击属性 2) raster2 2、统一像元大小 1&#xff09;点击环境 展示和填写 处理范围 栅格分析 点击确定 3、重采样 让raster1和..2保持一致&#xff0c;即…...

redis7.x源码分析:(4) ae事件处理器(一)

ae模块是redis实现的Reactor模型的封装。它的主要代码实现集中在 ae.c 中&#xff0c;另外还提供了平台相关的io多路复用的封装&#xff0c;它们都实现了一套相同的poll接口&#xff0c;就类似于C中提供了一个接口基类&#xff0c;由针对不同平台的派生类去实现。 // 创建平台…...

【React】React Router:深入理解前端路由的工作原理

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 React Router&#xff1a;深入理解前端路由的工作原理路由的演进历程传统多页面…...

51单片机-独立按键与数码管联动

独立键盘和矩阵键盘检测原理及实现 键盘的分类&#xff1a;编码键盘和非编码键盘 键盘上闭合键的识别由专用的硬件编码器实现&#xff0c;并产生键编码号或键值的称为编码键盘&#xff0c;如&#xff1a;计算机键盘。靠软件编程识别的称为非编码键盘&#xff1b;在单片机组成…...

visual studio 2005的MFC各种线程函数之间的调用关系

在 Visual Studio 2005 的 MFC 程序中的函数和消息机制涉及线程间通信、消息处理以及与窗口消息的交互。接下来我将详细分析以下每个函数的作用、如何使用它们以及它们之间的调用关系。 1. PostThreadMessage(m_iThOpID, MSG_OP_OVER, 0, (LPARAM)iLparm); 函数用途&#xff1…...

网页中调用系统的EXE文件,如打开QQ

遇到一个实际的问题&#xff0c;需要在网页中打开本地的某个工业软件。 通过点击exe文件就可以调用到程序。 比如双击qq的exe就可以启动qq的程序。 那么问题就变成了如何加载exe程序呢&#xff1f; 可以通过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自然语言处理&#xff1a;4.3推荐系统&#xff1a…...

Halo 正式开源: 使用可穿戴设备进行开源健康追踪

在飞速发展的可穿戴技术领域&#xff0c;我们正处于一个十字路口——市场上充斥着各式时尚、功能丰富的设备&#xff0c;声称能够彻底改变我们对健康和健身的方式。 然而&#xff0c;在这些光鲜的外观和营销宣传背后&#xff0c;隐藏着一个令人担忧的现实&#xff1a;大多数这些…...

summernote富文本批量上传音频,视频等附件

普通项目,HTML的summernote富文本批量上传音频,视频等附件(其他附件同理) JS和CSS的引入 <head><th:block th:include"include :: summernote-css" /> </head> <body><th:block th:include"include :: summernote-js" /> …...

IDEA如何设置编码格式,字符编码,全局编码和项目编码格式

前言 大家好&#xff0c;我是小徐啊。我们在开发Java项目&#xff08;Springboot&#xff09;的时候&#xff0c;一般都是会设置好对应的编码格式的。如果设置的不恰当&#xff0c;容易造成乱码的问题&#xff0c;这是要避免的。今天&#xff0c;小徐就来介绍下我们如何在IDEA…...

【计算机网络实验】之静态路由配置

【计算机网络实验】之静态路由配置 实验题目实验目的实验任务实验设备实验环境实验步骤路由器配置设置静态路由测试路由器之间的连通性配置主机PC的IP测试 实验题目 静态路由协议的配置 实验目的 熟悉路由器工作原理和机制&#xff1b;巩固静态路由理论&#xff1b;设计简单…...

十五届蓝桥杯赛题-c/c++ 大学b组

握手问题 很简单&#xff0c;相互牵手即可&#xff0c;但是要注意&#xff0c;第一个人只能与其他49个人牵手&#xff0c;所以开头是加上49 #include <iostream> using namespace std; int main() {int cnt0;for(int i49;i>7;i--){cnti;//cout<<i<<&quo…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)

一、数据处理与分析实战 &#xff08;一&#xff09;实时滤波与参数调整 基础滤波操作 60Hz 工频滤波&#xff1a;勾选界面右侧 “60Hz” 复选框&#xff0c;可有效抑制电网干扰&#xff08;适用于北美地区&#xff0c;欧洲用户可调整为 50Hz&#xff09;。 平滑处理&…...

在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能

下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能&#xff0c;包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理

在城市的某个角落&#xff0c;一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延&#xff0c;滚滚浓烟弥漫开来&#xff0c;周围群众的生命财产安全受到严重威胁。就在这千钧一发之际&#xff0c;消防救援队伍迅速行动&#xff0c;而豪越科技消防一体化安全管控平台构建的消防“…...