首页 > 其他分享 >【数据结构】栈

【数据结构】栈

时间:2023-04-30 23:47:38浏览次数:37  
标签:ElemType top 栈顶 Pop base stackSize 数据结构

1  前言

这节我们来看看计算机中的常见的栈结构。

2  栈定义

栈是一个后进先出(Last In Fist Out, LIFO)的线性表,它要求只在表尾进行删除和插入操作。

所谓的栈,其实就是一个特殊的线性表(顺序表、链表),但是它在操作上有一些特殊的要求和限制:

  • 栈的元素必须“后进先出”
  • 栈的操作只能在这个线性表的表尾进行。
  • 对于栈来说,这个表尾称为栈的栈顶(top),相应的表头称为栈底(bottom)。

栈的插入和删除操作:

栈的插入操作(Push),叫做进栈,也称为压栈,入栈。

栈的删除操作(Pop),叫做出栈,也称为弹栈。

因为栈的本质是一个线性表,线性表有两种存储形式,那么栈也分为栈的顺序存储结构和栈的连式存储结构。

3  栈的顺序存储结构

我们看下顺序型的栈:

最开始栈中不包含任何数据,叫做空栈,此时栈顶就是栈底。然后数据从栈顶进入,栈顶栈底分离,整个栈的当前容量变大。数据出栈时从栈顶弹出,栈顶下移,整个栈的当前容量变小。

3.1  数据结构定义

typedef struct
{
    ElemType *base;
    ElemType *top;
    int stackSize;
}sqStack;

这里定义了一个顺序存储的栈,它包含了三个元素:base,top,stackSize。其中base是指向栈底的指针变量,top是指向栈顶的指针变量,stackSize指示栈的当前可使用的最大容量。

3.2  创建一个栈

#define STACK_INIT_SIZE 100
initStack(sqStack *s)
{
  s->base = (ElemType *)malloc( STACK_INIT_SIZE * sizeof(ElemType) );
  if( !s->base )
    exit(0);
  s->top = s->base; // 最开始,栈顶就是栈底
  s->stackSize = STACK_INIT_SIZE;
}

3.3  入栈操作

  • 入栈操作又叫压栈操作,就是向栈中存放数据。

  • 入栈操作要在栈顶进行,每次向栈中压入一个数据,top指针就要+1,直到栈满为止
#define SATCKINCREMENT 10
Push(sqStack *s, ElemType e)
{
    // 如果栈满,追加空间
    if( s->top – s->base >= s->stackSize )
    {
        s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));
        if( !s->base )
            exit(0);

        s->top = s->base + s->stackSize; // 设置栈顶
        s->stackSize = s->stackSize + STACKINCREMENT; // 设置栈的最大容量
    }

    *(s->top) = e;
    s->top++;
}

3.4  出栈操作

  • 出栈操作就是在栈顶取出数据,栈顶指针随之下移的操作。

  • 每当从栈内弹出一个数据,栈的当前容量就-1
Pop(sqStack *s, ElemType *e)
{
  if( s->top == s->base ) // 栈已空空是也
    return;
  *e = *--(s->top);
}

3.5  清空一个栈

  • 所谓清空一个栈,就是将栈中的元素全部作废,但栈本身物理空间并不发生改变(不是销毁)。

  • 因此我们只要将s->top的内容赋值为s->base即可,这样s->base等于s->top,也就表明这个栈是空的了。这个原理跟高级格式化只是但单纯地清空文件列表而没有覆盖硬盘的原理是一样的。其实就是说栈所在磁盘空间的数据并未清除,只是逻辑上清空了一个栈。

ClearStack(sqStack *s)
{
    s-top = s->base;
}

3.6  销毁一个栈

销毁一个栈与清空一个栈不同,销毁一个栈是要释放掉该栈所占据的物理内存空间,因此不要把销毁一个栈与清空一个栈这两种操作混淆。

DestroyStack(sqStack *s){
  int i, len;
  len = s->stackSize; //当前栈中的元素个数
  for( i=0; i < len; i++ ){
    free( s->base ); //循环释放栈所占的物理内存空间
    s->base++; //栈底指针上移
  }
  s->base = s->top = NULL;
  s->stackSize = 0;
}

3.7  计算栈的当前容量

  • 计算栈的当前容量也就是计算栈中元素的个数,因此只要返回s.top-s.base即可。

  • 注意,栈的最大容量是指该栈占据内存空间的大小,其值是s->stackSize,它与栈的当前容量不是一个概念哦。
int StackLen(sqStack s)
{
  return(s.top – s.base); // 初学者需要重点讲解
}

4  栈的链式存储结构

看完顺序型的存储,我们接下来再简单看看链式的:

4.1  数据结构定义

teypedef struct StackNode
{
  ElemType data; // 存放栈的数据
  struct StackNode *next;
} StackNode, *LinkStackPtr;
teypedef struct LinkStack
{
  LinkStackPrt top; // top指针
  int count; // 栈元素计数器
}

4.2  入栈操作

对于栈链的Push操作,假设元素值为e的新结点是s,top为栈顶指针,我们得到如下代码:

Status Push(LinkStack *s, ElemType e)
{
  LinkStackPtr p = (LinkStackPtr) malloc (sizeof(StackNode));
  p->data = e;
  p->next = s->top;
  s->top = p;
  s->count++;
  return OK;
}

4.2  出栈操作

至于链栈的出栈Pop操作,假设变量p用来存储要删除的栈顶结点,将栈顶指针下移一位,最后释放p即可。

Status Pop(LinkStack *s, ElemType *e)
{
  LinkStackPtr p;
  if( StackEmpty(*s) ) // 判断是否为空栈
    return ERROR;
    
  *e = s->top->data;
  p = s->top;
  s->top = s->top->next;
    
  free(p);
  s->count--;
    
  return OK;
}

5  引申

大家知道栈的应用么,比如我们平时表达式的计算,计算机是非常喜欢用栈来算的,表达式又分中缀或者后缀表达式:

中缀表达式简单来说就是我们小学课本中常见的那种形式,也就是我们人所习惯的一种算术表示方式,比如下图。

 后缀表达式,又称为逆波兰表达式,粗略的理解就是加减乘除在数字后面的一种算术表达方式,例如:

比如我们在计算后缀表达式的过程,这里拿前面的来举例哈:

 

逆波兰表达式实现代码:

int main()
{
    sqStack s;
    char c;
    double d, e;
    char str[MAXBUFFER];
    int i = 0;

    InitStack(&s);

    printf("Please input:");
    scanf("%c", &c);

    while(c != '#')
    {
        while( isdigit(c))
        {
            str[i++] = c;
            //'\0'一般放在字符串的结束处,表示字符串的结束,其是ascii值为0的字符的转义。
            str[i] = '\0';
            if(i >= 10)
            {
                printf("ERROR: The data entered is too large!\n");
            }
            scanf("%c", &c);
            if( c == ' ')
            {
                d = atof(str);
                Push(&s, d);
                i = 0;
                break;
            }
        }

        switch(c)
        {
            case '+':
                Pop(&s, &e);
                Pop(&s, &d);
                Push(&s, d+e);
                break;
            case '-':
                Pop(&s, &e);
                Pop(&s, &d);
                Push(&s, d-e);
                break;
            case '*':
                Pop(&s, &e);
                Pop(&s, &d);
                Push(&s,d*e);
                break;
            case '/':
                Pop(&s, &e);
                Pop(&s, &d);
                if(e != 0){
                    Push(&s, d/e);
                }
                else{
                    printf("\nERROR: Divisor is zero !\n");
                }
                break;
        }
        scanf("%c", &c);
    }

    Pop(&s, &d);
    printf("\n The result is :%f\n", d);

    return 0;
}

 

6  小结

好了,栈我们就先看到这里最重要的就是先进后出哈,有理解不对的地方欢迎指正哈。

标签:ElemType,top,栈顶,Pop,base,stackSize,数据结构
From: https://www.cnblogs.com/kukuxjx/p/17365906.html

相关文章

  • 数据结构与算法复习--(2)
    算法和算法分析算法的定义对特定问题求解方法和步骤的一种描述,它是指令的有限序列。其中每个指令表示一个或多个操作。算法的描述自然语言:英语、中文流程图:传统流程图、NS流程图伪代码:类语言:类C语言程序代码:C语言程序、Java语言程序算法与程序算法是解决问题的一......
  • 【数据结构】链式型存储结构-双向链表
    1 前言只要大家坐过火车,对于双向链表的理解就相当简单。双向链表就是在单链表的基础之上,为每一个结点增加了它的前继结点,我们来看看。2 双向链表双向链表的定义如下:typedefstructDaulNode{ElemTypedata;structDaulNode*prior;//前驱结点structDa......
  • 【数据结构】链式型存储结构-循环单链表
    1 前言对于单链表,由于每个结点只存储了向后的指针,到了尾部标识就停止了向后链的操作。也就是说,按照这样的方式,只能索引后继结点不能索引前驱结点。这样一来,不从头结点出发,这样就无法访问到全部结点。为了解决这个问题,我们只需要将单链表的尾结点的指针由空指针改为指向头结点......
  • 【数据结构】链式型存储结构-静态链表
    1 前言地球人都知道C语言是个伟大的语言,它的魅力在于指针的灵活性,使得它可以非常容易地操作内存中的地址和数据,这比其他高级语言更加灵活方便。(面向对象语言,比如java,可以使用对象引用机制间接地实现指针的某些功能)但是古人还是木有C语言丫,木有JAVA丫,只有原始的Basic,Fortran等......
  • 【数据结构】链式型存储结构-单链表
    1 前言线性表的链式存储结构的特点就是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以在内存中未被占用的任意位置。比起顺序存储结构每个元素只需要存储一个位置就可以了。现在链式存储结构中,除了要存储数据信息外,还要存储它的后继元素的存储地址(指针)。也就是说......
  • 【数据结构】线性表分类以及顺序型存储结构
    1 什么是线性表线性表的定义:由零个或多个数据元素组成的有限序列首先它是一个序列,也就是说元素之间是有先来后到之分。若元素存在多个,则第一个元素无前驱,而最后一个元素无后继,其他元素都有且只有一个前驱和后继。线性表强调是有限的,事实上无论计算机发展到多强大,他所能处理......
  • 数据结构与算法基础复习--(1)
    基本术语1.数据(Data)数据是能输入计算机且能被计算机处理的各种符号的集合信息的载体是对客观事物符号化的表示能够被计算机识别、存储和加工包括:数值型的数据:整数、实数等非数值型的数据:文字、图像、图形、声音等2.数据元素数据元素是数据的基本单位,在计......
  • 数组模拟实现数据结构
    数组模拟链表实现①单链表:邻接表(存储图和树)②双链表:优化某些问题单链表inte[N]存储val,intne[N]存储next//单链表模板inthead,e[N],ne[N],idx;//head表示头节点的下标,e[i]表示节点i的值,ne[i]表示节点i的指针是多少,idx存储当前已经用到了哪个点v......
  • 【Redis】Redis数据结构——链表
    【Redis】Redis数据结构——链表注意事项:本文第三点redis中操作列表的相关命令可参考博文:【Redis】Redis基础命令集详解_Etui۹(・༥・´)و̑̑的博客本文参考内容如下:1、Redis数据结构——链表-随心所于-2、《Redis设计与实现》(黄健宏·著)第3章链表本文仅供学习交流使用!1、Redi......
  • 《大话数据结构》读书笔记 附PDF #C3
    刚刚读完了《大话数据结构》,这本书真的是一本不错的入门级别的数据结构和算法的教材。首先,作者通过幽默的语言和丰富的图示,使得枯燥的数据结构与算法变得生动有趣。在阅读过程中,我感受到了作者对于知识点深入浅出的讲解,即使是像我这样初学者也能够轻松理解。其次,书中的配套练习......