数据结构与算法分析 —— C 语言描述:栈

栈模型

栈是限制插入和删除只能在一个位置上进行的表,该位置是表的末端,叫做栈的顶(top)。对栈的基本操作有 Push(进栈)和 Pop(出栈),前者相当于插入,后者则是删除最后插入的元素。最后插入的元素可以通过使用 Top 例程在执行 Pop 之前进行检查。对空栈进行的 Pop 或 Top 一般被认为是栈 ADT 的错误。另一方面,当运行 Push 时空间用尽是一个实现错误,但不是 ADT 错误。

栈有时又叫做 LIFO(后进后出)表。普通的清空栈的操作和判断是否空栈的测试都是栈的操作指令系统的一部分,但是,对栈所能够做的基本上也就是 Push 和 Pop 操作。

栈的实现

由于栈是一个表,因此任何实现表的方法都能实现栈。

栈的单链表实现

栈的第一种实现方法是使用单链表。可以通过在表前端插入来实现 Push,通过删除表前端元素实现 Pop。Top 操作只是检查表前端元素并返回它的值。有时 Pop 操作和 Top 操作合二为一。

创建一个空栈也很简单,只要创建一个头节点,MakeEmpty 设置 Next 指针指向 NULL。Push 是通过向链表前端进行插入而实现的,其中,表的前端做为栈顶。Top 的实现是通过检查表在第一个位置上的元素而完成的。最后,Pop 是通过删除表的前端的元素而实现的。

很清楚,所有的操作均花费常数时间,因为这些例程没有任何地方涉及栈的大小(空栈除外),更不用说依赖于栈大小的循环了。这种实现方法的缺点在于对 malloc 和 free 的调用开销是昂贵的,特别是于指针操作的例程相比。有的缺点通过使用第二个栈可以避免,第二个栈初识时为空栈。当一个单元从第一个栈弹出时,它只是被放到了第二个栈中,此后,当地一个栈需要新的单元时,他首先去检查第二个栈。

栈的数组实现

栈的另一种实现方法避免了指针并且可能是更流行的解决方案。这种策略的唯一潜在危害是我们需要提前声明一个数组的大小。一般说来,这并不是问题,因为在典型的应用程序中,即使有相当多的栈操作,在任意时刻栈元素的实际个数从不会太大。声明一个数组足够大而不至于浪费太多的空间通常并没有什么困难。如果不能做到这一点,那么节省的做法是使用链表来实现。

用一个数组实现栈很简单的。每一个栈有一个 TopStack,对于空栈它是一个 -1(这就是空栈的初始化)。为了将某个元素 X 压入该栈,我们将 TopOfStack 加 1,然后置 Stack[TopOfStack] = X,其中 Stack 是代表具体栈的数组。未了弹出栈元素,我们置返回值为 Stack[TopOfStack] 然后 TopOfStack 减 1。当然,由于潜在地存在多少个栈,因此 Stack 数组和 TopOfStack 只代表一个栈结构的一部分。使用全局变量和固定名字来表示这种(或任意)数据结构非常不好,因为在大多数实际情况下总是存在多个栈。当编写实际程序的时候,你应该尽可能严格地遵循模型,这样,除一些栈例程外,你的程序的任何部分都没有存取被每个栈蕴含的数组或栈项顶(top-of-stack)变量的可能。这对所有的 ADT 操作都是成立的。像 Ada 和 C++ 这样的现代程序设计语言实际上都能够实施这个法则。

注意,这些操作不仅以常数时间运行,而且是以非常快的常数时间运行。在某些机器上,若在带有自增和自减寻址功能的寄存器上操作,则(整数的)Push 和 Pop 都可以写成一条机器指令。最现代化的计算机将栈操作作为它的指令系统的一部分,这个事实强化了这样一种观念,即栈很可能是计算机科学中仅次于数组的最基本的数据结构。

一个影响栈的执行效率的问题是错误检测。链表实现仔细地检查错误。对空栈的 Pop 或者对满栈的 Push 都将超出数组的界限并引起程序崩溃。显然,我们不愿出现这样的情况。但是,如果把这些条件的检测放到数组实现过程中,那就很可能要花费像实际栈操作那样多的时间。由于这个原因,除非在错误处理及其重要的场合(如在操作系统中),一般在栈例程中省去错误检测就成了惯用手法。虽然在多数情况下可能通过声明一个栈大到不至于使得操作溢出,并保证使用 Pop 操作的例程绝不对一个空栈执行 Pop 而侥幸避免对错误的检测,但是,这充其量只不过是使得程序得以正常运行而已,特别是当程序很大并且不止一个人编写或分若干次写成的时候。因为栈操作花费如此快的常数时间,所以一个程序的主要运行时间很少会花在这些例程上面。这就意味着,忽略错误检测一般是不妥的。你应该随时编写错误检测的代码;如果他们冗长,那么当它们确实耗费太多时间时你总可以将它们去掉。

应用

  • 平衡符号(编译器检查)
  • 后缀表达式
  • 中缀到后缀的转换
  • 函数调用
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容