栈和队列(数据结构)

【数据结构|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);
*/

Python爬虫实战:5分钟搞定东方财富网股票数据抓取(附完整代码) 本文详细介绍了如何利用Python爬虫技术,快速抓取东方财富网的实时股票数据。通过逆向分析其JSONP数据接口,构建了包含请求处理、数据清洗、分页调度及多格式存储的完整自动化采集系统,并提供了应对反爬策略与生产部署的进阶优化思路,附有可直接运行的完整代码。 阅读详情

相关推荐

Android12 车载开发TaskView实现分屏

这里只给出TaskView的代码,相关联的源码,请看源码,如果想要使用TaskView需要自己去编译frameWork的源码,打包一个aar,或者jar出来使用,否则无法直接使用。1.如果分屏的应用如启动两个应用按照权重占比,互相不叠加在一起,点触摸击事件都可响应,例如左边启动QQ了,右边启动的是微信,二者窗口都可操作。TaskView是谷歌整合安卓多任务多窗口下交互重新重构的独立出来的module,本身继承自ServiceView,TaskView的相关概念性的东西参考。//创建的taskview。

android_bird_bird的博客 9551

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

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

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

Vue路由守卫实战:利用token实现跨项目免登录跳转(最新版)

本文详细介绍了如何利用Vue路由守卫和Token验证实现跨项目免登录跳转方案。通过核心架构设计、路由守卫精细化控制及Token交换安全实践,有效解决了多系统跳转中的认证问题,提升用户体验和系统连贯性。

n7o8p的博客 138

【数据结构】栈和队列

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

2503_92883549的博客 1181

STM32L051低功耗模式下LPUART的使用

STM32L0系列低功耗UART配置代码清单及注释,大家可参考学习使用。

嵌入式工程师 2104

地平线Matrix-Client实战:3分钟搞定摄像头调试与3D视图配置(附避坑指南)

本文详细介绍了地平线Matrix-Client在摄像头调试与3D视图配置中的实战技巧,包括环境准备、核心功能快速上手、高阶调试与性能优化等。通过3分钟快速配置指南和避坑建议,帮助开发者高效完成智能视觉系统开发,特别适用于自动驾驶和机器人视觉领域。

w7x8y9z的博客 267

数据结构——栈和队列

可以使用。

2302_80250536的博客 1302

TS808效果器制作教程

TS808效果器制作教程 可以在立创商城购买到大部分的电阻电容和IC,音频接口,踩钉,电位器和电源接口可以去某宝看看都买得到的。链接https//pan.baidu.com/s/1QId1eIE3fJ2u5NLBSISvGA。安装过程过于繁琐,把所有元器件按照图纸焊接完成就可以调试了,祝你们成功!这是TS808经典效果器制作过程的记录。IC是JRC4558。...

qq_18948149的博客 3427

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

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

Philosophy7的博客 4195

Word论文插入带有方括号序号的参考文献——尾注+交叉引用

文章目录1. 尾注功能2. 为参考文献的序号添加方括号3. 调整参考文献前的序号大小4. 重复引用同一参考文献5. 将交叉引用的序号改为带有方括号的上标序号格式6. 删除参考文献前的尾注横线 1. 尾注功能 首先使用尾注功能顺序插入参考文献,暂时不考虑方括号问题,具体操作如下: ① 将光标放在需要插入参考文献角标的地方 ② 在word菜单栏,“引用-脚注部分右下角箭头”(用来设置尾注格式) ③如...

Your_Raymond的博客 9万+

Data+AI━━数仓建模的智能进化之路!

Data+AI━━数仓建模的智能进化之路!

ith321的博客 1745

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

栈和队列详解!!!

2403_86779341的博客 1384

YOLOv8【第十一章:视频追踪与流处理篇·第16节】一文搞懂,DeepStream 部署入门:NVIDIA 视频分析 SDK 与 YOLOv8 结合!

🏆 本文收录于 《YOLOv8实战:从入门到深度优化》 专栏。该专栏系统复现并梳理全网各类 YOLOv8 改进与实战案例(当前已覆盖分类 / 检测 / 分割 / 追踪 / 关键点 / OBB 检测等方向),坚持持续更新 + 深度解析,质量分长期稳定在 97 分以上,可视为当前市面上 覆盖较全、更新较快、实战导向极强 的 YOLO 改进系列内容之一。

**My Coding Family** 839

【数据结构】 栈和队列

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

qq_56251766的博客 1180

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

数据结构—栈和队列

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

2303_80188300的博客 1850

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

数据结构 栈和队列

数据结构 栈和队列

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

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

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

在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。// 返回 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、付费专栏及课程。

余额充值