栈和队列(数据结构)

【数据结构|C语言版】栈和队列 各位小伙伴大家好!上次小编给大家讲解了数据结构中的重要基础:算法效率和复杂度分析,接下来我们讲解一下栈和队列!【知识框架】以上就是小编对栈和队列的讲解。如果觉得小编讲的还可以,还请一键三连。互三必回!持续更新中~! 阅读详情
栈

今天再来看一种数据结构,叫做栈

1、概念及其结构

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

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶

出栈:栈的删除操作叫做出栈。出数据也在栈顶。

2、栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构更优一点。因为数组在尾上插入数据的代价比较小

 书归正传,我们先写一下栈的简单结构和它的功能

先写一个栈的初始化

当然少不了删除了

这里我们将top初始化为0,所以就是先插入数据然后top++

如果我们将top定义成了-1,那么在插入数据的时候就是top++,然后再插入数据

我们再写一个判断栈是否为空的功能

我们写了栈的插入那么就要写栈的删除

在写一个找出top位置的功能

 好了,这下我们来看队列

队列
1、概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特性

入队列:进行插入操作的的一端称为队尾

出队列:进行删除操作的一端称为队头

由于在队头出数据需要挪动,效率比较低,所以队列用链表来实现比较好一点,用最简单的单链表就行

先写好结构

2、队列的实现

由于队列是队尾入数据,队头出数据故我们需要两个指针分别指向队尾和队头

我们就用一个结构体来存储这两个指针

接下来就是老生常谈的初始化

还有删除整个队列

当然少不了插入数据

还有删除单个队列数据

接着是取队头数据和队尾数据

最后是判断是否为空和计算大小

栈和队列OJ
括号匹配问题

 有效的括号 - 力扣(LeetCode)

这道题就是让你查看给的括号到底能不能都匹配在一起,这道题我们用栈做最靠谱了。

思路是左括号入栈,右括号出栈,栈顶元素进行匹配

这里就是左括号入栈

然后是右括号出栈

也会有特殊例子,就像只有一个左括号,那么只入栈就结束了,这时我们就要加一个条件判断栈是否为空,如果栈为空,那么说明栈出完了,那就算正常结束,如果栈不为空那就是flase

还有一种情况,就是只给了一个右括号,那么就没有入栈,但是程序还会找栈的top,这时也会报错,那我们就要在写一个判断语句

最后这才算大功告成 

typedef char STDataType;

typedef struct Stack

{

   STDataType* a;

    int capacity;

    int top;//初始值为0,表示栈顶元素写一个位置的下标

}ST;

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

void StackDestroy(ST* ps);//删除整个栈的数据

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

void StackPop(ST* ps);//删除栈的一个数据

STDataType StackTop(ST* ps);//找出top位置

bool StackEmpty(ST* ps);//判断栈是否为空

int StackSize(ST* ps);//栈的大小

//初始化

void StackInit(ST* ps)

{

    assert(ps);

    ps->a = (STDataType*)malloc(sizeof(STDataType) * 4);

    if (ps->a == NULL)

   {

        perror("malloc fail");

        exit(-1);

   }

    ps->capacity = 4;

    ps->top = 0;

}

//删除

void StackDestroy(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->capacity == ps->top)

   {

        STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * sizeof(STDataType) * 2);

        if (tmp == NULL)

       {

            perror("realloc fail");

            exit(-1);

       }

        ps->a = tmp;

        ps->capacity *= 2;

   }

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

    ps->top++;

}

//判断栈是否为空

bool StackEmpty(ST* ps)

{

    assert(ps);

    return ps->top == NULL;

}



//删除栈的一个数据

void StackPop(ST* ps)

{

    assert(ps);

    assert(!StackEmpty(ps));

    ps->top--;

}

//找出top位置

STDataType StackTop(ST* ps)

{

    assert(ps);

    assert(!StackEmpty(ps));

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

}

 //栈的大小

int StackSize(ST* ps)

{

    assert(ps);

    return ps->top;

}



bool isValid(char * s){

ST st;

StackInit(&st);

while(*s)

{

    if(*s=='['||*s=='{'||*s=='(')

   {

        StackPush(&st,*s);

       ++s;

   }

    else

   {

        if(StackEmpty(&st))

       {

            StackDestroy(&st);

            return false;

       }

        char top=StackTop(&st);

        StackPop(&st);

        if((*s== ']' && top !='[')

        ||(*s=='}' && top !='{')

        ||(*s==')' && top !='('))

       {

            StackDestroy(&st);

            return false;

       }

        else

       {

           ++s;

       }

   }

}

    bool ret = StackEmpty(&st);

    return ret;

}
用队列实现栈

 用队列实现栈 - 力扣(LeetCode)

这道题是用队列实现栈,队列是先进先出,而栈是后进先出

这里push进来1234,如果是栈pop的话就是1 2 3 4依次出来,但队列恰好相反,是4 3 2 1

题目的条件是用两个队列来实现,我们就可以让有数据的那个队列先出数据,出到size剩了一个了就刚好了pop队尾的数据

大思路就是:保持一个队列存数据,一个队列为空,出数据时,倒一下

首先,先定义两个队列,来实现上面所说的功能,也可以定义成指针,不过后面就还需要用malloc

题目第一个要求的功能就是Creat函数,意思就是没有结构体,这就需要我们创建加初始化

这里我们定义一个obj来指向malloc

然后把q1和q2传给初始化函数,最后返回obj

下面就是push函数,按上面的思路,就是保持一个为空,一个不为空,哪个队列不为空就向那个队列插,或者都为空,那就随便插了

接着是pop函数,这里pop函数的要求不仅是删除一个数据,还要返回栈顶的元素,这里我们的思路是找到为空的队列和不为空的,然后将非空的队列里的数据转移到空的队列中,最后再返回栈顶元素

下一个函数是求栈顶元素的,倒也能用队列来回倒,但我们刚好有个求队尾元素的函数 

我们直接调用,这个省了不少事

最后两个一个是判空,另一个是释放队列

值得一提的是,我们如果只释放obj,创造出来的两个q1和q2就并没有被删除,这时就造成了内存泄漏

用栈实现队列

 用栈实现队列 - 力扣(LeetCode)

思路:

我们来看看上一道题的兄弟题目,用栈实现队列

栈是后进先出,而队列是先进先出

删除的思路也是倒一下,把有数据的栈里的数据全部倒到空栈里,剩一个数据的时候就删除,这样就把栈底的数据删了

当我们入数据的时候,因为现在有数据的栈里是234,我们入一个5,顺序就乱了,要想按顺序入数据就还得倒到另一个栈里再入。

但是这样太麻烦了,我们换一种思路,将原先有数据的栈就叫做出栈,为空的栈就叫做入栈,这样也是按顺序存储,出栈时也是哪顺序出,直到右边为空,再想出的时候就再倒。

typedef int STDatatype;
typedef struct Stack
{
  STDatatype* a;
  int capacity;
  int top;   // 初始为0,表示栈顶位置下一个位置下标
}ST;

void StackInit(ST* ps);
void StackDestroy(ST* ps);
void StackPush(ST* ps, STDatatype x);
void StackPop(ST* ps);
STDatatype StackTop(ST* ps);

bool StackEmpty(ST* ps);
int StackSize(ST* ps);


void StackInit(ST* ps)
{
  assert(ps);

  //ps->a = NULL;
  //ps->top = 0;
  //ps->capacity = 0;

  ps->a = (STDatatype*)malloc(sizeof(STDatatype)*4);
  if (ps->a == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }

  ps->top = 0;
  ps->capacity = 4;
}

void StackDestroy(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)
    {
      perror("realloc fail");
      exit(-1);
    }

    ps->a = tmp;
    ps->capacity *= 2;
  }

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

// 20:20继续
void StackPop(ST* ps)
{
  assert(ps);
  assert(!StackEmpty(ps));

  ps->top--;
}

STDatatype StackTop(ST* ps)
{
  assert(ps);
  assert(!StackEmpty(ps));

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

bool StackEmpty(ST* ps)
{
  assert(ps);
  return ps->top == 0;
}

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

  return ps->top;
}


typedef struct {
ST popset;
ST pushset;
} MyQueue;

bool myQueueEmpty(MyQueue* obj);
MyQueue* myQueueCreate() {
MyQueue*pq=(MyQueue*)malloc(sizeof(MyQueue));
StackInit(&pq->popset);
StackInit(&pq->pushset);
return pq;
}

void myQueuePush(MyQueue* obj, int x) {
  assert(obj);
StackPush(&obj->pushset,x);
}


int myQueuePop(MyQueue* obj) {
assert(obj);
assert(!myQueueEmpty(obj));

int k=myQueuePeek(obj);
StackPop(&obj->popset);
return k;
}

int myQueuePeek(MyQueue* obj) {
assert(obj);
assert(!myQueueEmpty(obj));
if(StackEmpty(&obj->popset))
{
    while(!StackEmpty(&obj->pushset))
    {
         StackPush(&obj->popset,StackTop(&obj->pushset));
         StackPop(&obj->pushset);
    }
}
return StackTop(&obj->popset);
}

bool myQueueEmpty(MyQueue* obj) {
  assert(obj);
return StackEmpty(&obj->popset)&&StackEmpty(&obj->pushset);
}

void myQueueFree(MyQueue* obj) {
  assert(obj);
StackDestroy(&obj->popset);
StackDestroy(&obj->pushset);
free(obj);
}

设计循环队列

 设计循环队列 - 力扣(LeetCode)

来看看这个循环队列

这里我们经过权衡利弊,最终选择使用数组实现

至于怎么判空呢,当rear等于front的时候队列就是空的,判满则是用(rear+1)%(k+1)==front判断

这里我们先写一下前面的代码,先malloc出一个结构体来,在malloc出数组,注意这里要多开一个空间,方便判断

然后再顺便把判空和判满写了

下面来看看插入

我们首先进行一个判满,当不满时,rear先插入,再++,但是会有特殊情况

这种情况下rear++就会越界,我们想让他再从0开始,就可以写一个if语句,给他赋值为0。或者是%=一下k+1

然后是出数据,正常情况就是front++,等走到最后一个的时候就%=一下(k+1)

接着是取头,而且题目有规定,如果队列为空要返回-1

然后是取头,也是规定为空返回-1,这个也有特殊情况

最后就可以这样写

当然,还有一种更优思路,不过一般人不太能想到

如果画图,说不定就能想到

最后是释放,也是两层释放

typedef struct {
int*a;//数组
int front;//头
int rear;//尾
int k;//数组的大小
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //多开一个解决判满的问题
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->front=obj->rear=0;
    //这里的k是放满的时候能存储的最大数据个数,空间大小应该是k+1
    obj->k=k;
    return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
assert(obj);
return obj->rear==obj->front;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    assert(obj);
    return ((obj->rear+1)%(obj->k+1))==obj->front;
    }
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
assert(obj);

if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->rear++]=value;
obj->rear%=(obj->k+1);
return true;
}

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

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

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[(obj->rear+obj->k)%(obj->k+1)];
}



void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
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);
*/

Rsoft仿真实战:长周期光纤光栅参数优化与模式耦合分析 本文详细介绍了使用Rsoft软件进行长周期光纤光栅(LPFG)参数优化与模式耦合分析的实战方法。通过Rsoft仿真,可以高效优化光栅周期、包层厚度等关键参数,深入分析纤芯模与包层模的耦合特性,为光纤通信和传感应用提供可靠的设计依据。 阅读详情

相关推荐

LabVIEW内存泄漏诊断与优化:从报错到高效运行

本文深入探讨LabVIEW内存泄漏的诊断与优化方法,从理解内存泄漏本质到使用专业工具链进行问题定位,再到高频场景的解决方案和高级优化策略。通过实战案例展示如何解决工业数据采集系统中的内存不足问题,帮助开发者提升LabVIEW应用的稳定性和效率。

moon9的博客 197

数据结构与算法——栈和队列

栈和队列是两种重要的线性结构,本篇文章详细总结了数据结构与算法中栈和队列的终点知识,并且结合代码片段实现其基本操作,干货满满,每一句话都是重点,希望能给更多的小伙伴在学习数据结构与算法时带来一定帮助...

小田是个程序员的博客欢迎你的到来呀~ 2万+

嵌入式学习(29)-ASM330LHH驱动程序

ASM330LHH是一种系统级封装器件,具有3D数字加速度计和3D数字陀螺仪,扩展温度范围上限为+105 °C,适用于汽车非安全领域的应用。ASM330LHH 的满量程加速范围为±2 g、±4 g、±8 g 和 ±16 g,角速度范围扩展至 ±125 dps、±250 dps、±500 dps、±1000 dps、± 2000 dps 和 ±4000 dps,适合各种汽车应用。

剑藏锋的博客 1175

【数据结构】栈和队列

本文系统介绍了栈和队列两种基础数据结构。栈遵循"先进后出"原则,仅允许在固定端进行插入和删除操作;队列遵循"先进先出"原则,在一端插入,另一端删除。文章详细分析了双端队列和循环队列的实现原理,包括角标移动公式、有效长度计算等核心算法。同时比较了链表和数组两种实现方式的复杂度差异,并展示了Java集合框架中Stack类和Queue接口的具体使用方法。通过限制操作方式,任何线性结构都能实现栈或队列的特性,为程序设计提供了灵活的数据组织工具。

2503_92883549的博客 1181

niushop靶场漏洞查找-文件上传漏洞等(超详细)

发现报错,而且这个报错十分熟悉,与我们所学的sqli报错注入特别像。这里查询到后面的url有且仅有一个,目测估计是后台。上传的图片是包含一句话木马的图片。发现获取到了webshell。实战漏洞-niushop。

qq_59020256的博客 1728

瑞芯微RK3576开发板实战:6TOPS NPU如何加速你的AIoT项目(附性能测试)

本文以瑞芯微RK3576开发板为核心,深入探讨其6TOPS NPU在AIoT项目中的实战应用。通过详细的性能测试对比,展示了NPU如何将AI模型推理速度提升15倍以上,并大幅降低CPU负载。文章涵盖从环境搭建、模型转换优化到多屏异显与系统级调优的全流程,为开发者提供了将硬件算力转化为实际项目加速效果的完整指南。

oauth7security的博客 820

数据结构——栈和队列

可以使用。

2302_80250536的博客 1302

LangChain Agent Skills 实战指南:12个案例玩转智能体技能封装与编排

大语言模型应用开发中,智能体(Agent)的能力扩展正从零散的函数调用走向标准化封装。Agent Skills 将某项具体能力组织为带说明、代码与依赖的技能目录,让模型能自动发现和调度,其核心价值在于解决工具数量膨胀后的维护混乱与调用失控问题。与 Tool 的单一执行、MCP 的协议连接不同,Skill 更强调能力的语义描述与组合复用,配合 LangGraph 的状态编排和 LangSmith 的可观测性,可构建生产级智能体系统。在实际场景中,Agent Skills 可覆盖 RAG 知识库问答、多轮记忆保

weixin_34335458的博客 407

[详解栈和队列]数据结构之栈与队列

✅作者简介:大家好,我是Philosophy7?让我们一起共同进步吧!🏆 📃个人主页:Philosophy7的csdn博客 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍+收藏⭐️+留言📝支持一下博>主哦🤞 文章目录栈和队列1.物理结构和逻辑结构2.什么是栈2.1栈的基本操作3.什么是队列队列的基本操作小结 栈和队列 1.物理结构和逻辑结构 再了解栈和队列之前,我们要先知道什么是逻辑结构和物理结构。 逻辑结构: 按照逻辑结构划分:数据结.

Philosophy7的博客 4195

华为OD机试 - 幸运数字(Java 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中,刷题。

学Java,找哪吒 3776

NL2SQL实践系列(1):深入解析Prompt工程在text2sql中的应用技巧

NL2SQL实践系列(1):深入解析Prompt工程在text2sql中的应用技巧

丨汀、的博客 4327

【数据结构】栈和队列详解

栈和队列详解!!!

2403_86779341的博客 1384

OPPO 后端校招面试,过于简单了!

你好,我是 Guide。2026 届秋招陆续开奖公司的越来越多了,今天来看一家手机厂 OPPO。前几天,已经分享了和今年校招开奖的情况。其中,小红书的薪资最为炸裂,大白菜一年都能给到 51w+。不过,相对应的工作强度和压力也是真的难顶,确实很难有钱多又轻松的活!OV (OPPO 和 Vivo)这几年发展真还可以的,这两家的高端机口碑都不错。本人也是最近入手了一款 Vivo 的上一代旗舰,拍照是真的不错啊!每个月会有 1.2k 生活补贴,生活费是按照 8 % 缴纳。

JavaGuide 750

【数据结构】 栈和队列

数据结构中栈和队列的学习~

qq_56251766的博客 1180

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

数据结构—栈和队列

栈是一种只能在一端进行插入或删除操作的线性表。在表中允许进行插入、删除操作的一端称为栈顶。栈顶的当前位置是动态的,由一个称为栈顶指针的位置指示器来指示。表的另一端称为栈底。当栈中没有数据元素时称为空栈。栈的插入操作通常称为进栈或入栈,栈的删除操作通常称为退栈或出栈。栈的主要特点是"先进后出",即后进栈的元素先出栈。每次进栈的元素都放在原当前栈顶元素之前成为新的栈顶元素,每次出栈的元素,都是原当前栈顶元素。栈也成为后进先出表。

2303_80188300的博客 1850

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

数据结构 栈和队列

数据结构 栈和队列

双吉堡工程专业的一名27届Javaer。 2001

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

【数据结构与算法】之四道关于栈和队列的经典题型!

在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。// 返回 false,队列已满。请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(// 返回 true。// 返回 true。// 返回 true。// 返回 true。// 返回 true。// 返回 true。// 返回 False。

2301_80221228的博客 1177
上一篇: C++内存管理
下一篇: 数组中只出现一次的两个数-位运算
宴之敖者、
博客等级 码龄4年 131粉丝 45原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值