【栈与队列】——栈的实现及应用

news/2024/11/26 2:25:29/

目录

  • 概念
  • 栈的实现
    • 初始化栈
    • 入栈
    • 出栈
    • 获取栈顶元素
    • 获取栈中有效元素个数
    • 判断栈是否为空
    • 栈的销毁
  • 栈的应用

概念

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

压栈

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

出栈

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

总结起来,即栈是一种特殊的线性表,数据的插入以及删除操作都在栈顶,遵循后进先出的原则,即后进来的元素在进行出栈时先于早进来的元素。

在这里插入图片描述

栈的实现

这里我们发现,实现栈的话,用单链表或者数组都可以,单链表的头插与头删就满足后进先出,而数组即我们前面写过的顺序表,数组的尾插与尾删也满足后进先出的原则。这两种实现方式的时间复杂度都为O(1),并无什么差别,这里我们就用顺序表(数组)来实现。

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{STDataType* _a;int _top;		// 栈顶int _capacity;  // 容量 
}Stack;

初始化栈

首先是初始化,这里我们先初始化为一个容量为4的栈,

// 初始化栈 
void StackInit(Stack* ps)
{assert(ps);ps->_a = (STDataType*)malloc(sizeof(STDataType)*4);ps->_top = 0;//栈顶元素的下一个ps->_capacity = 4;//栈的初始容量为4
}

这里需要注意的是,我们用_top来表示栈顶,_top== 0 和 top == -1是两个概念, 等于0时表示的是栈顶的下一个,等于-1时才表示栈顶。如下图:
_top=-1
在这里插入图片描述
_top=0
在这里插入图片描述
注意两者区别即可。

入栈

接下来是入栈,这里我们由于是写的一个能动态增长的栈,所以我们再进行入元素之前,要先判断这个栈是否满了,如果满了的话,就要进行扩容。

// 入栈 
void StackPush(Stack* ps, STDataType data)
{assert(ps);//判断增容//_top==_capacity说明已经满了,要进行扩容if (ps->_top == ps->_capacity){//容量调整为原来的二倍STDataType* tmp = realloc(ps->_a, 2 * ps->_capacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail");exit(-1);}ps->_a = tmp;ps->_capacity *= 2;//记得更新_capacity}//接下来才是入栈,先入元素,_top再++。(假如_top初始化是-1的话,要先++,再入元素)ps->_a[ps->_top] = data;ps->_top++;
}

出栈

出栈的实现也很简单,就相当于数组尾删,直接_top- - 即可,不过要注意的是,假如栈是个空栈,就不能进行出栈操作了。这里我们用assert进行判断

// 出栈 
void StackPop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));//判断是否为空ps->_top--;
}

获取栈顶元素

这里需要注意的是,由于我们一开始初始化的_top=0,它表示的是栈顶的下一个元素,原因上面也已经解释过了,所以我们获取栈顶元素时的下标实际为_top-1,不过注意空栈是不能获取栈顶元素的。

// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));return ps->_a[ps->_top - 1];
}

获取栈中有效元素个数

我们的_top其实就表示了我们的元素个数,因为我们是从0开始的,进一个元素之前就++了,假如是从-1开始的话,有效元素个数应为_top+1个。

// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{assert(ps);return ps->_top;
}

判断栈是否为空

空栈就是_top等于0的情况下。

// 检测栈是否为空,如果为空返回true,否则false
bool StackEmpty(Stack* ps)
{assert(ps);return ps->_top == 0;
}

栈的销毁

释放malloc开辟的空间后,将所有元素置空置0

// 销毁栈 
void StackDestroy(Stack* ps)
{assert(ps);free(ps->_a);ps->_capacity = ps->_top = 0;ps->_a = NULL;
}

栈的应用

有人可能会问,这个东西能干嘛啊,其实它的作用也很大的,就比如后面要学的一些知识,比如二叉树的层序遍历、快排的非递归实现等等,都会用得到,这里我们拿一道力扣上的题来举个应用例子。
题目如下:

在这里插入图片描述
对于该题,以我们的水平,假如不知道栈的话,做这个可能有点小麻烦,可能有人说用双指针遍历等等,其实都很难解决,但是用栈的特点,就很好做了。

我们直接左括号入栈,当遇到右括号时出栈进行匹配,这里就用到了后进先出的特点完美解决问题。如下:

在这里插入图片描述
代码实现:(由于这里我们是用C语言写的,不像C++等语言可以直接调用库来使用,所以该题我们要自己创造出一个栈,就用我们上面写的,同时由于是字符串类型,所以记得将typedef int 改为char类型…)

这里由于为了不再重复的占用字数,我只将实现的代码写在下面,上面栈的实现直接拷贝到该函数上面即可。

void StackDestroy(Stack* ps)
{assert(ps);free(ps->_a);ps->_capacity = ps->_top = 0;ps->_a = NULL;
}
bool isValid(char * s){Stack st;//初始化栈StackInit(&st);while(*s){//左括号入栈if(*s == '[' || *s=='('|| *s== '{'){StackPush(&st,*s);s++;}else{//假如没有左括号,空栈if(StackEmpty(&st)){return false;}//top存放栈顶元素char top=StackTop(&st);StackPop(&st);//出栈//进行对比if((*s==']'&& top !='[')||(*s==')'&& top !='(')||(*s=='}'&& top !='{')){StackDestroy(&st);return false;}else{s++;}}}//栈不为空bool ret=StackEmpty(&st);StackDestroy(&st);return ret;
}

http://www.ppmy.cn/news/6047.html

相关文章

Java守护线程简述

Java守护线程简述前言前置知识线程JVM退出代码测试查看子线程是否继承父线程的类型守护线程在程序退出时的表现普通线程在程序退出时的表现总结前言 最近再看《Java并发编程实战》,正好有一小节关于守护线程的知识,这里做一点小总结。 前置知识 这里只…

圣诞节学算法---线段树

线段树 快到圣诞节了,圣诞树是不是很漂亮?今天我们就来学习一下它的近亲的线段树 (话说这两玩意好像除了读音相似没啥关系) 引入 例题 1 给定一个数组 aaa 求数组中下标为l−rl - rl−r元素的和 看到这题大家都很容易想到用前缀和以O(n)O(n)O(n)预处…

聊聊首次使用航顺HK32F030C8T6的体验

先说结论,项目基本上开发测试完成了,mcu运行正常。 这个项目是一个智能家居的项目,主板和副板都使用了HK32F030C8T6,这也是笔者第一次使用航顺的芯片。 关于这个芯片的资料,从官网只能下载到datasheet和user mannal的pdf文档&am…

IO多路复用实现方式

IO分类 NIO NIO即同步非阻塞IO。非阻塞的recvfrom系统调用之后,进程并没有被阻塞,内核马上返回进程,如果数据还没准备好,此时会返回一个error。进程在返回之后,可以干点别的事情,然后再发起recvfrom系统调…

硬盘 / 硬盘控制器主要端口寄存器 / Controller Register

文章目录IDE 与 SATA硬盘分区表结构硬盘控制器主要端口寄存器data 寄存器Error && FeaturesErrorFeaturesSector countLBA low | mid | highdevice 寄存器StatusCommandIDE 与 SATA 很久以前,硬盘控制器和硬盘是分开的,后面开发了一个新接口&am…

【小程序】案例 - 本地生活(首页)

1. 首页效果以及实现步骤 新建项目并梳理项目结构 配置导航栏效果 配置 tabBar 效果 实现轮播图效果 实现九宫格效果 实现图片布局 2. 接口地址 获取轮播图数据列表的接口 【GET】 https://www.escook.cn/slides 获取九宫格数据列表的接口 【GET】 https://www.esco…

C++内存管理

内存管理 c:malloc、calloc、realloc、free c:new(不会初始化)、delete 内存管理方式 对于内置类型 //申请和释放单个元素的空间,使用new和delete操作符 int* p1 new int;//申请1个int类型的空间 delete p1;int*…

jenkins pipeline 指定执行节点

第一种写法: pipeline { agent { label “slave-hw” } stages { stage(‘执行更新程序包’) { steps { sh ‘cd /apps/nedy/nedy/csctbb/HWCLOUD ; sh test.sh’ } } stage(‘是否继续’) { steps { input message: ‘确认继续吗?’, ok: ‘确认’ } } …