当前位置:   article > 正文

【数据结构之栈系列】栈及其经典面试题详解_栈试题分类

栈试题分类

前言

前面学习的线性表中包含顺序表和链表,这两种数据结构允许在任意位置进行插入和删除,那么有没有一种数据结构是不能在任意位置进行插入删除,而只允许在一边进行插入删除的呢??当然有的,这就是我们今天要学习的一种新的数据结构:栈

一、栈的介绍

栈是一种只允许在一端进行插入删除的数据结构,插入删除的一端叫做栈顶,不能进行插入删除的一端叫做栈底。

  • 入栈:指在栈进行插入数据的操作。
  • 出栈:指删除栈中的栈顶元素的操作。
    由于栈的特殊性,所以栈的元素的出入会满足后进先出的原则。

二、数据类型重定义

通常情况下,为了能够方便的修改数据结构中存放的数据类型,我们会对数据类型进行重定义
在这里插入图片描述

三、栈的结构

在学习任何的数据结构的时候,最重要的首先是要了解这个数据结构的结构,既然栈是一种数据结构,那么就说明栈可以用来存储数据,所以栈的结构中一定包含一个数据域,由于栈需要不断对栈顶元素进行出栈,所以需要一个标记栈顶元素的指针,这个指针是一个数组下标,也就是一个整数,综合这两种,我们觉得数据域设置成数组能够方便的处理这个问题。由于是数组,如果是静态数组,就导致数组的容量不能变,容量太小,会导致不够,容量太大,导致空间浪费。因此,我们觉得动态数组(能够进行扩容的数组)能够方便处理以上问题。动态数组就涉及到扩容的问题,所以肯定需要一个变量来记录数组中的容量。
在这里插入图片描述
在上述的结构中,val表示栈的数据域,是一个动态的数组,top表示栈顶指针,能够标识栈顶元素的情况(可以有两种表示方法),capacity表示栈中的容量大小
这里需要注意一个点:top能够表示栈顶元素的情况,是数组的下标,其初始状态可以是两种表示方法

  • 第一种:top = 0,表示的是栈顶元素的下一个位置,即入栈时新元素插入的位置
  • 第二种:top = -1,表示的是栈顶元素的位置,当栈为空的时候,显然没有栈顶元素,所以此时top = -1。
    通常情况下,为了方便表示,我们会对定义的数据结构的类型进行重定义
    在这里插入图片描述

四、栈中的常见操作

学习数据结构的另一个核心就是学习如何对这些数据结构进行操作

1. 常见函数接口的声明

在这里插入图片描述
在上面的声明中,我们发现函数参数中传的是栈的结构体地址,这是为啥呢?
我们知道栈的结构本质是一个结构体,而结构体一般都是很大的,同时,我们在函数体中有时可能需要对栈中的内容进行修改,比如入栈和出栈,就需要改变栈中的内容,如果传的是栈的结构体,那么在函数的形参中我们知道是一份实参的拷贝,所以对形参的改变是不会影响实参的,那么我们考虑传结构体的地址,通过结构体的地址,函数就可以通过指针找到真正想改变的内容
因此,传结构体的地址的好处有两个:

  • 节省形参的空间
  • 能够找到真正想改变的实参,从而改变实参的内容

2. 常见函数接口的实现

(1)栈的初始化

![在这里插入图片描述](https://img-blog.csdnimg.cn/eaf41fe1eec14806a680bc01cba32f72.pn

初始化函数是根据数据结构中的结构来实现的,我们知道栈中包含一个动态数组的指针和一个栈顶指针(数组下标)和一个容量,动态数组刚开始啥数据都没有,我们只需要对其置成空指针即可,防止出现野指针,栈顶指针指向的是栈顶元素在数组中的下标的下一个位置,即新元素入栈时所在的位置,当栈为空的时候,栈顶指针指向0,刚开始的容量就是0

(2)销毁栈

在这里插入图片描述

销毁栈函数主要是完成对栈中申请的空间进行释放,注意在我们不想要使用这个栈的时候,一定要记得对栈进行销毁,本质就是对栈中开辟的数组进行释放,防止出现内存泄露。

(3)入栈

在这里插入图片描述
入栈时一定要考虑栈是否已经满了,如果已经满了,则需要进行扩容,扩容时在确定新容量的时候一定要注意原先栈中的容量是否为0(初始化状态),这种情况需要进行特殊处理。扩容完成之后就是将数据插入栈顶,因为top表示的是栈顶元素的下一个位置,所以是先将元素入栈到top位置,再将top++。

(4)出栈

在这里插入图片描述
出栈的本质就是删除栈顶元素,在出栈的时候一定要注意栈是否为空,当st->top>0时才可以进行删除数据,删除的时候并不是真的将该数据从内存中移除,而是将栈顶指针往前移一位即可。栈中的top其实标识也可以是栈中的有效数据,因为刚开始栈为空的时候,top为0,标识没有数据,每次在入栈的时候,插入数据之后都会对top进行++操作,每次删除栈顶元素的时候,都会对top进行–的操作,所以栈中的top可以标识栈中的有效数据个数。

(5)返回栈顶元素

在这里插入图片描述
返回栈顶元素函数中,也要注意栈是否为空,如果栈为空,无法返回栈顶元素,这里需要注意,因为top表示的是栈顶元素的下一个位置,所以返回的是top的前一个位置的值,而不是返回top位置的值。这个函数在遍历访问栈的时候是很重要的。

(6)判空

在这里插入图片描述
当栈顶指针指向0的时候表示现在栈是空的状态,即初始化状态。这个函数在遍历访问栈的时候也是很重要的。

(7)栈的大小

在这里插入图片描述
当栈为空的时候,top = 0,每次入栈的时候,top++,出栈的时候top–,所以top可以很好地表示栈的数据个数

(8)栈的容量

在这里插入图片描述


在上面的函数定义中,我们发现每一个函数都需要对栈的结构体指针进行断言操作,这是为啥?
在函数的操作中,我们知道函数中需要通过栈的结构体指针找到栈中的内容,也就是需要对栈的结构体指针进行解引用,因此,这个结构体指针是一定不能为空的,否则就会出现空指针的解引用从而使程序崩溃。

五、测试栈

使用栈的时候,一定需要注意几点:首先是创建栈的结构体,接下来,通过这个栈的结构体的地址对栈中的内容进行处理:处理首先是对栈进行初始化,就是可以插入和删除数据,当栈中拥有一定的数据的时候,这个时候我们可以遍历访问栈中的数据,这个通常需要使用的函数有判空函数,返回栈顶元素函数,出栈函数,当我们使用完栈之后,不想再使用的时候需要对栈进行销毁,防止出现内存泄露
1.
在这里插入图片描述
从上面的测试代码中,我们要学习栈的基本使用:先定义一个栈的结构体,再对这个栈进行初始化操作,后面再进行各种常见的操作。除此之外,我们还需要学习栈的遍历思路:需要一个循环来解决,当栈不为空时,先访问栈的栈顶元素,再出栈。
2.
在这里插入图片描述

六、栈的常见面试题

  1. 括号匹配:有效的括号
    题目:
    在这里插入图片描述

提交代码:

bool isValid(char * s){
    // 需要一个栈
    // 创建并初始化栈
    Stack st;
    StackInit(&st);
    // 正常使用栈
    while(*s)
    {
        if(*s == '('||*s == '{'||*s == '[')
        {
            // 左括号,入栈
            StackPush(&st,*s);
        }
        else
        {
            // 右括号:出栈
            // 出栈时需要判断此时栈是否为空,如果栈为空,则匹配失败
            if(StackEmpty(&st))
            {
            // 此时徐奥入栈,但是栈为空,所以匹配失败
                return false;
            }
            else
            {
                // 栈不为空,出栈顶元素进行匹配,匹配失败,则返回false;
                char retTop = StackTop(&st);
                StackPop(&st);
                if(*s == ')'&&retTop!='('||*s == '}'&&retTop!='{'||*s == ']'&&retTop!='[')
                {
                // 当出栈的括号和遍历到的右括号不匹配的时候,匹配失败
                    return false;
                }
               
            }
        }
         s++;
    }
    return StackEmpty(&st);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

提交结果:
在这里插入图片描述

思路分析:
本题中采用构造一个栈来解决问题,遍历给定的字符串,遍历到的括号可能是左括号也可能是右括号。当遇到左括号时,直接入栈,当遇到右括号时,出栈顶元素和此时的右括号进行匹配,此时栈中可能为空也可能不为空,如果栈此时为空,则无法匹配,返回false。如果栈不为空,则出栈顶元素进行匹配,如果匹配,继续向后遍历字符串,如果不匹配,则此时返回false。当遍历完字符串之后,需要检查此时栈是否为空,因为可能会出现栈中还有多余的左括号的情况,如果栈为空,则成功匹配,返回true,如果栈不为空,则匹配失败,返回false。

综合上面的处理,我们可以知道,题目给定的字符串可能出现的情况:

  • 为空
  • 正常的情况,匹配成功的
  • 遇到第一个就是右括号,匹配失败
  • 遇到第一个是左括号,还没发确定,只能先入栈
  • 最后一个是左括号的时候,一顶入栈的,此时就会导致栈中不为空,所以显然匹配失败
  • 最后一个是右括号的时候,可能匹配成功,也可能匹配失败,这个时候需要看匹配完成之后栈是否为空,如果栈为空,则成功,栈不为空,则失败。
    所以一般我们对一串数据进行分析的时候,一般需要考虑的问题,是否为空,常规情况(中间位置),第一个位置,最后一个位置。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/556874
推荐阅读
相关标签
  

闽ICP备14008679号