数据结构入门(三)栈的应用

  在之前的两篇文章——数据结构入门(一)栈的实现数据结构入门(二)栈的应用之数学表达式求值中,笔者分别介绍了“栈”这个数据结构在数的进制转换和数学表达式求值方面的应用。在本文中,笔者将会再介绍栈的三个应用,它们分别是:

  • 判断字符串是否回文
  • 括号匹配
  • 行编辑程序
  • 二叉树的深度优先遍历

  栈的结构实现可以参考数据结构入门(二)栈的应用之数学表达式求值,本文将不再具体给出。

判断字符串是否回文

  所谓回文字符串就是指正读反读均相同的字符序列,如“12321”、“aha”、“ahaha”、“清水池里池水清”均是回文,但“ahah”不是回文。通过栈这个数据结构我们将很容易判断一个字符串是否为回文。
  首先我们需要找到该字符串的中心点。对于长度为奇数的字符串,中心点就是中间的那个元素;对于长度为偶数的字符串,中心点恰好位于长度为一半的那个元素及其后一个元素的中间。接着将中心点前面的字符依次入栈,然后将当前栈中的字符依次出栈,看看是否能与 中心点之后的字符一一匹配,如果都能匹配则说明这个字符串是回文字符串,否则就不是回文字符串。
  以下是利用栈来实现判断字符串是否回文的Python代码:

# -*- coding: utf-8 -*-
# using Stack to check if a string is plalindrome

from Stack import Stack

def isPlalindrome(words):
    length = len(words)
    flag = length//2
    s = Stack()
    for i in range(flag):
        s.push(words[i])

    start = flag+1 if length % 2 else flag
    for item in words[start:]:
        if s.pop() != item:
            return False

    return True

def main():
    words_list = ["12321", "aha", "ahaha", "清水池里池水清", "ahah"]
    for words in words_list:
        res = isPlalindrome(words)
        print("'%s' is plalindrome: %s" % (words, res))

main()

输出结果如下:

'12321' is plalindrome: True
'aha' is plalindrome: True
'ahaha' is plalindrome: True
'清水池里池水清' is plalindrome: True
'ahah' is plalindrome: False

括号匹配

  在字符串中,我们常常会遇到括号匹配的问题,常见的括号如下:

  • 小括号: “(”和“)”
  • 中括号: “[”和“]”
  • 花括号: “{”和“}”

每一个开符号必须匹配与其对应的闭符号。以下为匹配示例:

  • 正确: ( )(( )){([( )])}
  • 正确: ((( )(( )){([( )])}))
  • 错误: )(( )){([( )])}
  • 错误: ({[])}
  • 错误: (

  下面说明括号匹配的算法。从左至右检查每个元素,用栈S储存括号。每当遇到一个开符号时,就入栈,每当遇到闭符号时,就出栈(假设S非空),并检查两者是否匹配。如果所有元素都检查完毕,且栈S为空,那么原来的表达式括号匹配,否则就不匹配。
  下面是括号匹配的Python代码:

# -*- coding: utf-8 -*-
# matching parentheses

from Stack import Stack

def is_matched(expr):
    # return True if all delimiters are properly match; False otherwise
    lefty = '({['   # opening delimiters
    righty = ')}]'  # respective closing delimiters
    s = Stack()
    for c in expr:
        if c in lefty:
            s.push(c)
        elif c in righty:
            if s.is_empty():    # nothing to match with
                return False
            if righty.index(c) != lefty.index(s.pop()): # mismatched
                return False
    return s.is_empty()

def main():
    expr_list = ['[(5+x)-(y+z)]', '(5+x))*y', '{[1+(x+y)]}*[(3*z)]']
    for expr in expr_list:
        Flag = is_matched(expr)
        print('%s is matched: %s' %(expr, Flag))

main()

输出结果如下:

[(5+x)-(y+z)] is matched: True
(5+x))y is matched: False
{[1+(x+y)]}
[(3*z)] is matched: True

行编辑程序

  一个简单的行编辑程序的功能是:接收用户从终端输入的程序或数据,并存入用户的数据区。由于用户在终端上进行输入时,不能保证不出差错,因此,若在编辑程序中,“每接收一个字符即存入用户数据区”的做法是不恰当的。较好的做法是,设立一个输入缓冲区,用以接受用户输入的每一行字符,然后逐行存入用户数据区。允许用户输入出差错,并在发现有误时可以及时更正。例如,当用户发现刚刚输入的一个字符是错的时,可以补进一个退格符“#”,表示前一个字符无效;如果发现当前输入的行内差错较多或难以补救,则可以输入一个退行符“@”,表示当前行中的字符均无效。比如,假设从终端接收了两行这样的字符:

whli##ilr#e(s#*s)
outcha@putchar(*s=#++);

则实际有效的是下列两行:

while(*s)
putchar(*s++);

  行编辑程序的输出结果可用栈来解决。可设这个输入缓冲区为一个栈结构,每当从终端接受了一个字符后先做如下判别:如果它既不是退格符也不是退行符,则将该字符入栈;如果是一个退格符,则从栈顶删去一个字符;如果它是退行符,则将该栈清空。以下为实现行编辑程序的Python代码:

# -*- coding: utf-8 -*-
# line edit programming
from Stack import Stack

# line edit programming using Stack
def LineEdit(chars):

    s = Stack()
    for char in chars:
        if char != '\n':    # line edit
            if char == '#':
                s.pop()
            elif char == '@':
                s.clear()
            else:
                s.push(char)
        else:   # output
            line = []
            while not s.is_empty():
                line.append(s.pop())
            print(''.join(line[::-1]))

def main():
    chars = """whli##ilr#e(s#*s)
    outcha@putchar(*s=#++)
    fi@if a=# == 'exti##it':
    system@    sys.tiex####exit((#)
    """

    LineEdit(chars)

main()

输出结果如下:

while(*s)
putchar(*s++)
if a == 'exit':
    sys.exit()

二叉树的深度优先遍历

  关于二叉树的介绍与实现,可以参考笔者的文章:二叉树的Python实现。二叉树的深度优先遍历,也就是先序、中序、后续遍历,在文章二叉树的Python实现中,我们已经用递归的方法实现了,这是因为二叉树天然就具有良好的递归性质,就连它的定义也可用递归来实现。在本文中,笔者将要用栈来实现二叉树的深度优先遍历。
  以先序遍历为例,先访问根结点,然后遍历左子树接着是遍历右子树,因此我们可以利用堆栈的先进后出的特点,先将右子树压栈,再将左子树压栈,这样左子树就位于栈顶,可以保证结点的左子树先与右子树被遍历。
  二叉树的实现代码不再给出,读者可参考二叉树的Python实现,用栈实现前序遍历的函数如下:

    # 使用栈结构实现前序遍历
    def preStack(self):
        if self.data is not None:
            s = Stack()
            s.push(self)
            while not s.is_empty():
                currentNode = s.pop()
                print(currentNode.data, end=' ')
                if currentNode.right is not None:
                    s.push(currentNode.right)
                if currentNode.left is not None:
                    s.push(currentNode.left)

构建的示例二叉树如下:

示例二叉树

输出的结果如下:

先序遍历(递归)为:
18 7 3 4 11 5 1 3 6 2 4 
先序遍历(非递归)为:
18 7 3 4 11 5 1 3 6 2 4 

总结

  堆栈(Stack)最早由 Alan M. Turing(艾伦·图灵)于 1946 年提出,当时是为了解决子程序的调用和返回。在本文及前面的文章中,介绍了一些关于栈的应用,当然,还有许多话题未涉及,比如:解决迷宫问题,HTML标签匹配,子程序的调用和返回等。另外,栈还常常与递归方法一起使用,能轻松地解决很多问题~
  关于栈在其它问题中的应用,可以参考网址: https://www.geeksforgeeks.org/stack-data-structure/

参考文献

  1. 《啊哈!算法》啊哈磊著 人民邮电出版社 p32-35
  2. 《数据结构(C语言版)》 严蔚敏 吴伟民 著 清华大学出版社 p48-50
  3. Data Structures & Algorithms in Python, M.T.Goodrich, R.Tamassia, M.H.Goldwasser著, p229-237
  4. 二叉树深度遍历的非递归算法分析及Java实现: https://my.oschina.net/husthang/blog/852982
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 1)这本书为什么值得看: Python语言描述,如果学的Python用这本书学数据结构更合适 2016年出版,内容...
    孙怀阔阅读 12,434评论 0 15
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,646评论 0 13
  • 算法思想贪心思想双指针排序快速选择堆排序桶排序荷兰国旗问题二分查找搜索BFSDFSBacktracking分治动态...
    第六象限阅读 2,983评论 0 0
  • 那一天我爱上你我的世界就变得美丽 那一天是哪一天你忘记的日子却种进了我的心里 你说你的梦在远方远方的天很高远方的路...
    半月婵阅读 672评论 11 21
  • 文/陵子 摄影/陵子 摄影 可以不专业 但一定要喜欢 喜欢大自然的美好 喜欢光影景的味道 喜欢瞬间的定格 喜欢其中...
    陵子心语阅读 265评论 5 7