栈和队列及其多种接口实现-c语言

news2025/8/3 0:13:32

今天我们来完成栈和队列,首先我们要明白什么是栈,什么是队列。

目录

栈的选择

栈的结构

栈的初始化

栈的销毁

入栈

出栈

返回栈顶元素

计算数据个数

判断是否为空

队列的选择

队列的结构

入队列

出队列

判断是否为空

取队头元素

取队尾元素

计算数据个数

队列的销毁


我们之前写过顺序表,链表,这都是数据结构,而栈和队列,也是数据结构,栈的特殊地方在于,栈是后进先出,也叫LIFO(last in first out),比如我们平时在桌子上堆放的书,我们堆放了十本书,如果不直接把书抽出来的话,我们要拿到最下面那本书,需要把上面九本先拿下去,也就是先要从最上面的那一本开始拿,而最上面的那一本,是我们最后放上去的,这就是一个栈。再说队列,队列的特殊地方在于先进先出,也叫FIFO(first in first out),比如我们日常生活里的排队就是一个队列,不考虑插队这些,正常情况下,第一个来的人一定第一个完成自己的业务,最后一个来的人最后完成,这就是一个队列。

我们还是采取工程化的写法。

 首先我们来完成栈,栈的实现可以使用数组或者链表,那我们选取哪个比较好呢?

栈的选择

我们来比较一下二者的优缺点。

 

首先我们看数组: 使用数组就相当于之前顺序表的尾插和尾删,用尾去做栈顶,非常合适,唯一的缺点是空间不足时需要扩容。完美避开了顺序表插入删除时需要挪动数据的缺点。

我们再来看链表,链表又有多种结构,我们介绍一种使用单链表来完成的,插入和删除都只有O(1)。

使用这种结构即可完成,最初栈顶和栈底都为空,插入第一个元素后,栈顶栈底都指向第一个元素,每次插入新元素后,只需将其赋给栈顶,删除时,先删除节点,再把栈顶指向栈顶的next即可。

如果用尾做栈顶,那么使用双向链表最好,如果使用头做栈顶,那么单链表最好,插入删除都是O(1)。

知道了两种结构完成栈的优缺点,大家会选择哪一个呢?我们这里选择数组来完成栈,因为使用数组的话各方面效率都会更优,这里知识和预加载有关,这里就不多介绍,我们举个例子,使用数组和链表的区别,学生时代家里每个月都会给你打生活费,数组就像是一个月给你打一次生活费,够你一个月花,链表就像每天给你打一次,很明显,使用数组要比链表效率高。

决定好了用什么来实现栈,接着我们来完成栈的结构和各类接口

栈的结构

typedef int STDataType;

typedef struct Stack {
	STDataType* a;
	int top;
	int capacity;
}ST;

我们将其写为动态栈,即a不直接写为数组,不然就写死了,并且我们使用top来记录栈顶位置

接着我们来完成栈的初始化

栈的初始化

void StackInit(ST* ps)//初始化
{
	assert(ps);
	ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL) {
		printf("malloc fail\n");
		exit(-1);
	}
	ps->capacity = 4;
	ps->top = 0;
}

这里的top选取0还是-1看自己喜欢,选取-1代表的是top就是当前栈顶元素,选取0表示top为栈顶元素的后一位,我这里选择初始赋0,同时,如果初始化失败,我们输出提示并结束程序

栈的销毁

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

入栈

void StackPush(ST* ps, STDataType x)//入栈
{
	assert(ps);
	if (ps->top == ps->capacity) {//满了扩容
		STDataType* tmp= (STDataType*)realloc(ps->a,ps->capacity*2*sizeof(STDataType));
		if (tmp == NULL) {
			printf("realloc fail\n");
			exit(-1);
		}
		else {
			ps->a = tmp;
			ps->capacity *= 2;
		}
	}
	ps->a[ps->top] = x;
	ps->top++;
}

入栈时我们要判断栈是否满了,满了需要扩容,扩容时我们扩容到原来的二倍,并且要判断是否扩容失败,失败的话我们给出提示并且结束程序,入栈后要记得给top+1

出栈

void StackPop(ST* ps)//出栈
{
	assert(ps);
	assert(ps->top>0);
	ps->top--;
}

出栈我们直接使top-1,有人可能会先让栈顶元素置为0,但是这句是不需要的,因为入栈元素有时候本身就可能为0,并且我们不能随意出栈,比如top为0时,即空栈,所以我们加一句断言

返回栈顶元素

STDataType StackTop(ST* ps)//返回栈顶元素
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

和出栈同理,栈为空时不能返回,我们加一句断言

计算数据个数

void StackSize(ST* ps)//计算数据个数
{
	assert(ps);
	return ps->top;
}

判断是否为空

bool StackEmpty(ST* ps)//判断是否为空
{
	assert(ps);
	return ps->top == 0;
}

我们直接return ps->top==0,使用bool类型,如果top==0,为真,返回true,否则返回false

我们来测试一下我们的代码

可以看到,没有问题。

 完成了栈,我们接着来看队列

队列的选择

和栈一样,队列也需要在数组和链表里选择一个,队列只允许在它的一端插入数据,在另一端删除数据

 这次其实我们要选择链表,因为数组会出现假溢出现象,比如这个数组可以放5个元素,现在已经放了四个,我们出队列一个元素,再入队列一个,就会发现数组下标0的位置是空的,但队列却显示已满,如果要挪动数据的话也非常麻烦,还有一种办法就是写成循环队列,但那是后续的事情,而且也有局限性。选取链表的话,用单链表就可以解决,采用上图结构,队列就只需要尾插和头删,效率都很高。

选择好了用什么来实现队列后,我们来设计队列的结构吧

队列的结构

typedef int QDataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

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

我们设计好队列的节点,然后采取两个指针,队头和队尾来设计队列即可。而且采用这样的结构可以避免使用二级指针(后续接口如果参数传QNode的话是需要二级指针的)

入队列

void QueuePush(Queue* pq, QDataType x)//入队列
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL) {
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->tail == NULL) {
		pq->head = pq->tail = newnode;
	}
	else {
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

入队列是在队尾入元素,入队列我们需要判断队列是否为空,入队列需要一个新节点,我们给节点申请一个空间,申请失败我们输出提示并且报错,然后将x赋给节点的data,节点的next置为空,我们还要判断队列是否有节点,如果一个没有,那么队头和队尾都指向这个新节点,否则队尾的next指向新节点,再把队尾指向新节点

出队列

void QueuePop(Queue* pq)//出队列
{
	assert(pq);
	assert(pq->head);
	if (pq->head->next == NULL) {
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else {
		Queue* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

出队列是出队头元素,我们要进行断言,我们出队列需要把头节点的空间释放,然后让head指向它的next,但在释放前我们需要把head的next保存起来,否则我们就找不到next了,这是正常情况,如果队列只有一个元素,出队列后会造成tail的野指针现象,所以我们需要额外判断,所以我们把出队列分为队列有一个元素和多个元素的情况

判断是否为空

bool QueueEmpty(Queue* pq)//判断是否为空
{
	assert(pq);
	return pq->head == NULL;
}

我们直接返回该判断语句即可,为空返回true,否则返回false

取队头元素

QDataType QueueFront(Queue* pq)//取队头元素
{
	assert(pq);
	assert(pq->head);
	return pq->head->data;
}

我们直接返回队头元素即可,最好再加一句断言,有时候会很有用,比如初始化后,队列里没有元素,这种情况没有第二句断言我们就不知道程序哪里出现了错误

取队尾元素

QDataType 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) {
		cur = cur->next;
		size++;
	}
	return size;
}

这个接口有人可能会把循环条件写成cur!=pq->tail,但这样是错误的,会少计算一个元素

队列的销毁

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

销毁需要注意先要保存cur的下一个,不然会找不到,然后就是最后要把head和tail置为空

我们最后再来测试一下代码

没有问题,到这里,我们的栈和队列以及他们的接口就全部实现了,希望大家可以有所收获,下面我会附上全部代码

#pragma once
//Stack.h
#include <stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>
typedef int STDataType;

typedef struct Stack {
	STDataType* a;
	int top;
	int capacity;
}ST;

void StackInit(ST* ps);//初始化
void StackDestory(ST* ps);//销毁
void StackPush(ST* ps,STDataType x);//入栈
void StackPop(ST* ps);//出栈
STDataType StackTop(ST* ps);//返回栈顶元素
void StackSize(ST* ps);//计算数据个数
bool StackEmpty(ST* ps);//判断是否为空

 

//Stack.c
#include "Stack.h"
void StackInit(ST* ps)//初始化
{
	assert(ps);
	ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);
	if (ps->a == NULL) {
		printf("malloc fail\n");
		exit(-1);
	}
	ps->capacity = 4;
	ps->top = 0;
}
void StackDestory(ST* ps)//销毁
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->top = ps->capacity = 0;
}
void StackPush(ST* ps, STDataType x)//入栈
{
	assert(ps);
	if (ps->top == ps->capacity) {//满了扩容
		STDataType* tmp= (STDataType*)realloc(ps->a,ps->capacity*2*sizeof(STDataType));
		if (tmp == NULL) {
			printf("realloc fail\n");
			exit(-1);
		}
		else {
			ps->a = tmp;
			ps->capacity *= 2;
		}
	}
	ps->a[ps->top] = x;
	ps->top++;
}
void StackPop(ST* ps)//出栈
{
	assert(ps);
	assert(ps->top>0);
	ps->top--;
}
STDataType StackTop(ST* ps)//返回栈顶元素
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}
void StackSize(ST* ps)//计算数据个数
{
	assert(ps);
	return ps->top;
}
bool StackEmpty(ST* ps)//判断是否为空
{
	assert(ps);
	return ps->top == 0;
}
#pragma once
//Queue.h
#include <stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>
typedef int QDataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

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

void QueueInit(Queue* pq);//初始化
void QueueDestory(Queue* pq);//销毁
void QueuePush(Queue* pq, QDataType x);//入队列
void QueuePop(Queue* pq);//出队列
QDataType QueueFront(Queue* pq);//取队头元素
QDataType QueueBack(Queue* pq);//取队尾元素
int QueueSize(Queue* pq);//取数据个数
bool QueueEmpty(Queue* pq);//判断是否为空
//Queue.c
#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) {
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)//入队列
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL) {
		printf("malloc fail\n");
		exit(-1);
	}
	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 {
		Queue* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}
QDataType QueueFront(Queue* pq)//取队头元素
{
	assert(pq);
	assert(pq->head);
	return pq->head->data;
}
QDataType 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) {
		cur = cur->next;
		size++;
	}
	return size;
}
bool QueueEmpty(Queue* pq)//判断是否为空
{
	assert(pq);
	return pq->head == NULL;
}
//test.c
#include "Stack.h"
#include"Queue.h"
void testStack() {
	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);
}

void testQueue() {
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	while (!QueueEmpty(&q)) {
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	QueueDestory(&q);
}
int main() {
	//testStack();
	testQueue();
	return 0;
}

如有错误,还请指正。

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

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

相关文章

Java Spring Bean的生命周期 三级缓存

Java Spring Bean的生命周期 三级缓存 SpringBean的生命周期&#xff1a;是从 Bean 实例化之后&#xff08;即通过反射创建出对象之后&#xff09;&#xff0c;到Bean成为一个完整对象&#xff0c;最终存储到单例池中&#xff0c;这个过程被称为Spring Bean的生命周期。Spring…

盘一盘那些年我们使用的Java

一、序 那些年我们使用过的Java版本。我是一个80后&#xff0c;当年在大学时代使用的是Java5&#xff0c;当时是大三的时候学校有了编程课&#xff0c;最开始学的是汇编语言、VB、C然后再是Java。当时就是Java5&#xff0c;搞了个课程设计与顺便也参加了个校园程序设计大赛。当…

MCE | Nrf2 的“戏精”之路

外界刺激 (如药物、紫外线和电离辐射) 和内源性自由基和活性氧 (ROS) 会直接或者间接地损伤蛋白质、脂质和 DNA 等细胞成分&#xff0c;为了抵御这些不利影响&#xff0c;机体形成了一套复杂的氧化应激应答系统来缓解细胞所受的损害。而 Nrf2&#xff0c;作为调控抗氧化应激的一…

SAP ABAP Function Module 的动态调用方式使用方式介绍试读版

在本教程前面的步骤 7&#xff0c;我们介绍了 ABAP Function Module 的基本使用方法&#xff1a; 7. ABAP function module 的使用 最近我的知识星球有朋友提问&#xff1a; 大佬&#xff0c;我想问一下动态获取到物料主数据的字段名之后&#xff0c;如何将获取到的字段名去与…

01. Docker的基本介绍

Docker概述&#xff1f; Docker 是一个开源的应用容器引擎&#xff0c;基于Go语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。 容器…

1网络模型

网络层次可划分为五层因特网协议栈和七层因特网协议栈 五层 互联网分层 物理层 发送端&#xff1a;链路层给的bit把他变成信号&#xff0c;什么电磁波的信号通过介质传出去 接收端&#xff1a;把物理信号还原为原来的数据01010110这种 传的是比特 链路层 网卡&#xff1a;有…

力扣 112. 路径总和

力扣 112. 路径总和 题目 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。…

[附源码]java毕业设计医院挂号系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

css解决uniapp使用image标签图片无法撑满全屏问题

css解决uniapp使用image标签图片无法撑满全屏问题 文章目录css解决uniapp使用image标签图片无法撑满全屏问题前言一、问题还原二、问题解决1、在解决问题之前有必要先来学习一个css的属性2、问题的原因3、解决问题总结前言 本片文章主要讲解了如何解决&#xff0c;开发中遇到需…

RTL8380M管理型交换机系统软件操作指南三:VLAN

接下来对管理型交换机的VLAN部分进行详细的描述&#xff0c;主要包括以下七部分内容&#xff1a; VLAN概述、VLAN优点、VID概念、PVID、端口处理报文方式、基础配置、VLAN端口配置一 VLAN概述 VLAN&#xff08;Virtual Local Area Network&#xff09;的中文名为“虚拟局域网”…

数仓之范式

学习目录一、基本概念二、函数依赖三、三范式区别一、基本概念 定义&#xff1a;范式是指数据建模中必须遵守一定的规则 目的&#xff1a;降低数据的冗余性 缺点&#xff1a;获取数据时&#xff0c;需要通过Join拼接出最后的数据 分类&#xff1a;第一范式(1NF)、第二范式(…

大数据可视化之医疗大数据平台

一 项目背景 随着信立泰企业的不断发展&#xff0c;公司管理的需求倒逼业务系统不断引入。经营数据的不断积累使得企业的信息化成为企业进行技术改造及提高企业管理水平的重要手段。信立泰因之而制定了对应的信息化战略&#xff0c;主要任务是最大限度的利用医疗大数据平台加強…

基于java的购物中心商铺管理系统的设计与实现/商铺管理系统

摘 要 随着社会的发展&#xff0c;计算机的优势和普及使得购物中心商铺管理系统的开发成为必需。购物中心商铺管理系统主要是借助计算机&#xff0c;通过对信息进行管理。减少管理员的工作&#xff0c;同时也方便广大用户对个人所需信息的及时查询&#xff0c;其次是大量信息的…

Python:语法进阶

目录 一、运算符 基本运算符 比较运算符 赋值运算符 多变量赋值 逻辑运算符 三元运算符 源码&运行结果 test01 案列 效果 二、循环语句 一、运算符 基本运算符 比较运算符 赋值运算符 多变量赋值 a, b, c 1, 2, 3 # a b c 3 print(b, c) 逻辑运算符 三元运…

docker (五) (搭建MySQL数据库集群)

MySQL集群搭建我们通过PXC【Percona XtraDB Cluster】来实现强一致性数据库集群搭建。 一 Percona XtraDB Cluster &#xff08;理论&#xff09; 1 简介 Percona XtraDB Cluster是MySQL高可用性和可扩展性的解决方案&#xff0c;Percona XtraDB Cluster提供的特性如下&…

二叉树的前序/中序/后序遍历新手入门介绍

一、前序遍历 | 根左右(DLR) 1.1 简介 前序遍历简介也叫先序遍历 前序遍历 可以分为三部分&#xff1a;根、左子树、右子树 先遍历根节点 、再遍历左子树、再遍历右子树 左/右 子树遍历方法&#xff1a;先访问根节点&#xff0c;再访问 左孩子节点&#xff0c;访问到左孩子节…

04、SpringAOP详解

1、Spring AOP简介 1、什么是AOP 1、定义阐述 AOP的全称是 Aspect Oriented Programming&#xff0c;是面向切面编程的技术&#xff0c;把一个个的横切关注点放到某个模块中去&#xff0c;称之为切面。那么每一个的切面都能影响业务的某一种功能&#xff0c;切面的目的就是功…

蒙泰转债上市价格预测

蒙泰转债基本信息转债名称&#xff1a;蒙泰转债&#xff0c;评级&#xff1a;A&#xff0c;发行规模&#xff1a;3.0亿元。正股名称&#xff1a;蒙泰高新&#xff0c;今日收盘价&#xff1a;31.3&#xff0c;转股价格&#xff1a;26.15。当前转股价值 转债面值 / 转股价格 * 正…

【Java进阶】学好常用类,code省时省力

一、工具类 所谓工具类&#xff0c;即将完成通用功能的方法分类放到类中&#xff0c;工具类能够被高效地重复使用&#xff0c;使我们的编码快速、高效。 工具类的设计 工具方法使用public static修饰&#xff0c;通过工具类名调用工具方法。对于工具类&#xff0c;我们通常都…

AI内容生成时代:该如何和AI对话?

北大出版社&#xff0c;人工智能原理与实践 人工智能和数据科学从入门到精通 详解机器学习深度学习算法原理 人工智能原理与实践 全面涵盖人工智能和数据科学各个重要体系经典 AI自动生成内容&#xff08;AIGC)最近可以说非常热门。而如何给AI有效输入提示&#xff0c;从而达…