首页 > 其他分享 >栈与队列(概念与实现)

栈与队列(概念与实现)

时间:2024-10-27 10:18:24浏览次数:8  
标签:ps head obj 队列 Queue 概念 int 实现

目录

前言

一,栈

1,栈(Stack)

2,栈的实现

3,栈的用途

二,队列

1,队列(Queue)

2,队列的实现

3,队列的用途

4,环形队列

5,环形队列的用途


前言

       栈与队列,作为核心的数据结构,对于每一位程序员来说都至关重要。它们简化了问题解决的过程,提升了代码的效率。

       在这篇博客中,我们将探讨这两大工具的精髓,领悟其在编程中的价值。让我们一起探索栈与队列的奥秘,开启高效编程之旅。

一,栈

1,栈(Stack)

       栈属于线性表的一种,它具有一种特性,只允许在固定的一端对元素进行插入与删除操作,正因为这种特性,使得在其中元素存取顺序相反,即后进先出(Last In First Out )

       能进行元素操作的那端叫做栈顶,另一端叫做栈底

       栈的元素插入操作称为 进栈 / 压栈 / 入栈;栈的元素删除操作称为 出栈。    

       栈就好像一个好几层的箱子,每一层只能放一件物品,放物品时,顺序是:ABCDE,当我们取出时,顺序是:EDCBA,放在箱底的东西自然最后拿出来;而我们存取物品的途径是箱口。

2,栈的实现

栈可以由数组与链表实现,那应该使用哪一种呢?

       我们不妨比较一下:

1,使用链表作为底层代码,采用头插与头删,头插需要创建新节点并改变多个指针指向,头删时,要删除当前节点,并改变下一节点与 phead 指向。

2,使用数组为底层代码,插入时,参考顺序表尾删尾插,创建新节点再赋值,删除时,只需要size--。

相较之下,数组更胜一筹,结构如下:

下面总结一下各个方法的实现步骤:

初始化,先判断节点的存在(防止后续为空),将数组置为空,再将各项数据置为零。

入栈,判空,先判断容量是否足够,不够就扩容(realloc,再输入数据),足够就直接输入。

出栈,判空,将有效元素个数减一即可

判空栈,判空,返回有效元素个数

销毁,判断数组是否为空,不为空就free,然后置空,令其他内容值为零。

取栈顶,判断栈是否为空(是否有元素),返回,数组下标为有效数目减一。

得出元素个数,判空,返回有效数目。

头文件,定义声明文件:

#pragma once
#include<stdio.h>
#include<assert.h>
#include <stdlib.h> 
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack//动态
{
	STDataType* val;//底层数组
	int top;      //有效数据
	int capacity; //可用容量
}Stack;

//初始化
void StackInit(Stack*ps);
//入栈/压栈/进栈
void StackPush(Stack* ps,STDataType x );
//判断栈空
bool StackEmpty(Stack*ps);
//出栈
void StackPop(Stack* ps);
//取栈顶元素
STDataType StackTop(Stack* ps);
//获取栈中有效元素个数
int StackSize(Stack* ps);
//栈的销毁
void StackDestory(Stack* ps);

源文件,操作实现文件:

#define _CRT_SECURE_NO_WARNINGS 1
#include"Stack.h"
//初始化
void StackInit(Stack* ps)
{
	assert(ps);//为什么要判空?
	ps->val = NULL;
	ps->capacity = ps->top = 0;
}
void StackPush(Stack* ps, STDataType x)
{
	assert(ps);
	if (ps->capacity == ps->top)
	{
		int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		STDataType* tmp = (STDataType*)realloc(ps->val, newCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc faill!\n");
			exit(1);
		}
 		ps->val = tmp;
		ps->capacity = newCapacity;
	}
		ps->val[ps->top++] = x;
	
}
bool StackEmpty(Stack*ps)
{
	assert(ps);
	return ps->top == 0;
}

void StackPop(Stack* ps)
{
	assert(ps);
	ps->top--;
}
STDataType StackTop(Stack* ps)
{
	assert(!StackEmpty(ps));//assert(ps);为什么不可以?这里是判断是否有数据
	return ps->val[ps->top-1];
}
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->top;
}
void StackDestory(Stack* ps)
{
	if (ps->val != NULL)
		free(ps->val);
	ps->val = NULL;
	ps->top = ps->capacity = 0;
}

3,栈的用途

栈(Stack)是一种后进先出(Last In First Out, LIFO)的数据结构,它在计算机科学中有着广泛的应用。以下是栈的一些主要用途:

  1. 函数调用:操作系统使用栈来管理函数调用。当函数被调用时,当前函数的状态会被保存在栈上,包括局部变量、返回地址等。当函数执行完毕后,可以从栈中恢复状态,并返回到调用点。

  2. 表达式求值:编译器在解析和求值表达式时,经常使用栈来存储操作数和操作符,特别是对于逆波兰表示法(后缀表达式)。

  3. 内存分配:在许多编程语言中,局部变量的内存分配是在栈上进行的。这些变量在函数调用结束时自动从栈中释放。

  4. 后进先出的操作:任何需要后进先出逻辑的场景都可以使用栈,例如撤销操作(如撤销在文本编辑器中的更改)、浏览器的后退功能等。

  5. 算法中的辅助结构:在许多算法中,栈被用作辅助数据结构,例如深度优先搜索(DFS)、图的遍历、迷宫解决算法等。

  6. 括号匹配:在编译器设计中,栈常用来检查表达式中的括号是否匹配。

  7. 程序调用的上下文切换:在多任务操作系统中,当任务切换发生时,当前任务的状态会被保存在栈上,当任务再次运行时,可以从栈中恢复其状态。

  8. 递归:递归算法通常依赖栈来存储每一层递归调用的状态。

  9. 表达式转换:例如,将中缀表达式转换为后缀表达式或前缀表达式。

  10. 缓存和缓冲:栈可以用来实现简单的缓存机制,例如在深度优先搜索中存储路径。

  11. 错误处理:在某些系统中,错误处理和异常处理的信息被推送到一个错误栈中,以供后续处理。

栈由于其简单而有效的数据管理方式,成为了计算机科学中非常基础且重要的工具之一。

二,队列

1,队列(Queue)

       队列也属于线性表,其特点就是,只允许在一端插入数据,在另一端删除数据,因此,其内数据存入的就先取出,即先进先出(First In First Out)。

       队列中,插入的一端称为队尾,删除的一端成为对头。

       在其中,插入称为入队,删除称为出队。

       队列的特点,就像你买早餐排的队伍,先排的就先买到早餐,而后来的就后买到早餐

2,队列的实现

       同样,队列也可以由链表和数组构成,进行比较得知,链表更优。

基本结构:

 同样,总结一下大致实现步骤:

初始化,判空,赋值

入队,判空,创节点,判断链表是否为空,再进行头尾指针的相关操作。

出队,判断指针与队列是否为空,看队列中数据个数是否大于一,只有一个就free,判空;多于一个就正常操作。

查询队头队尾元素,判空(指针与队列),返回相应指针对应的数值。

查询有效元素数目,返回size,在实现中·新创建的,用于记录元素个数。

销毁,判空,遍历链表,逐个free,头尾节点置空,size置为零。

头文件:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDataType;
typedef struct QListNode//链表节点
{
	struct QListNode* next;
	QDataType val;
}QNode;
typedef struct Queue
{
	QNode* head;
	QNode* rear;
	int size;
}Queue;
//初始化
void QueueInit(Queue* ps);
//插入/入队
void QueuePush(Queue* ps, QDataType x);
//出队列
void QueuePop(Queue* ps);
//判空
bool QueueEmpty(Queue* ps);
//取队列有效元素
int QueueSize(Queue* ps);
//获取队列头部元素
int QueueHead(Queue* ps);
//获取队尾元素
int QueueRear(Queue* ps);
//队列的销毁
void QueueDestory(Queue* ps);

源文件: 

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
void QueueInit(Queue* ps)
{
	assert(ps);
	ps->head = ps->rear = NULL;
	ps->size = 0;
}
void QueuePush(Queue* ps, QDataType x)
{
	assert(ps);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail!\n");
		exit(1);
	}
	newnode->val = x;
	newnode->next = NULL;
	if (ps->head == NULL)//判断链表为空
	{
		ps->head = ps->rear = newnode;
	}
	else
	{
		ps->rear->next = newnode;
		ps->rear = newnode;
	}
	ps->size++;
}
void QueuePop(Queue* ps)
{
	assert(ps);
	assert(!QueueEmpty(ps));
	if (ps->size == 1)
	{
		free(ps->head = ps->rear);
		ps->head = ps->rear = NULL;
	}
	else {
		QNode* tmp = ps->head;
		ps->head = ps->head->next;
		free(tmp);
		tmp = NULL;
	}
	ps->size--;
}
bool QueueEmpty(Queue* ps)
{
	assert(ps);
	return ps->head == NULL;
}
int QueueSize(Queue* ps)
{
	return ps->size;
}
int QueueHead(Queue* ps)
{
	assert(ps);
	assert(!QueueEmpty(ps));
	return ps->head->val;
}
int QueueRear(Queue* ps)
{
	assert(ps);
	assert(!QueueEmpty(ps));
	return ps->rear->val;
}
void QueueDestory(Queue* ps)
{
	assert(ps);
	QNode* pcur = ps->head;
	while (pcur)
	{
		QNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	ps->head = ps->rear = NULL;
	ps->size = 0;
}

3,队列的用途

队列(Queue)是一种先进先出(First In First Out, FIFO)的数据结构,它在计算机科学和实际应用中有着广泛的使用。以下是队列的一些主要用途:

  1. 任务调度:操作系统使用队列来管理待处理的任务和进程。例如,在多任务操作系统中,就绪队列用来存储所有准备好运行的进程。

  2. 缓冲:在输入/输出操作中,队列常用来作为缓冲区,以匹配不同速度的设备或组件。例如,打印机队列管理打印任务,确保它们按顺序打印。

  3. 消息传递:在并发编程和网络通信中,队列常用于进程或线程间的消息传递,确保消息按照发送顺序被处理。

  4. 数据结构:队列可以用于实现其他数据结构,如优先队列(通过优先级来管理元素)和堆栈(通过限制操作来实现后进先出)。

  5. 算法:在算法中,队列用于广度优先搜索(BFS)等算法,以层序遍历图或树。

  6. 资源管理:在网络中,队列用于管理网络资源,比如在拥塞控制中,队列可以用来存储等待发送的数据包。

  7. 事件处理:在图形用户界面(GUI)和游戏开发中,队列用于管理事件,如鼠标点击和键盘输入,确保它们按顺序被处理。

  8. 后台处理:在Web开发中,队列可以用于管理后台任务,比如邮件发送、图片处理等,这些任务可以异步执行,不干扰主线程。

  9. 数据缓冲:在数据流处理中,队列用于平滑数据流,尤其是在数据到达速度不均匀时。

  10. 负载均衡:在分布式系统中,队列可以帮助平衡负载,通过分配任务到不同的服务器或节点。

  队列因其简单和高效的特点,在许多不同的场景下都非常有用。它们有助于管理和控制数据的流    动,确保操作的顺序性和一致性。

4,环形队列

       扩展了解一下,实际中我们有时还会使用一种队列叫循环队列。如操作系统中生产者消费者模型就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。

下面用数组来实现:

要注意的几点,

1,如何判断队列满了?

我们默认rear指针指向的那个位置为空,且一直为空,当head+1=rear时队列就满了。

2,如何实现数组循环?

我们可以对下标取模操作:   ( rear + 1 ) % capacity    。

typedef struct {
    int* data; // 存储循环队列的数组
    int head;  // 队列头部索引
    int tail;  // 队列尾部索引
    int size;  // 循环队列的容量
} MyCircularQueue;

// 构造器,设置队列长度为 k
MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->data = (int*)malloc(sizeof(int) * (k + 1));
    obj->head = 0;
    obj->tail = 0;
    obj->size = k + 1; // 实际分配 k+1 个空间
    return obj;
}

// 检查循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->head == obj->tail;
}

// 检查循环队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->tail + 1) % obj->size == obj->head;
}

// 向循环队列插入一个元素
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if (myCircularQueueIsFull(obj)) {
        return false;
    }
    obj->data[obj->tail] = value;
    obj->tail = (obj->tail + 1) % obj->size;
    return true;
}

// 从循环队列中删除一个元素
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj)) {
        return false;
    }
    obj->head = (obj->head + 1) % obj->size;
    return true;
}

// 从队首获取元素
int myCircularQueueFront(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj)) {
        return -1;
    }
    return obj->data[obj->head];
}

// 获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj)) {
        return -1;
    }
    // 由于 tail 指向下一个插入的位置,所以需要减一才是队尾元素
    return obj->data[(obj->tail - 1 + obj->size) % obj->size];
}

// 释放循环队列占用的内存
void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->data);
    free(obj);
}

5,环形队列的用途

环形队列(也称为循环队列)非常有用,它在很多场景下都是一种高效的数据结构。以下是环形队列的一些用途和优点:

  1. 内存效率:环形队列通常使用固定大小的数组实现,与动态扩容的队列相比,它可以更有效地使用内存,因为它不需要在队列满时重新分配内存。

  2. 避免数据搬移:在普通的队列实现中,当队头元素被移除时,所有其他元素都需要向前移动一个位置。环形队列通过循环使用数组来避免这种数据搬移,从而提高性能。

  3. 固定大小:环形队列的大小是固定的,这使得它在需要固定资源分配的系统中非常有用,比如嵌入式系统或实时操作系统。

  4. 实时系统:在实时系统中,环形队列常用于任务调度和消息传递,因为它们提供了确定性的时间和空间复杂度。

  5. 缓冲区管理:在处理流数据(如音频、视频流)时,环形队列可以用作缓冲区,以平滑数据流,避免因数据到达速度不均匀导致的处理问题。

  6. 网络通信:在网络通信中,环形队列常用于管理数据包,特别是在实现网络协议时,比如TCP/IP协议栈中的缓冲区管理。

  7. 生产者-消费者模型:在多线程编程中,环形队列是生产者-消费者模型的一个常用实现,因为它可以高效地处理并发访问。

以下是环形队列的一些具体优点:

  • 常数时间操作:插入(入队)和删除(出队)操作通常可以在常数时间内完成,因为它们不需要移动队列中的其他元素。
  • 空间重用:环形队列通过循环使用数组空间,使得空间利用率更高。
  • 无锁队列:在某些实现中,环形队列可以设计为无锁队列,进一步提高并发环境下的性能。

总之,环形队列由于其高效性和确定性,在需要高效数据管理和处理的场景中非常有用

标签:ps,head,obj,队列,Queue,概念,int,实现
From: https://blog.csdn.net/2301_80001281/article/details/143255857

相关文章

  • SpringBoot秒杀系统实现asgyk--程序+源码+数据库+调试部署+开发环境
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表用户,商家,商铺信息,商品信息,限时秒杀,商品分类,顾客咨询,抢购提醒,营业统计开题报告内容一、研究背景秒杀活动作为电商平台的常见促销手段,可以极大提升用户......
  • 大数据技术045_python国潮男装微博评论数据分析系统的设计与实现 django flask爬虫可
    目录具体实现截图技术栈预期达到的目标开发技术介绍论文大纲目录编码规范核心代码部分展示其他项目推荐详细视频演示源码获取方式具体实现截图技术栈Python也提供了数据库的操作接口,通过引入Python的MySQL处理对象连接数据库后,使用通用的SQL语句方法实现数......
  • 部署 Traefik 实现 dashboard 与 原生Ingress使用 CRD IngressRoute使用
    部署Traefik00-namespace.ymlapiVersion:v1kind:Namespacemetadata:name:test-traefik00-role.ymlkind:ClusterRoleapiVersion:rbac.authorization.k8s.io/v1metadata:name:traefik-rolenamespace:test-traefikrules:-apiGroups:-"&......
  • k8s部署metallb实现service的LoadBalancer模式
    开启ipvs并开启严格ARP模式参考https://metallb.io/installation/kubectleditconfigmap-nkube-systemkube-proxy源mode:""ipvs:strictARP:false改成mode:"ipvs"ipvs:strictARP:truek8s原生部署metallb下载wgethttps://raw.githubus......
  • 【Orange Pi 5 Linux 5.x 内核编程】-字符设备文件操作实现
    字符设备文件与操作(具体实现)文章目录字符设备文件与操作(具体实现)1、内核空间程序(设备驱动)1.1kmalloc()1.2kfree()1.3copy_from_user()1.4copy_to_user()1.5open操作实现1.6write操作实现1.7read操作实现1.8close操作2、用户空间应用程序......
  • FlowLayout实现流式布局效果,看这一篇就够了!
    FlowLayout实现流式布局效果    【Android开源库】FlowLayout的基本使用什么是流式布局?就是像水一样可以流动?不,之所以这样命名只是在强调它的不规则性,它会根据你的内容多少测量你需要的控件的尺寸,完成自定义的效果。之前我做过自定义View的流式布局效果,今天就来使用hon......
  • 沈阳工业大学助农扶贫微信小程序的设计与实现[论文+源码]
    设计题目:沈阳工业大学助农扶贫微信小程序的设计与实现摘要由于APP软件在开发以及运营上面所需成本较高,而用户手机需要安装各种APP软件,因此占用用户过多的手机存储空间,导致用户手机运行缓慢,体验度比较差,进而导致用户会卸载非必要的APP,倒逼管理者必须改变运营策略。随着微信小......
  • 实现在一个字符串中查找另一个字符串的功能
    题目:实现字符串函数strstr的功能,在一个字符串中查找另一个字符串,若查找到则返回子串第一次出现的地址,若查找不到返回NULL。解题思路:在一个字符串(假设为str1)中查找另一个字符串(假设为str2)。思路是遍历整个字符串str1,如果出现与字符串str2首字母相同的字母,那么我从这个位置(注......
  • 独立开发者如何利用AI实现高收入
    引言在探索独立开发领域时,AI技术的出现为开发者打开了新世界的大门。本文将分享如何利用AI技术提高开发效率,实现更高的收入。AI在编程中的应用AI技术的快速发展为独立开发者带来了前所未有的机遇。通过使用AI,我们可以:加速编程过程利用AI模型,如ChatGPT,我们可以快速生......
  • 基于卷积神经⽹络(CNN)实现垃圾分类Matlab
    源码⼀、垃圾分类如何通过垃圾分类管理,最⼤限度地实现垃圾资源利⽤,减少垃圾处置量,改善⽣存环境质量,是当前世界各国共同关注的迫切问题之⼀。根据国家制定的统⼀标准,现在⽣活垃圾被⼴泛分为四类,分别是可回收物、餐厨垃圾、有害垃圾和其他垃圾。可回收物表⽰适宜回收和资源利⽤......