数据结构-----栈

Posted guohaoblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构-----栈相关的知识,希望对你有一定的参考价值。

今天来介绍一下栈。本文先从栈的基本概念和结构入手,接着讲解基本操作,最后通过一个回文数的例子来巩固今天学的栈操作

一、栈的基本概念

栈: 只允许在一端进行插入或删操作的线性表。首先栈是一种线性表,但是限定这种线性表只能在某一段进行插入和删除操作

栈顶:线性表允许进行插入和删除的那一端

栈底:固定的,不允许进行插入和删除的另一端

空栈:不含任何元素的空表

二、栈的操作

队列是FIFO(先进先出),栈是FILO(先进后出 或者 后进先出)

 

最先进栈的元素,是不是就只能是最后出栈呢

答案是不一定,要看什么情况。栈对线性表的插入和删除的位置进行了限制,并没有对元素进出的时间进行限制,也就是说,在不是所有元素都进栈的情况下,进去的元素也可以出栈,只要保证是栈顶元素出栈就可以,那么变化就很多了,举例来说:

如果我们有3个整形数字元素1、2、3依次进栈,会有哪些出栈次序呢?

  • 第一种:1、2、3进,再3、2、1出。这事最简单的最好理解的一种,出栈次序为321
  • 第二种:1进,1出,2进,2出,3进,3出,出栈次序为123
  • 第三种:1进,2进,2出,1出,3进,3出,出栈次序为213
  • 第四种:1进,1出,2进,3进,3出,2出,出栈次序为132
  • 第五种:1进,2进,2出,3进,3出,1出,出栈次序为231

有没有可能是312这样的次序出栈呢,答案是不可能的,因为3先出栈,就意味着3曾经进栈,既然3进栈了,那也意味着1和2已经进栈了,此时,2一定是在1的上面,就是更接近栈顶,那么出栈只可能是321,不然不满足123一次进栈的要求,所以此时不会发生1比2先出栈的情况

栈的基本操作

InitStack(&S):初始化一个空栈S 
StackEmpty(S):判断一个栈是否为空,若栈S为空返回true,否则返回false 
Push(&S,x):进栈,若栈S未满,将x加入使之成为新栈顶 
Pop(&S,&x):出栈,若栈S非空,弹出栈顶元素,并用x返回 
GetTop(S,&x):读栈顶元素,若栈S非空,用x返回栈顶元素 
ClearStack(&S):销毁栈,并释放栈S占用的存储空间。

(符号“&”是C艹特有的,用来宝石引用,有的采用C语言中的指针类型“*”,也可以达到传址的目的)

如果没有特殊要求,可以直接用这些基本的操作函数

三、栈的存储结构

3.1、顺序存储结构

#define MaxSize 50              //定义栈中元素的最大个数
typedef struct{
    Elemtype data[MaxSize];     //存放栈中元素
    int top;                    //栈顶指针
}SqStack;

共享栈

利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一位数据空间,将两个栈的栈底分别设置在共享空间的两端,两个栈向共享空间的中间延伸。

两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=MaxSize时1号栈为空,当且仅当两个栈顶的指针相邻时,判断为占满。当0号栈进栈时top0先加1再复制,1号栈进栈时top1先减1再复制;出栈时刚好相反。

共享栈是为了更有效地利用存储空间,两个栈的空间相互调节,只有在整个存储空间被占满时才发生上溢。其存取数据的时间复杂度均为O(1)所以对存取效率没有什么影响。

事实上使用遮掩的数据结构,通常都是当两个栈的空间需求有相反关系时,也就是一个栈增长时另一个栈在缩短的情况。就像买卖股票一样,你买入时,一定是有一个你不知道的人在做卖出操作。有人赚钱,就一定有人赔钱。这样使用两栈共享空间存储方法再有比较大的意义。

当然,这里指两个具有相同数据类型的栈,类型不同不适用这种方法。

3.2、栈的链式存储结构(链栈)

typedef struct Linknode{
    ElemType data;          //数据域
    struct Linknode *next;  //指针域
} *LinkStack;               //栈类型定义

 

四、实战

以上就是栈的基本介绍,其实主要就记住三点------栈的FILO、栈的顺序存储结构和栈的链式存储结构

下面来,利用栈的操作,判断输入的字符串是否为回文数

(所谓的回文数,就是输入的字符串是否对称),下面采用两种方法判别

4.1、不采用栈来操作(个人第一反应的方法),代码如下

#include<stdio.h>
#include<string.h>
#define N 101

// 判断是否为回文数

int main()
{
    char str[N];
    int i,middle,length,flag;
    
    while(gets(str))
    {        
        length = strlen(str);
        middle = length/2;    // 取中位数
        flag = 1;     // 用0标记不是回文数  1标记回文数 
        for(i=0;i<middle;++i)
        {
            if(str[i]!=str[length-i-1])
            {
                flag = 0;
                break;                
            }
        } 
        if(flag==1)
            printf("YES
");
        else
            printf("NO
");    
    }
    
    return 0;
} 

 

4.2、采用栈(顺序栈)来操作

#include<stdio.h>
#include<string.h>
#define MaxSize 101

// 采用栈来判断回文数

typedef struct{
    char data[MaxSize];    // 存放栈中元素
    int top;               // 栈顶指针 
}Stack;

int main()
{
    Stack s;
    char array[MaxSize];
    int i,middle,len,next;
    
    while(gets(array))
    {
        len = strlen(array);
        middle = len/2;
        s.top = 0;  // 栈初始化
        // 将middle 前的字符依此入栈
        for(i=0;i<middle;++i)
        {
            s.data[++s.top] = array[i];
        }
        
        // 判断字符串的长度是奇数还是偶数,并找出需要进行字符匹配的起始下标         
        if(len%2==0)
            next = middle + 1;
        else
            next = middle + 2;
        
        // 开始匹配
        for(i=next-1;i<len;++i)
        {
            if(array[i]!=s.data[s.top])
                break;
            s.top--;    
        }        
    
        // 如果top的值为0,则说明栈呢所有字符都被一一匹配了
        if(s.top==0)
            printf("YES
");
        else
            printf("NO
");     
    }
    
    return 0;
} 

 

不管是队列还是栈,一般采用顺序存储的较多。就跟上面的例子一样,不采用栈来的更快,更简洁些,代码风格因人而异!本人菜鸟一枚,欢迎拍砖赐教

 






以上是关于数据结构-----栈的主要内容,如果未能解决你的问题,请参考以下文章

方法与对象内存分析

Android Studio - 带回栈的导航抽屉

使用callstack在C中实现栈数据结构?

VSCode自定义代码片段5——HTML元素结构

VSCode自定义代码片段5——HTML元素结构

VSCode自定义代码片段5——HTML元素结构