首页 > 其他分享 >栈和队列(数据结构)

栈和队列(数据结构)

时间:2024-11-29 23:33:47浏览次数:7  
标签:ps pq 队列 SQ assert 数据结构 void

一. 栈

1.1 概念与结构

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

1.2 栈的实现

在实现栈之前,我们想一下实现栈要用什么结构,是数组?顺序表?单链表?根据栈的特性:只允许在一端进行插入和删除元素的操作。我们会首选数组,原因是什么呢?在C语言中,使用数组来实现栈结构有以下几个优势:访问效率高
- 数组是连续存储的内存结构,通过下标访问元素的时间复杂度为O(1)。
- 顺序表本质上也是基于数组实现的,虽然也有连续存储的特性,但在栈的操作场景下,数组可以更简洁地实现栈的功能。
实现简单
- 用数组实现栈,只需要定义一个数组和一个记录栈顶位置的变量。
- 相比之下,链表实现栈需要定义链表节点结构体,涉及节点的动态内存分配和指针操作,实现起来较为复杂。
空间利用可预测
- 数组实现栈,空间大小在定义时基本确定(如果是静态数组),如 char buffer_stack[100]; ,其占用的内存空间是固定的。这在一些对内存使用要求可预测的场景很有用,便于进行内存管理和性能评估。
- 链表的空间是动态分配的,会产生内存碎片,而且每个节点都有指针域占用额外空间。顺序表虽然也基于数组,但在动态扩展等操作时也会涉及复杂的内存管理问题,不如数组简单直接用于栈的实现。

下面是使用数组实现栈的结构:

//stack.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

//定义栈的结构
typedef int datatype;
typedef struct Stack
{
	datatype* arr;
	int capacity;//栈的空间大小
	int top;     //栈顶
}ST;

void STInit(ST* ps);  //初始化
void STDestory(ST* ps);  //销毁
datatype STTop(ST* ps);  //取栈顶元素
bool STEmpty(ST* ps);  //判断是否为空
void STPush(ST* ps,datatype x); //插入数据
void STPop(ST* ps);    //删除数据 

//test.c
#define  _GRT_SECURT_NO_WARNINGS 1
#include "stack.h"
void test1()
{
	ST st;
	STInit(&st);
	STPush(&st, 1);
	STPush(&st, 2);
	STPush(&st, 3);
	STPush(&st, 4);
	//STPush(&st, 5);
	//循环出栈,直到栈为空
	while (!STEmpty(&st))
	{
		datatype data = STTop(&st);
		printf("%d", data);
		//出栈
		STPop(&st);
	}
	STDestory(&st);
}

int main()
{
	test1;
	return 0;
}

//stack.c
#define  _GRT_SECURT_NO_WARNINGS 1
#include"stack.h"
void STInit(ST* ps)//初始化
{
	assert(ps);
	ps->arr = NULL;
	ps->capacity = ps->top = 0;
}

datatype STTop(ST* ps)//取栈顶元素
{
	assert(ps);
	assert(!STEmpty(ps));
	return ps->arr[ps->top - 1];
}

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

void STPush(ST* ps,datatype x)  //插入数据
{
	//1.判断空间是否足够
	if (ps->capacity == ps->top)
	{
		//增容
		int newcapacity = ps->capacity == 0 ? 4 : 2 * ps ->capacity;
		ST* tmp = (ST*)realloc(ps->arr, newcapacity * sizeof(ST));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(1);
		}
		ps->arr = tmp;
		ps->capacity = newcapacity;
	}
	ps->arr[ps->top++] = x;
}

void STPop(ST* ps)    //删除数据 
{
	assert(ps);
	assert(!STEmpty(ps));
	--ps->top;
}


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

  关于栈的实现呢,我们到这里差不多讲完了,跟顺序表的实现基本是一样的,但是不一样的地方同样是存在的,大家可能发现在实现栈的时候我们没有写打印数据的函数,因为栈是不能被遍历的,它只能从一端来插入或者删除数据,也就是说假如我们依次输入1 2 3 4,那么位于栈底的数字就是1,位于栈顶的数字就是4,如果我们现在要打印在栈中的数字,必须先打印4并且在打印完之后让4出栈之后,我们才能访问到下一个数字3,所以在栈中是不存在一次性遍历的。

二. 队列

2.1 概念与结构

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

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。而且大家注意一点是在实现队列结构的时候我们是需要先创建结点的结构,再创建一个队列的结构。
//定义队列结构
//创建节点结构
typedef int qdatatype;
typedef struct queue
{
	qdatatype data;
	struct queue* next;
}SQNode;

struct queue
{
	SQNode* phead;
	SQNode* ptail;
}SQ;

实现队列的全部结构功能如下:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
//定义队列结构
//创建节点结构
typedef int qdatatype;
typedef struct QueueNode
{
	qdatatype data;
	struct QueueNode* next;
}SQNode;

typedef struct queue
{
	SQNode* phead;//队头
	SQNode* ptail;//队尾
	int size;//记录队列有效的个数
}SQ;

//初始化队列
void QueueInit(SQ* pq);
//销毁队列
void SQDestroy(SQ* pq);
// ⼊队列,队尾
void SQPush(SQ* pq, qdatatype x);
// 出队列,队头
void SQPop(SQ* pq);
//取队头数据
qdatatype SQFront(SQ* pq);
//取队尾数据
qdatatype SQBack(SQ* pq);
//队列判空
bool SQEmpty(SQ* pq);
//队列有效元素个数
int SQSize(SQ* pq);

#define  _GRT_SECURT_NO_WARNINGS 1
#include"queue.h"

//初始化队列
void QueueInit(SQ* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	int size = 0;
}
//销毁队列
void SQDestroy(SQ* pq)
{
	assert(pq);
	assert(!SQEmpty(pq));
	SQNode* pcur = pq->phead;
	while (pcur)
	{
		SQNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
// 入队列,队尾
void SQPush(SQ* pq, qdatatype x)
{
	assert(pq);
	SQNode* newnode = (SQNode*)malloc(sizeof(SQNode));
	if (newnode == NULL)
	{
		perror("fail");
		exit(1);
	}
	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}
//队列判空
bool SQEmpty(SQ* pq)
{
	assert(pq);
	return pq->phead = NULL && pq->ptail == NULL;
}
// 出队列,队头
void SQPop(SQ* pq)
{
	assert(pq);
	assert(!SQEmpty(pq));
	if (pq->ptail == pq->phead)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
       SQNode* new = pq->phead->next;
	   free(pq->phead);
	   pq->phead = new;
	}
	--pq->size;
	
}
//取队头数据
qdatatype SQFront(SQ* pq)
{
	assert(pq);
	assert(!SQEmpty(pq));
	return pq->phead->data;
}
//取队尾数据
qdatatype SQBack(SQ* pq)
{
	assert(pq);
	assert(!SQEmpty(pq));
	return pq->ptail->data;
}

//队列有效元素个数
int SQSize(SQ* pq)
{
	assert(pq);
	return pq->size;
}

#define  _GRT_SECURT_NO_WARNINGS 1
#include"queue.h"

void test1()
{
	SQ* q;
	QueueInit(&q);
	SQPush(&q, 1);
	SQPush(&q, 2);
	SQPush(&q, 3);
	SQPush(&q, 4);
	printf("head:%d\n", SQFront(&q));
	printf("tail:%d\n", SQFront(&q));
}


int main()
{
	test1();
	return 0;
}

标签:ps,pq,队列,SQ,assert,数据结构,void
From: https://blog.csdn.net/OKkankan/article/details/143996376

相关文章

  • 【初阶数据结构和算法】初识树与二叉树的概念以及堆和完全二叉树之间的关系
    文章目录一、树的概念与结构1.树的概念2.树的相关术语3.树的表示4.树形结构实际运用举例二、二叉树的概念及特殊二叉树1.二叉树的概念2.特殊的二叉树满二叉树完全二叉树二叉树的性质(由满二叉树特点推导)三、二叉树的存储结构1.二叉树的顺序结构2.二叉树的链式结构......
  • 深入理解 FreeRTOS 队列集(建议收藏!!!)
    在FreeRTOS操作系统这个“大家庭”里,队列集扮演着一个特殊的“管家”角色,它让多个队列之间的协作变得井井有条。一、队列集的基本概念队列集就像是一个专门用来存放其他队列“钥匙”(句柄)的盒子。假设我们有队列A这个“小仓库”,它能存放LengthA数量的“宝贝”(数......
  • 代码随想录第十一天|栈与队列part02--150.逆波兰表达式求值、239.滑动窗口最大值、347
    150.逆波兰表达式求值(150.逆波兰表达式求值)题目分析:计算逆波兰表达式(后缀表达式:左右中)的值,算符仅包含四则运算,操作数为一个整数或另一个表达式,整数除法向零截断(向下取整),无除零运算,答案及中间结果不超过32位,即使用整型int即可。解题重点:后缀表达式的每一个表达式中:读入1个算......
  • 算法与数据结构练习——异或
    知识点讲解:一、异或操作定义:异或是指相同为0,不同为1,也可理解为无进位相加!!很重要!!二、关于异或运算的几个性质:1.0^N=N       (0和任何数异或都是原来的数)2.N^N=0       (任何数异或自己都是0)3.满足交换律和结合律:所以无论怎么改变顺序,最后的结果都是一样......
  • RK3568平台开发系列讲解(Input子系统篇)输入子系统数据结构体
    ......
  • 数据结构查找
    语雀链接:https://www.yuque.com/g/wushi-ls7km/ga9rkw/kl8h357ofcgocddz/collaborator/join?token=AwLuhwfJL8wLO2FH&source=doc_collaborator#《数据结构查找》......
  • 【计算机科学】深入理解队列:有序的数据之道
    在编程世界中,数据结构是解决问题的核心工具,而队列则是其中的基础模块之一。无论是任务调度、缓存系统还是算法设计,队列的先进先出(FIFO)特性使其成为高效解决问题的利器。本篇文章将从零开始,带你理解队列的概念、实现思路、典型应用及其背后的逻辑。本篇文章需要读者具有链......
  • JDK17 AbstractQueuedSynchronizer 二 条件队列
    条件队列同步队列中的线程是为了争抢锁,而条件队列中的线程是主动释放锁,挂起自己,等条件满足时被别的线程唤醒,继续工作。AQS里只有1个同步队列,但可以有多个等待队列,每个等待队列对应一个ConditionObject对象。publicstaticvoidmain(String[]args){ ReentrantLocklo......
  • JDK17 AbstractQueuedSynchronizer 一 同步队列
    AQS抽象队列同步器是JDK提供的用于管理线程间同步状态的类。常见的同步器类ReentrantLock,CountDownLatch,Semaphore等都是AbstractQueuedSynchronizer的子类。AQS提供三个功能提供同步状态。一个是state属性,管理资源的状态。一个是AQS的父抽象类的exclusiveOwnerThread......
  • [数据结构]建立二叉树的中序线索二叉树
     输入二叉树的扩展的先序遍历序列,建立一棵二叉树,然后建立该二叉树的中序线索二叉树,在中序线索二叉树基础上中序遍历,输出中序遍历序列。二叉树结点类型为char,特殊字符为@。输入格式:输入先序遍历序列:ABD@F@@@CE@@@输出格式:输出二叉树的中序遍历序列为:DFBAEC输入样例:......