栈和队列 - C语言实现

news2024/11/24 9:49:14

目录

栈的概念

栈的实现

队列

队列的概念

队列的实现


栈的概念

栈是一种后进先出 (LIFO - last in first out) 的数据结构,通常利用数组或链表实现。栈只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出原则。

它的基本操作包括 push (将元素压入栈中) 和 pop (将栈顶元素弹出)。栈还可以支持其它一些常见操作,例如:peek - 查看栈顶元素但不弹出、isEmpty - 判断栈是否为空、size - 获取栈中元素的个数。栈的抽象图例如下。

 

其实可以把栈形象的想象成一个瓶子(上面开口下面封闭的那种)。进栈的操作就相当于往瓶子中放东西,而出栈操作就相当于从瓶子中倒东西,而且不论是哪种操作,其只能在瓶口的那一端进行操作,没有办法操作瓶底或者其它位置。这样,这种先进后出的栈结构就很好理解了。

栈的实现

栈的实现有两种,一种是顺序表,一种是链表。这两种方式中顺序表要更方便一些,因为栈是一种只在一端进行操作的数据结构,不支持随机位置操作。而链表比顺序表的优势就是随机位置操作要更快,而且链表的维护成本要比顺序表大(因为顺序表就是数组嘛),所以用顺序表的尾插、尾删来实现栈的入栈和出栈是一种很便捷的方式。

栈的实现不难,但不同的人实现的方式也是各式各样的,所以就不再赘述了。如果需要,可以参考下面的代码。(小白本人是用顺序表实现的,但也可以用链表实现,这个很灵活)

// 初始化栈 
void StackInit(Stack* ps)
{
	assert(ps != NULL);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = -1;
}
//数组扩容
void Expansion(Stack* ps)
{
	ps->_a = (STDataType*)realloc(ps->_a, (ps->_capacity + 1) * 2 * sizeof(STDataType));
	assert(ps->_a != NULL);
	ps->_capacity = (ps->_capacity + 1) * 2;
}
// 入栈 
void StackPush(Stack* ps, STDataType data)
{
	assert(ps != NULL);
	//需要扩容的情况
	if (ps->_top + 1 == ps->_capacity) 
		Expansion(ps);
	ps->_a[++ps->_top] = data;
}
// 出栈 
void StackPop(Stack* ps)
{
	assert(ps != NULL);
	assert(!StackEmpty(ps));
	ps->_top--;
}
// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{
	assert(ps != NULL);
	assert(!StackEmpty(ps));
	return ps->_a[ps->_top];
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
	assert(ps); //这句断言其实可以不要,但为了严谨还是写上
	return ps->_top == -1;
}
// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{
	assert(ps != NULL);
	return ps->_top + 1;
}
// 销毁栈 
void StackDestroy(Stack* ps)
{
	assert(ps != NULL);
	free(ps->_a);
	ps->_capacity = 0;
	ps->_top = -1;
}

队列

队列的概念

队列是一种先进先出(FIFO - first in first out)的数据结构,类似于排队等候的场景。队列只允许在一端进行插入数据操作,在另一端进行删除数据操作

它有两个基本操作:入队和出队。入队操作将一个元素插入到队列的末尾,出队操作从队列的开头删除一个元素。因为元素是按照先进先出的顺序被处理,所以队列能够确保处理的顺序是正确的。

队列的实现

队列的实现也是有顺序表和链表两种实现方式,如果是单调队列用链表会方便些,而双向循环队列用顺序表又比较方便。具体原因是因为:单调队列要对头和尾进行操作,如果是顺序表的话需要移动元素(删除之后要移动元素),实现起来会很麻烦,而链表的话直接跳过一个指针就可以了。而双向循环链表的如果用链表实现会比较麻烦,首先用循环链表的话操作更麻烦一些,维护要比顺序表的成本大,其次用顺序表空间利用率和时间利用率都要比用链表优秀一些。而且可以实现不用一次性移动很多数据的出队列操作,所以相比之下,顺序表是个很不错的选择。

队列的实现也不难,不同的人实现的方式也是各式各样的,所以就不再赘述了。如果需要,可以参考下面的代码。

  • 单调队列
// 初始化队列 
void QueueInit(Queue* q)
{
	//带头节点的实现
	assert(q != NULL);
	q->_front = (QNode*)malloc(sizeof(QNode));
	assert(q->_front != NULL);
	q->_front->_next = NULL;
	q->_rear = q->_front;
}
// 队尾入队列 
void QueuePush(Queue* q, QDataType data)
{
	assert(q != NULL);
	q->_rear->_next = (QNode*)malloc(sizeof(QNode));
	QNode* newNode = q->_rear->_next;
	assert(newNode != NULL);
	newNode->_data = data;
	newNode->_next = NULL;
	q->_rear = newNode;
}
// 队头出队列 
void QueuePop(Queue* q)
{
	assert(q != NULL);
	assert(!QueueEmpty(q));
	QNode* tmp = q->_front->_next;
	q->_front->_next = tmp->_next; 
	if (tmp->_next == NULL)
		q->_rear = NULL;
	free(tmp);
}
// 获取队列头部元素 
QDataType QueueFront(Queue* q)
{
	assert(q != NULL);
	assert(!QueueEmpty(q));
	return q->_front->_next->_data;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* q)
{
	assert(q != NULL);
	assert(!QueueEmpty(q));
	return q->_rear->_data;
}
// 获取队列中有效元素个数 
int QueueSize(Queue* q)
{
	QNode* cur = q->_front->_next;
	int size = 0;
	while (cur)
	{
		cur = cur->_next;
		size++;
	}
	return size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q != NULL);
	return q->_front->_next == q->_rear && q->_rear == NULL;
}
// 销毁队列 
void QueueDestroy(Queue* q)
{
	assert(q != NULL);
	QNode* cur = q->_front->_next;
	while (cur)
	{
		QNode* next = cur->_next;
		free(cur);
		cur = next;
	}
	q->_front->_next = NULL;
	q->_rear = NULL;
}
  • 循环队列

循环队列参考一道leetcode的题

622. 设计循环队列 - 力扣(LeetCode)icon-default.png?t=N4HBhttps://leetcode.cn/problems/design-circular-queue/

typedef struct 
{
    int* queueArray;
    int front;
    int rear;
    int size;
    int capacity;
} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* cirQueue = (MyCircularQueue*) malloc (sizeof(MyCircularQueue));
    cirQueue->front = 0;
    cirQueue->rear = -1;
    cirQueue->size = 0;
    cirQueue->capacity = k;
    cirQueue->queueArray = (int*) calloc (k, sizeof(MyCircularQueue));
    return cirQueue;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return obj->size == 0;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    return obj->size == obj->capacity;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    int k = obj->capacity;
    if(!myCircularQueueIsFull(obj))
    {
        obj->rear = (obj->rear + 1) % k; 
        obj->queueArray[obj->rear] = value;
        obj->size++;
        return true;
    }
    return false;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    int k = obj->capacity;
    if(!myCircularQueueIsEmpty(obj))
    {
        obj->front = (obj->front + 1) % k; 
        obj->size--;
        return true;
    }
    return false;
}

int myCircularQueueFront(MyCircularQueue* obj) 
{
    if(!myCircularQueueIsEmpty(obj))
        return obj->queueArray[obj->front];
    return -1;
}

int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(!myCircularQueueIsEmpty(obj))
        return obj->queueArray[obj->rear];
    return -1;
}

void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->queueArray);
    free(obj);
}

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);
 
 * bool param_2 = myCircularQueueDeQueue(obj);
 
 * int param_3 = myCircularQueueFront(obj);
 
 * int param_4 = myCircularQueueRear(obj);
 
 * bool param_5 = myCircularQueueIsEmpty(obj);
 
 * bool param_6 = myCircularQueueIsFull(obj);
 
 * myCircularQueueFree(obj);
*/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/549584.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaWeb14 - 异步请求 - 02 - Ajax

1. 概述 1.1 官方文档 Ajax 在线文档:https://www.w3school.com.cn/js/js_ajax_intro.asp 1.2 Ajax 基本介绍 1.2.1 Ajax 是什么 AJAX 即"Asynchronous Javascript And XML"(异步 JavaScript 和 XML)Ajax 是一种浏览器异步发起请求(指定发哪些数据)&…

LeetCode刷题集(七)(LeetCode70.爬楼梯)

学习目标:拿下LeetCode70.爬楼梯 学习完本文章之后拿下LeetCode70题 题目实例: 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 样例1、输入:n 2 输出&#xff1…

Java学习路线(7)——面向对象基础

1、概念 对象: 是实际存在的具体实例。类: 是对象共同特征的描述 。 2、类的组成 成员变量 成员变量是类中的全局变量,它的作用域是class car的“{}”之内。 public class car{String carTypeName; //汽车类型名称double minPrice; //最低…

OpenCV在iOS端的集成及Mat和UIImage互相转化(附源码)

OpenCV是一个非常强大的图形处理框架,可以运行在Linux、Windows、Android和Mac OS操作系统上,在自动驾驶、智能家居、人脸识别、图片处理等方面提供了非常丰富且功能强大的api,在图片处理方便,基本上可以满足对图片处理的所有需求…

Fourier分析入门——第4章——频率域

目录 第 4 章 频率域(The Frequency Domain) 4.1 频谱分析(Spectral Analysis) 4.2 物理单位(Physics units) 4.3 笛卡尔坐标形式与极坐标形式对比 4.4 频谱分析的复数形式 4.5 复数值Fourier系数 4.6 复数值的和三角的Fourier系数之间的关系 4.7 2维或多维离散Fouri…

SpringBoot+Redis实现浏览量+1

当用户点击新闻查看详情后,数据库新闻的浏览量字段要加一,当在高并发场景下,很多人查看新闻详情直接操作数据库使浏览量字段加一对数据库压力过大,并且容易造成脏数据,这里结合redis实现浏览量加一。 业务逻辑&#x…

chatgpt赋能Python-pythonfoo

Pythonfoo: 优秀的Python库提高开发效率 Python是一门被广泛应用的动态编程语言,提供了各种各样的库来帮助开发人员完成不同类型的任务。在这些Python库中,Pythonfoo是一款旨在提高开发效率和代码简洁性的出色的Python库。 什么是Pythonfoo&#xff1f…

一种用于超低功耗无线传感器网络的消息传递算法(Matlab代码实现)

目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨‍💻4 Matlab代码 💥1 概述 无线传感器网络是由一定数量的无线传感器节点组成的网络系统,各节点可采集环境数据并通过无线通信进行数据传输,目前已广泛应用…

使用cubeMX生成USB HID设备

一,简介 本文主要介绍如何使用stm32F407开发板和cubeMX生成USB FS HID设备,供参考。 二,操作步骤 本次总共分以下几个步骤: 1,创建cubeMX工程,并配置HID,生成工程代码; 2&#xf…

光伏防逆流系统的介绍

安科瑞虞佳豪 5月17日,新疆和田地区洛浦县国家电投洛浦光伏电站,今年2月刚刚并网发电的200兆瓦光伏发电项目坐落于戈壁滩上,占地5500亩的368004块光伏面板在阳光照射下熠熠生辉,为和田地区经济社会发展持续提供着绿色能源。 洛浦…

HNU-操作系统OS-实验Lab6

OS_Lab6_Experimental report 湖南大学信息科学与工程学院 计科 210X wolf (学号 202108010XXX) 实验目的 理解操作系统的调度管理机制熟悉 ucore 的系统调度器框架,以及缺省的Round-Robin 调度算法基于调度器框架实现一个(Stride Scheduling)调度算法来替换缺省的调度算…

第12章_MySQL数据类型精讲

第12章_MySQL数据类型精讲 1. MySQL中的数据类型 类型类型举例整数类型TINYINT、SMALLINT、MEDIUMINT、INT(或INTEGER)、BIGINT浮点类型FLOAT、DOUBLE定点数类型DECIMAL位类型BIT日期时间类型YEAR、TIME、DATE、DATETIME、TIMESTAMP文本字符串类型CHAR、VARCHAR、TINYTEXT、TE…

chatgpt赋能Python-pythondeque函数

Python deque函数:高效地操作双向队列 介绍 在Python中,deque函数是一个非常重要的内置函数,用于创建双向队列。双向队列是一个序列容器,可以从两端添加或删除元素,这使得它成为许多常见问题的解决方案。deque函数是…

Vue--》深入理解 Vue 3 导航守卫,掌握前端路由的灵魂技能!

目录 vue3导航守卫讲解与使用 element-ui的安装与使用 配置路由和设置路径别名 设置登录页面并实现规则跳转 设置导航前置守卫 设置导航后置守卫 其他路由相关操作 vue3导航守卫讲解与使用 导航守卫是在 Vue Router 中提供的一种功能,它允许你在切换路由之前…

chatgpt赋能Python-pythonfirst

PythonFirst:Python编程新手的最佳起点 作为一门简洁而又强大的编程语言,Python在过去的几年中得到了越来越多的关注和应用。它广泛应用于数据分析、人工智能、Web开发、自动化等领域,成为了许多程序员的首选语言。如果你也是刚刚开始接触Py…

有哪些简单而知道的人少的excel操作技巧?

以下是 Excel 里鲜为人知而又简单、逆天的操作技巧: 1. 快速选中数据区域:双击数据区域左上角的方格即可快速选中整个数据区域。 2. 使用自动筛选快速查找和筛选数据:在 Excel 数据表中,使用自动筛选可以快速找到和筛选特定数据…

【Robot Dynamics Lecture Notes学习笔记之浮动基动力学】

Robot Dynamics Lecture Notes学习笔记之浮动基动力学 Contact ForcesSoft Contact Model Contact Forces from Constraints Constraint Consistent DynamicsContact Switches and Impact CollisionsImpulse TransferEnergy Loss 浮动基座系统的广义坐标分别由驱动关节坐标 q j…

小程序-基础加强(二):使用npm包

使用npm包 1.小程序对npm的支持与限制 目前,小程序中已经支持使用npm安装第三方包,从而来提高小程序的开发效率。但是,在小程序中使用npm包有如下3个限制: ①不支持依赖于Node.js内置库的包 ②不支持依赖于浏览器内置对象的包 …

Springboot +spring security,登录表单进阶配置及原理分析

一.简介 登录表单配置实操 二.创建项目 如何创建一个SpringSecurity项目,前面文章已经有说明了,这里就不重复写了。 三.配置默认用户信息 配置文件添加如下用户信息,代码如下: spring:security:user:name: lglbcpassword: 1…

HNU数据结构与算法分析-作业7-算法设计技术

1. (简答题, 10分)请简述分治法所能解决的问题一般具有哪些特征? 2. (简答题, 10分)请简述动态规划法的四个求解步骤。 3. (简答题, 10分)请比较动态规划法和贪心法,并写出两者的区别。 4. (简答题, 10分)请写出分支限界法和回溯法的区别。 二. 算法设计…