【python数据结构与算法】列表实现栈及栈的应用

讲义连接

栈的数据结构

Python的基本数据类型list是实现栈的基础
产检的列表方法

通过使用列表的方法,可以实现栈的特性:

列表访问实际上就是对数组的访问,python数组访问非常方便,可以直接通过指定下标分片。
因此,我们有两种实现方式:将列表首端作为栈顶或列表尾端作为栈顶。

这两种实现的算法复杂度是不一样的,因为列表的内存地址是连续的,列表对象寻址实际上是找到列表开始的元素所在的内存地址,尾端实现的栈可以直接在列表尾端追加或删除元素,首端实现的栈,需要将所有元素在内存中移动一遍,因此算法复杂度是O(n)

尾端实现栈顶的push/pop复杂度为O(1)
首端实现栈顶的push/pop复杂度为O(n)

'''
python列表实现栈1:列表的尾端作为栈顶
'''

class PythonStack1():
    def __init__(self):
        self.my_stack = []

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

    def pop(self):
        if len(self.my_stack) > 0 :
            res = self.my_stack[-1]
            self.my_stack.pop()
            return res
        return None

    def peek(self):
        if len(self.my_stack) == 0:
            return 'Head'
        return self.my_stack[len(self.my_stack) - 1]

    def isempty(self):
        return self.my_stack == []

    def size(self):
        return len(self.my_stack)
stack1 = PythonStack1()
stack1.push('A')
stack1.push('B')
stack1.push('C')
print(stack1)
print(stack1.size())
stack1.pop()
print(stack1.peek())
stack1.pop()
print(stack1.isempty())
stack1.pop()
print(stack1.isempty())
'''
python列表实现栈2:列表的首端作为栈顶
'''

class PythonStack2():
    def __init__(self):
        self.my_stack = []

    def push(self, item):
        self.my_stack.insert(0, item)

    def pop(self):
        self.my_stack = self.my_stack[1:]

    def peek(self):
        return self.my_stack[0]

    def isempty(self):
        return self.my_stack == []

    def size(self):
        return len(self.my_stack)
stack2 = PythonStack2()
stack2.push('A')
stack2.push('B')
stack2.push('C')
print(stack2)
print(stack2.size())
stack2.pop()
print(stack2.peek())
stack2.pop()
print(stack2.isempty())
stack2.pop()
print(stack2.isempty())

栈的应用

算法题目中,栈是解决问题常用的工具之一,其先入后出的特性常常为算法提供数据缓存。

括号匹配

栈的应用1-1:简单括号匹配

题目:从给出的表达式中识别括号是否是匹配的
思路:
1. 扫描表达式,过滤掉所有不是括号的字符
2. 遇到左括号压栈,遇到右括号弹出,最后栈为空说明括号匹配
3. 遇到右括号的时候要考虑栈是否已经为空,为空则直接判断不匹配
优化:
如果括号字符串长度是基数位,直接判断不匹配

stack1 = PythonStack1()
def get_brackets(string):
    stack = stack1
    balanced = True
    for i in string:
        if i == "(":
            stack.push("_")
        else:
            if stack.isempty():
                balanced = False
                break
            else:
                stack.pop()
    if balanced and stack.isempty():
         return True
    else:
        return False


def is_matching_brackets(expr):
    brackets = []
    for i in expr:
        if i == "(" or i == ")":
            brackets.append(i)
    if len(brackets) % 2 == 0:  # 优化:如果括号字符串长度是基数位,直接判断不匹配
        return get_brackets(brackets)
    return False

string1 = '(1+2)+5)+()+2+(1+2)'
string2 = '()()()()((()))()()()()'
print(is_matching_brackets(string1))
print(is_matching_brackets(string2))

栈的应用1-2:多种括号匹配

题目:从给出的表达式中识别括号是否是匹配的
思路:
1. 扫描表达式,过滤掉所有不是括号的字符
2. 遇到任何一种左括号都做压栈处理
3. 遇到右括号:a) 如果栈为空,直接判断不匹配
b) 如果栈顶的括号类型不一致,直接判断不匹配

stack1 = PythonStack1()


def get_brackets(string):
    '''
    处理字符串,筛选出所有的括号
    :param string: 输入字符串
    :return: 只包含括号的字符串
    '''
    stack = stack1
    balanced = True
    for i in string:
        if i in ['(', '[', '{']:
            stack.push(i)
        else:
            if stack.isempty():
                balanced = False
                break
            else:
                if not matches(stack.peek(), i):
                    balanced = False
                    break
                stack.pop()
    if balanced and stack.isempty():
        return True
    else:
        return False


def matches(arg_a, arg_b):
    '''
    检查括号类型是否匹配
    :param arg_a: 左括号列表
    :param arg_b: 右括号列表
    :return: 左右括号是否是同一种类型
    '''
    a_list = ['[', '{', '(']
    b_list = [']', '}', ')']
    return a_list.index(arg_a) == b_list.index(arg_b)


def is_matching_brackets(expr):
    '''
    主方法
    :param expr: 输入的字符串
    :return: 判断括号是否匹配
    '''
    brackets = []
    for i in expr:
        if i in ['[', '{', '(', ']', '}', ')']:
            brackets.append(i)
    if len(brackets) % 2 == 0:  # 优化:如果括号字符串长度是基数位,直接判断不匹配
        return get_brackets(brackets)
    return False


string1 = '[](){}}}}]'
string2 = '[](){}'
print(is_matching_brackets(string1))
print(is_matching_brackets(string2))

进制转换

题目:十进制转换为二进制
思路:
1. 计算方式,给出的数字不断对2取余,取到的数是又低到高的顺序,再由高到低得到最终的二进制结果
2. 需要一个栈来存放余数,弹出
3. 遇到右括号:a) 如果栈为空,直接判断不匹配
b) 如果栈顶的括号类型不一致,直接判断不匹配

stack = PythonStack1()


def get_binary_number(number):
    while number > 0:
        stack.push(number % 2)
        number = number // 2
    res = ''
    while not stack.isempty():
        res += str(stack.peek())
        stack.pop()
    return res


print(get_binary_number(16))
print(get_binary_number(255))

栈的应用2-2:

题目:十进制转换为N进制,N不大于10
思路:
通过闭包实现N进制的定制

stack = PythonStack1()

def transfer(N):
    def inner(number):
        while number > 0:
            stack.push(number % N)
            number = number // N
        res = ''
        while not stack.isempty():
            res += str(stack.peek())
            stack.pop()
        return res

    return inner

get_2num_from_10num = transfer(2)
print(get_2num_from_10num(255))
get_8num_from_10num = transfer(8)
print(get_8num_from_10num(255))

表达式转换

表达式表示法:

中缀表达式:A+(BC)
前缀表达式:+A
BC
后缀表达式:ABC*+

前缀表达式和后缀表达式中操作数的前后位置不变
操作符的顺序表示了运算的优先级,不需要括号来表示运算优先级

对于计算机来说,人类常用的中缀表达式的顺序是混乱的,其运算顺序并不是从左到右。
在不改变元素位置的情况下,调整运算符的顺序,就能取消括号,直接通过运算符的顺序直接标识计算顺序:

栈的应用3-1:表达式转换

题目:把中缀表达式转换为后缀表达式
思路:

  1. 用栈缓存运算符:+、-、*、/、(、)
  2. 用列表缓存后缀表达式
  3. 扫描中缀表达式:
    遇到操作符是‘(’,push
    遇到操作符是‘)’,连续pop,直到遇到‘(’,把‘(’pop后停止
    遇到操作符是‘+、-、*、/’,需要比较和栈顶操作符的优先级
    >>低于或等于栈顶优先级:连续pop
    高于栈顶优先级:push
    后缀表达式接受操作数和栈弹出的运算符
expr_stack = PythonStack1()

def priority(item):
    '''
    比较当前操作符与栈顶操作符的优先级
    :param input: 当前操作符
    :param stacktop: 栈顶操作符
    :return: 当前操作符优先级 < 栈顶操作符优先级
    '''
    level = {'*': 3, '/': 3, '+': 2, '-': 2, '(': 1, ')': 1}
    return level.get(item)


def get_post_expr(middle_expr):
    '''
    主方法
    :param middle_expr: 中缀表达式
    :return: 返回后缀表达式
    '''

    post_expr = []

    for i in list(middle_expr):
        if i == '(':
            expr_stack.push(i)
        if i == ')':
            topitem = expr_stack.pop()
            while topitem != '(':
                post_expr.append(topitem)
                topitem = expr_stack.pop()
        if i in ['*', '/', '+', '-']:
            # 比较当前的运算符优先级与栈顶的运算符优先级
            while not expr_stack.isempty() and priority(expr_stack.peek()) >= priority(i):
                post_expr.append(expr_stack.pop())
            expr_stack.push(i)  # 如果运算符优先级大于栈顶优先级,加入到栈
        if i in 'ABCDEFGHIJK' or i  in '0123456789':
            post_expr.append(i)  # 把非运算符加入到表达式列表中
    while not expr_stack.isempty():
        post_expr.append(expr_stack.pop())
    res = ''.join(post_expr)
    return res

expr1 = 'A+B*C-(D*E+F)'
print(get_post_expr(expr1))

栈的应用3-2:后缀表达式计算

题目:计算后缀表达式的结果
思路:

  1. 后缀表达式没有括号,运算符的顺序就是计算的顺序
  2. 运算都是二目运算,从左网右扫描,非运算符压入栈,遇到非运算符,弹出栈顶前两个非运算符,进行计算,计算结果再压入栈
  3. 注意栈顶两个元素的弹出顺序,在计算除法和减法时是有顺序要求的
compute_stack = PythonStack1()
def compute_post_expr(post_expr):

    for item in list(post_expr):

        if item in ['*','/','-','+']:
            member2 = compute_stack.pop()
            member1 = compute_stack.pop()
            res_item = str(member1) + item + str(member2)
            compute_stack.push(eval(res_item))
        else:
            compute_stack.push(item)
    return compute_stack


post_expr1 = '942*/33*3+-'
print(compute_post_expr(post_expr1).peek())
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值