Python数据结构与算法——栈

什么是栈

在数据结构中栈和队列可以理解为一种容器。它们也是一种简单的缓存结构,只支持数据的存储和访问。栈中的元素之间相互没有任何和的具体关系,只有时间的相互顺序。栈的相关操作包括数据元素的存入、访问、删除等。

Python中栈的实现


class Stack:

    def __init__(self):
        self.__items = []

    def is_empty(self):
        return self.__items == []

    def push(self, item):
        self.__items.append(item)

    def peek(self):
        if not self.is_empty():
            return self.__items[-1]
        else:
            return None

    def pop(self):
        if not self.is_empty():
            return self.__items.pop()
        else:
            return None

    def size(self):
        return len(self.__items)

栈的实际应用

给定一个列表,对这个列表进行反转

思路: 把要反转的列表中的元素一个个压入到栈中,然后再将栈中的数据一个个推出到一个新列表中,就得到了一个反转后的列表了


from stack import Stack

def reverse_list(l1):
    s = Stack()
    l2 = []

    for i in l1:
        s.push(i)

    while not s.is_empty():
        l2.append(s.pop())

    return l2

数字的进制转换

思路:对要转换的数字进行相应进制的整除取余,将得到的余数压入栈中,不停的进行整除取余压入栈中,最后从栈中把元素一个个推出就得到了相应进制的数了


from stack import Stack

def base_conversion(num, base):
    digits = "0123456789ABCDEF"
    rem_stack = Stack()

    while num > 0:
        rem = num % base
        rem_stack.push(rem)
        num = num // base

    rem_str = ""

    while not rem_stack.is_empty():
        rem_str = rem_str + digits[rem_stack.pop()]

    return rem_str

检查括号的匹配

思路:
1、顺序扫描被检查字符串中的字符
2、跳过无关的字符
3、遇到开括号压入栈
4、遇到闭括号时与弹栈元素匹配,检查是否匹配
5、匹配成功继续,否则匹配失败


def check_bracket(symbol_str):
    bracket_stack = Stack()
    balance = True
    index = 0

    while index < len(symbol_str) and balance:
        symbol = symbol_str[index]

        if symbol in "([{<":
            bracket_stack.push(symbol)
        elif symbol in ")]}>":
            if bracket_stack.is_empty():
                balance = False
            else:
                top = bracket_stack.pop()
                if not matches(top, symbol):
                    balance = False
        
        index = index + 1
    
    return balance and bracket_stack.is_empty()


def matches(open, close):
    opens = "([{<"
    closes = ")]}>"

    return opens.index(open) == closes.index(close)

表达式转换

计算一个表达式时,编译器通常使用后缀表达式,这种表达式不需要括号。比如,中缀表达式 2 + 5 * 6 转换成后缀表达式后应该为:2 5 6 * +
思路:
1、建立一个栈来存储待计算的操作数;
2、遍历字符串,遇到操作数则压入栈中,遇到操作符号则出栈操作数(n次),进行相应的计算,计算结果是新的操作数压回栈中,等待计算
3、按上述过程,遍历完整个表达式,栈中只剩下最终结果;


def infix_to_postfix(infix_expression):
    prec = {
        "*": 3,
        "/": 3,
        "+": 2,
        "-": 2,
        "(": 1
    }

    op_stack = Stack()
    postfix_list = []
    infix_list = infix_expression.split()

    for token in infix_list:
        if token in '0123456789' or token in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ':  # 如果是操作数
            postfix_list.append(token)
        elif token == "(": # 如果是左开括号
            op_stack.push(token)
        elif token == ")": # 如果是右闭括号
            top_token = op_stack.pop()
            while top_token != "(":
                postfix_list.append(top_token)
                top_token = op_stack.pop()
        else: # 如果是操作符
            while (not op_stack.is_empty()) and (prec[op_stack.peek()] >= prec[token]): # 判断当前操作符和栈顶操作符的优先级哪个高
                postfix_list.append(op_stack.pop())
            op_stack.push(token)

    while not op_stack.is_empty():
        postfix_list.append(op_stack.pop())

    return "".join(postfix_list)

    if __name__ == '__main__':
        print(infix_to_postfix("12 + 5 * 6 - 8"))

执行代码输出结果如下:

>>> 1256*+8-

计算后缀表达式的值

思路:将表达式拆分成一个列表,然后从左到右扫描,发现是数字,就压入表达式栈中,遇到操作符,就从表达式栈中弹出两个操作数,进行运算操作。


def calc_postfix_expression(postfix_expression):
    op_stack = Stack()
    expression_list = postfix_expression.split()

    for token in expression_list:
        if token in "0123456789":
            op_stack.push(int(token))
        else:
            op = token
            op2 = op_stack.pop()
            op1 = op_stack.pop()
            result = do_math(op, op1, op2)
            op_stack.push(result)

    return op_stack.pop()


def do_math(op, op1, op2):
    if op == "+":
        return op1 + op2
    elif op == "-":
        return op1 - op2
    elif op == "*":
        return op1 * op2
    elif op == "/":
        if op2 == 0:
            return "error"
        return op1 / op2

if __name__ == '__main__':
    print(calc_postfix_expression("12 5 6 * + 8 -"))

执行代码输出结果如下:

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