数据结构--栈

来源:互联网 发布:linux主目录是什么 编辑:程序博客网 时间:2024/06/01 08:21

什么是栈——首先,栈是一种线性表,也就是说,栈元素具有线性关系,即前驱后继的关系。只不过他是一种特殊的线性表,定义中说在线性表的表尾可以进行插入和删除的操作。这里的表尾指的是栈顶,而不是栈低。

栈的插入叫做进栈,栈的删除叫做出栈。栈的进出要遵循“后进先出”的规则。

例如:这里有三个数字 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。

总共就只有5种情况。

栈的顺序存储结构:

前面提到栈是线性表的一种特殊的情况,那么栈的顺序存储结构就是线性表的顺序存储方式,我们简称顺序栈。线性表是用数组进行实现的,那么用数组的哪一边作为栈底好呢。

是的,就是用下标为0的一端作为栈底比较好,因为首元素都存在栈底,变化最小。

 

栈的链式存储结构——链栈

栈也是线性结构的一种特例。与队列不同,他只有一个口,只能从这里读或者写数据,这个口称为栈顶(top)。栈是一种先进后出的数据结构。先进来的元素会放入栈底,而后进来的元素被放在它的上面,最后进来的元素的上面的位置,称为栈顶。

栈所提供的操作比一般的线性表要少很多,只提供:初始化、销毁、判断是否为空、求栈的长度、清空栈、将数据压入栈、将数据弹出栈、获得栈顶元素这几种操作。其中将数据压入栈、将数据弹出栈、获得栈顶元素是最重要的。有人可能觉得,将栈顶元素弹出与获得栈顶元素是不是有点重复,其实它们主要的目的在于,很多时候你只想知道当前栈顶的元素是谁,而并不想将它弹出。这样做可以简单一点。

了解了栈的基本结构和操作以后,自然而然的想到用数组来实现栈:因为前面的元素并不发生变化,只能在最后面入栈或者出栈。

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

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

栈顶——地址较高;

栈底——地址较低。 

定义一个顺序存储的栈,包含三个元素:base、top、stackSize。

 

栈的应用——递归

今天说的是栈与递归的关系,函数的递归调用和普通函数调用是一样的。当程序执行到某个函数时,将这个函数进行入栈操作,在入栈之前,通常需要完成三件事。

  1、将所有的实参、返回地址等信息传递给被调函数保存。

  2、为被调函数的局部变量分配存储区。

  3、将控制转移到北调函数入口。

当一个函数完成之后会进行出栈操作,出栈之前同样要完成三件事。

  1、保存被调函数的计算结果。

  2、释放被调函数的数据区。

  3、依照被调函数保存的返回地址将控制转移到调用函数。

上述操作必须通过栈来实现,即将整个程序的运行空间安排在一个栈中。每当运行一个函数时,就在栈顶分配空间,函数退出后,释放这块空间。所以当前运行的函数一定在栈顶。

递归程序:

#include<stdio.h>

 

voidrecurrence(int num)

{

    if ( num < 0 )

        return;

    printf("%d\n", num);

    recurrence(num - 1);

   printf("%d\n", num);

} int main(void) {recurrence(5); return 0; }

栈的应用——四则运算表达式求值

中缀表达式:就是目前我们用到的计算表达式 如:“9+(3-1)*3+5/2”

后缀表达式:就是把运算符放置到数字的后面 如:"9 3 1 - 3 * + 5 2 / +"

中缀表达式 转化为后缀表达式规则:

从走到有遍历中缀表达式的数字和字符

若是数字输出,即成为后缀表达式的一部分

 若是符号则判断其与栈顶符号的优先级

 是右括号或者优先级低于栈顶符号(乘除优先于加减)则栈顶元素一次出栈并输出并将当前符号进栈一直到最终输出后缀表达式后缀表达式如何用计算机得到结果:

   从左到右遍历表达式的每个数字和符号,

       遇到数字就进栈,

       遇到符号就将处于栈顶的两个数字出栈,

       进行运算,

       运算结果进栈,

       一直到最终结果