数据结构:详解【栈和队列】的实现

news2024/11/13 13:00:30

目录

  • 1. 栈
    • 1.1 栈的概念及结构
    • 1.2 栈的实现
    • 1.3 栈的功能
    • 1.4 栈的功能的实现
    • 1.5 完整代码
  • 2. 队列
    • 2.1 队列的概念及结构
    • 2.2 队列的实现
    • 2.3 队列的功能
    • 2.4 队列的功能的实现
    • 2.5 完整代码

1. 栈

1.1 栈的概念及结构

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈。入数据在栈顶
出栈:栈的删除操作叫做出栈。出数据也在栈顶

在这里插入图片描述
在这里插入图片描述

1.2 栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾插和头删的实际复杂度为O(1),是非常合适的。

在这里插入图片描述

1.3 栈的功能

  • 初始化栈
  • 销毁栈
  • 入栈
  • 出栈
  • 获取栈顶元素
  • 获取栈内有效元素的个数
  • 判断栈内是否为空,如果为空返回非0结果,不为空返回0

1.4 栈的功能的实现

(1)定义一个动态增长的栈

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	STDataType top;//定义栈顶
	size_t capacity;//栈的容量
}ST;

(2)初始化栈

void StackInit(ST* ps)
{
	//初始化空间
	 ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL)
	{
		printf("malloc fail!\n");
		return;
	}

	ps->top = 0;
	ps->capacity = 4;//初始化4个空间
}

(3)销毁栈

void StackDestory(ST* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

(4)入栈(相当于顺序表的尾插)

void StackPush(ST* ps, STDataType x)
{
	//插入数据之前判断是否增容
	if (ps->top == ps->capacity)
	{
		STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * sizeof(STDataType) * 2);
		if (tmp == NULL)
		{
			printf("realloc fail!\n");
			return;
		}
		else
		{
			ps->a = tmp;
			ps->capacity *= 2;
		}
	}

	ps->a[ps->top] = x;
	ps->top++;
}

(5)出栈(相当于顺序表的头删)

void StackPop(ST* ps)
{
	assert(ps);//断言,栈内为空则终止程序

	ps->top--;
}

(6)获取栈顶元素

STDataType StackTop(ST* ps)
{
	assert(ps);//断言,栈内为空则终止程序
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}

(7)获取栈内有效元素的个数

int StackSize(ST* ps)
{
	assert(ps);

	return ps->top;
}

(8)判断栈内是否为空,如果为空返回非0结果,不为空返回0

bool StackEmpty(ST* ps)
{
	assert(ps);

	return ps->top == 0;
}

1.5 完整代码

Stack.h

#define _CRT_SECURE_NO_WARNINGS 

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	STDataType top;//定义栈顶
	size_t capacity;//栈的容量
}ST;

//初始化栈
void StackInit(ST* ps);

//销毁栈
void StackDestory(ST* ps);

//从栈顶插入数据
void StackPush(ST* ps, STDataType x);

//从栈顶删除数据
void StackPop(ST* ps);

//获取栈顶元素
STDataType StackTop(ST* ps);

//获取栈内有效元素个数
int StackSize(ST* ps);

//判断栈内是否为空,如果为空返回非0结果,不为空返回0
bool StackEmpty(ST* ps);

Stack.c

#define _CRT_SECURE_NO_WARNINGS 

#include "Stack.h"

void StackInit(ST* ps)
{
	//初始化空间
	 ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL)
	{
		printf("malloc fail!\n");
		return;
	}

	ps->top = 0;
	ps->capacity = 4;//初始化4个空间
}

void StackDestory(ST* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->capacity = 0;
	ps->top = 0;
}

void StackPush(ST* ps, STDataType x)
{
	//插入数据之前判断是否增容
	if (ps->top == ps->capacity)
	{
		STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * sizeof(STDataType) * 2);
		if (tmp == NULL)
		{
			printf("realloc fail!\n");
			return;
		}
		else
		{
			ps->a = tmp;
			ps->capacity *= 2;
		}
	}

	ps->a[ps->top] = x;
	ps->top++;
}


void StackPop(ST* ps)
{
	assert(ps);//断言,栈内为空则终止程序

	ps->top--;
}

STDataType StackTop(ST* ps)
{
	assert(ps);//断言,栈内为空则终止程序
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}

int StackSize(ST* ps)
{
	assert(ps);

	return ps->top;
}

bool StackEmpty(ST* ps)
{
	assert(ps);

	return ps->top == 0;
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 

#include "Stack.h"

void StackTest()
{
	ST st;
	StackInit(&st);
	StackPush(&st, 1);
	StackPush(&st, 2);
	StackPush(&st, 3);
	StackPush(&st, 4);

	//打印栈内的数据,由于不能破坏栈的特性,所以不能遍历
	while (!StackEmpty(&st))
	{
		printf("%d ", StackTop(&st));
		StackPop(&st);
	}

	StackDestory(&st);
}

int main()
{
	StackTest();

	return 0;
}

2. 队列

2.1 队列的概念及结构

队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

2.2 队列的实现

队列也可以数组和链表的结构实现,使用单链表的结构实现更优一些。
因为如果使用数组的结构,出队列在数组头上出数据,这时需要挪动数据,时间复杂度为O(n),效率会比较低。
而单链表的尾插和头删的时间复杂度为O(1),十分合适。
在这里插入图片描述

2.3 队列的功能

  • 初始化队列
  • 销毁队列
  • 入队列
  • 出队列
  • 获取队列头部元素
  • 获取队列尾部元素
  • 获取队列中有效元素的个数
  • 判断队列是否为空,为空返回非0,不为空返回0

2.4 队列的功能的实现

(1)定义一个队列

typedef int QTDataType;

typedef struct QNode
{
	struct QNode* next;
	QTDataType data;
}QNode;

typedef struct Queue
{
	struct QNode* tail;
	struct QNode* head;
}Queue;

(2)初始化队列

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

(3)销毁队列

void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		cur = cur->next;
		free(cur);
	}
	pq->head = pq->tail = NULL;
}

(4)入队列(相当于单链表的尾插)

void QueuePush(Queue* pq, QTDataType x)
{
	assert(pq);

	QNode* newnode = (QTDataType*)malloc(sizeof(QTDataType));
	if (newnode == NULL)
	{
		printf("malloc fail!\n");
		return;
	}

	newnode->data = x;
	newnode->next = NULL;

	//第一个结点
	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	//多个节点
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

}

(5)出队列(相当于单链表的头删)

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	//只有一个节点
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		//保存下一个节点的地址
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

(6)获取队列头部元素

QTDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	return pq->head->data;
}

(7)获取队列尾部元素

QTDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	return pq->tail->data;
}

(8)获取队列中有效元素的个数

int QueueSize(Queue* pq)
{
	assert(pq);
	int size = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		size++;
		cur = cur->next;
	}

	return size;
}

(9)判断队列是否为空,为空返回非0,不为空返回0

int QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->head == NULL;
}

2.5 完整代码

Queue.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int QTDataType;

typedef struct QNode
{
	struct QNode* next;
	QTDataType data;
}QNode;

typedef struct Queue
{
	struct QNode* tail;
	struct QNode* head;
}Queue;

//初始化队列
void QueueInit(Queue* pq);

//销毁队列
void QueueDestory(Queue* pq);

//队尾入队列
void QueuePush(Queue* pq, QTDataType x);

//队头出队列
void QueuePop(Queue* pq);

//获取队列头部元素
QTDataType QueueFront(Queue* pq);

//获取队列尾部元素
QTDataType QueueBack(Queue* pq);

//判断队列是否为空,为空返回非0,不为空返回0
int QueueEmpty(Queue* pq);

//获取队列中有效元素的个数
int QueueSize(Queue* pq);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 

#include "Queue.h"

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		cur = cur->next;
		free(cur);
	}
	pq->head = pq->tail = NULL;
}

void QueuePush(Queue* pq, QTDataType x)
{
	assert(pq);

	QNode* newnode = (QTDataType*)malloc(sizeof(QTDataType));
	if (newnode == NULL)
	{
		printf("malloc fail!\n");
		return;
	}

	newnode->data = x;
	newnode->next = NULL;

	//第一个结点
	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	//多个节点
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	//只有一个节点
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		//保存下一个节点的地址
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

int QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->head == NULL;
}

QTDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	return pq->head->data;
}

QTDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	return pq->tail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	int size = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		size++;
		cur = cur->next;
	}

	return size;
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 

#include "Queue.h"

void QueueTest()
{
	Queue q;
	QueueInit(&q);
	
	QueuePush(&st, 1);
	QueuePush(&st, 2);
	QueuePush(&st, 3);
	QueuePush(&st, 4);
	
    //打印队列内的数据,由于不能破坏队列的特性,所以不能遍历
	while (!QueueEmpty(&st))
	{
		printf("%d ", QueueFront(&st));
		QueuePop(&st);
	}

	QueueDestory(&q);

}


int main()
{
	QueueTest();

	return 0;
}

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

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

相关文章

opencv自定义间隔帧获取视频转存为图片的GUI界面实现

该程序功能只将mp4转为jpg 希望得到您的指导 非常感谢您观看我的博客&#xff0c;我的博客是为了记录我的学习过程同时保留我的某些可重复利用代码以方便下次使用。如果您对我的博客有任何建议还请您不吝指出&#xff0c;非常感谢您对我的指导。 背景 在实现opencv逐帧获取…

js处理数组分类

const obj [{"groupingType": "1","remark": "梨花带雨","totalRmbMoney": 7,"kyeGroupingType": "广州一组"},{"groupingType": "2","remark": "99","…

js 实现动画的两种方案对比:setTimeout vs RAF (requestAnimationFrame)

setTimeout 需手动控制频率&#xff0c;页面隐藏后仍会执行动画&#xff0c;更加耗费性能。 requestAnimationFrame 简称 RAF , 会在浏览器中每次刷新屏幕时调用一个函数&#xff0c;用于创建平滑的动画&#xff0c;因为它会自动适应屏幕的刷新率&#xff0c;无需手动控制频率。…

搭建自己的博客-拾壹博客

写在前面 唠叨两句 作为一个技术开发人员&#xff0c;没有一个自己的博客&#xff0c;人生注定缺少点什么东西&#xff0c;是不是&#xff1f;最近研究了一些博客搭建&#xff0c;本文是使用开源项目”拾壹博客“进行搭建。 推荐等级 所需技术难度&#xff1a;4星 后续自定义…

Redis中的缓存击穿

缓存击穿 缓存击穿问题也叫热点key问题&#xff0c;就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了&#xff0c;无数的请求访问会在瞬间给数据库带来巨大压力。 &#x1f914;现象分析&#xff1a; 当线程1查询缓存时&#xff0c;未命中&#xff0c;于是从数据…

2004-2022年各省化学需氧量数据(无缺失)

2004-2022年各省化学需氧量数据&#xff08;无缺失&#xff09; 1、2004-2022年 2、范围&#xff1a;31省 3、指标&#xff1a;化学需氧量 4、来源&#xff1a;各省年鉴、国家统计局、环境年鉴 5、指标解释&#xff1a;化学需氧量(COD)排放量指工业废水中COD排放量与生活污…

python的O2O生鲜食品订购flask-django-nodejs-php

用户只能通过一些类似软件进行查看生鲜超市&#xff0c;这样的管理方式仍然是比较机械传统的&#xff0c;本文通过对市面上常见的线上管理系统与现实生活中结合问题的讨论&#xff0c;从一个微信小程序的O2O生鲜食品订购角度进行需求分析&#xff0c;提供一些新的思路&#xff…

2024年阿里云2核4G服务器价格30元、165元和199元1年

阿里云2核4G服务器租用优惠价格&#xff0c;轻量2核4G服务器165元一年、u1服务器2核4G5M带宽199元一年、云服务器e实例30元3个月&#xff0c;活动链接 aliyunfuwuqi.com/go/aliyun 活动链接如下图&#xff1a; 阿里云2核4G服务器优惠价格 轻量应用服务器2核2G4M带宽、60GB高效…

外卖店优先级c++

题目 输入样例&#xff1a; 2 6 6 1 1 5 2 3 1 6 2 2 1 6 2输出样例&#xff1a; 1样例解释 6时刻时&#xff0c;1 号店优先级降到 3&#xff0c;被移除出优先缓存&#xff1b;2 号店优先级升到 6&#xff0c;加入优先缓存。 所以是有 1 家店 (2 号) 在优先缓存中。 思路 …

整数和浮点数在内存中存储

整数在内存中的存储 整数的2进制表⽰⽅法有三种&#xff0c;即原码、反码和补码。 对于整形来说&#xff0c;数据存放内存中的其实是补码。 在计算机系统中&#xff0c;数值一律用补码来表示和存储。原因是&#xff0c;使用补码&#xff0c;可以使符号位和数值域统一处理&am…

手撕LRU 最近最少使用缓存淘汰策略 + LinkedHashMap

LRU 最近最少使用缓存淘汰策略 1 LRU 算法就是一种缓存淘汰策略2 手撕LRU3 LinkedHashMap 常见面试题 1 LRU 算法就是一种缓存淘汰策略 计算机的缓存容量有限&#xff0c;如果缓存满了就要删除一些内容&#xff0c;给新内容腾位置。但问题是&#xff0c;删除哪些内容呢&#x…

【算法】双指针的应用

文章目录 前言1. 移动零&#xff08;easy&#xff09;2. 复写零&#xff08;easy&#xff09;3. 快乐数&#xff08;medium&#xff09;4. 盛水最多的容器&#xff08;medium&#xff09;5. 有效三角形的个数&#xff08;medium&#xff09;6.和为 s 的两个数字&#xff08;eas…

diffusion model(十四): prompt-to-prompt 深度剖析

infopaperPrompt-to-Prompt Image Editing with Cross Attention Controlgithubhttps://github.com/google/prompt-to-promptOrg:Google Research个人复现https://github.com/myhz0606/diffusion_learning个人博客主页http://myhz0606.com/article/p2p 1 前言 基于扩散模型&a…

【OpenSSH】Windows系统使用OpenSSH搭建SFTP服务器

【OpenSSH】Windows系统使用OpenSSH搭建SFTP服务器 文章目录 【OpenSSH】Windows系统使用OpenSSH搭建SFTP服务器一、环境说明二、安装配置步骤1.下载完成后&#xff0c;传至服务器或者本机并解压至C:/Program Files/目录下2.打开PowerShell终端3.进入到包含ssh可执行exe文件的文…

线性顺序表算法库

list.cpp 具体函数实现 #include <stdio.h> #include "list.h" #include <malloc.h>/************************************************** ①函数名: CreateList 功 能: 用数组构建顺序表 参 数: ①SqList *&L:传入的线性表 ②ElemType a[]:使用…

网络安全是什么? 为什么要学网络安全 ?网络安全怎么学习?

网络安全是什么&#xff1f; 网络安全是指保护计算机网络、网络设备、应用程序、数据和用户免受非法访问、攻击、破坏或泄漏的过程和技术。网络安全包括多个领域&#xff0c;例如网络防御、漏洞管理、加密技术、身份验证和访问控制等等。 网络安全非常重要&#xff0c;因为现…

【Java反序列化】CommonsCollections-CC1链分析

前言 好几天没发博文了&#xff0c;偷偷憋了个大的——CC1链分析&#xff0c;手撸了一遍代码。虽然说&#xff0c;这个链很老了&#xff0c;但还是花费了我一段时间去消化吸收&#xff0c;那么接下来&#xff0c;我会简洁的介绍下整个链的利用过程&#xff0c;还有哪些不理解的…

命令提示符——CMD基础操作介绍

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…

【MySQL】基本查询(1)

【MySQL】基本查询&#xff08;1&#xff09; 目录 【MySQL】基本查询&#xff08;1&#xff09;表的增删改查Create单行数据 全列插入多行数据 指定列插入插入否则更新替换 RetrieveSELECT 列全列查询指定列查询查询字段为表达式为查询结果指定别名结果去重 WHERE 条件英语不…

学点儿Java_Day9_多图带你搞懂Java访问修饰符

1 前言 在学习Java访问修饰符的作用及相应的效果时&#xff0c;看到了像下面这样的图 表达的内容基本都是一致的&#xff0c;但是带着一些疑惑经过一番“深入”学习后&#xff0c;我觉得这样划分不够细致&#xff0c;把我的总结分享一下。 2 多图带你搞懂Java访问修饰符 …