python数据结构与算法学习之基本结构

本文详细介绍了Python中的线性结构,包括栈、队列、双端队列和列表,并探讨了它们在括号匹配、进制转换、表达式转换等问题中的应用。同时,还提供了一系列相关的编程练习,帮助读者加深理解。
摘要由CSDN通过智能技术生成

线性结构Linear Structure

  • 线性结构是一种有序数据项的集合,其中每个数据项都有唯一的前驱和后继。
  • 线性结构总有两端,在不同情况下称呼不一样(前后、左右、顶底)。
  • 两端的称呼并不是关键,不同线性结构的两端区别在于数据增减的方式。有的结构只允许数据项从一端添加,有的结构则允许 数据项从两端移除。
  • 四个线性结构:
    栈 stack —— 后进先出(LIFO:last in first out)
    队列 queue —— 先进先出(FIFO:first in first out)
    双端队列 deque
    列表 list
  • 这些数据结构的共同点在于,数据项之间只存在先后的次序关系,都是线性结构。

栈Stack

  • 一种有次序的数据项集合,在栈中数据的加入和移除都仅发生在同一端,这一端叫栈“顶top”,另一端叫栈“底base”。
  • 距离栈底越近的数据项,留在栈中的时间就越长,而最新加入栈的数据项会被最先移除,这种次序通常称为“后进先出LIFO”。(last in first out)
  • 栈的特性:反转次序,进栈和出栈的次序正好相反。如计算机网页访问、Word里的撤销按钮。

抽象数据类型Stack(ADT Stack)

  • 抽象数据类型“栈”是一个有次序的数据集,每个数据项仅从“栈顶”一端加入到数据集中、从数据集中移除,栈具有后进先出LIFO的特性

  • 抽象数据类型“栈”定义为如下的操作
    Stack():创建一个空栈,不包含任何数据项
    push(item):将item加入栈顶,无返回值
    pop():将栈顶数据项移除,并返回,栈被修改
    peek():“窥视”栈顶数据项,返回栈顶的数
    据项但不移除,栈不被修改
    isEmpty():返回栈是否为空栈
    size():返回栈中有多少个数据项

ADT Stack操作样例
请添加图片描述

用列表实现ADT Stack

  • list左端还是右端作为栈顶,性能有所区别。栈顶为左端时push/pop的复杂度为O(n),而栈顶为右端的实现push/pop的复杂度为O(1)。
    请添加图片描述

栈的应用:简单括号匹配

  • LISP语言大量使用括号。

  • 括号的使用遵循“平衡”规则,对括号的正确匹配是很多语言编译器的基础算法。

括号匹配代码

from pythonds.basic.stack import Stack

def parChecker(symbolString):
    s = Stack()
    balanced = True
    index = 0
    while index < len(symbolString) and balanced:
        symbol = symbolString[index]
        if symbol == "(":
            s.push(symbol)
        else:
            if s.isEmpty():
                balanced = False
            else:
                s.pop()
        index = index + 1

    if balanced and s.isEmpty():  # 要检查栈是否空
        return True
    else:
        return False

print(parChecker('((()))'))
print(parChecker('(()'))

栈的应用:通用括号匹配

括号匹配代码

def matches(open,close):
    opens = '{[('
    closes = '}])'  # 一样的括号位置要一样 index识别
    return opens.index(open) == closes.index(close)def parChecker(symbolString):
    s = Stack()
    balanced = True
    index = 0
    while index < len(symbolString) and balanced:
        symbol = symbolString[index]
        if symbol in '{[(': # 只判断三种括号
            s.push(symbol)
        else:
            if s.isEmpty():
                balanced = False
            else:
                top = s.pop()
                if not matches(top,symbol):  #top是open符号,symbol是close符号
                    balanced = False
        index += 1
    if s.isEmpty() and balanced:
        return True
    else:
        return False

栈的应用:进制转换

  • 十进制转二进制是“除以二”的过程,得到的余数是从低到高的次序,而输出则是从高到低,所以需要一个栈来次序反转。(最后开始读)请添加图片描述

十进制转十六以下任意进制代码

from pythonds.basic.stack import Stack

def baseConverter(decNumber,base):
    digits = "0123456789ABCDEF"
    remstack = Stack()

    while decNumber > 0:
        rem = decNumber % base (# base看是转n进制,base=n)
        remstack.push(rem)
        decNumber = decNumber // base

    newString = ""
    while not remstack.isEmpty():
        newString = newString + digits[remstack.pop()]

    return newString

print(baseConverter(25,2))
print(baseConverter(25,16))

表达式转换

中缀表达法

  • 中缀表达式形式:第一操作数、操作符、第二操作数。
  • 中缀(infix)表达式:A + B * C + D ,为了避免计算顺序混淆,引入全括号表达式;
  • 全括号中缀表达式:((A + (B * C)) + D)

前缀和后缀表达式

  • 前缀(prefix)表达式:将操作符移到前面,形式变为:操作符、第一操作数、第二操作数。
  • 后缀(postfix)表达式:将操作符移到后面,形式变为:第一操作数、第二操作数、操作符。
    在这里插入图片描述

中缀转换为前缀/后缀表达式

例:(A + (B * C))

  • 把操作符移到子表达式(B * C)的右括号位置替代它,再删去左括号得到BC,接着把操作符+移到相应的右括号并删掉左括号,表达式就转为 后缀 形式,即:ABC*+ 。
  • 同理,把操作符移到相应的左括号替代之,并删掉右括号,表达时就转换为 前缀 形式,即:+A*BC 。
    总结:无论表达式多复杂,转换为前缀或后缀只需要两个步骤:
    (1) 将中缀表达式转换为全括号形式;
    (2) 将所有操作符移动到子表达式所在的左括号(前缀)或者右括号(后缀)处,替代之,再删除所有括号。

通用的中缀转后缀算法

流程

  • 从左到右扫描中缀表达式单词列表,如果单词是操作数(a,b,c,1,2,3等),则直接添加到后缀表达式列表的末尾;
  • 如果单词是左括号“(”,则压入opstack栈顶;
  • 如果单词是右括号“)”,则反复弹出opstack栈顶操作符,加入到输出列表末尾,直到碰到左括号;
  • 如果单词是操作符“*/±”,则压入opstack栈顶。但在压入栈顶之前,要比较其与栈顶操作符的优先级,如果栈顶的高于或等于它,就要反复弹出栈顶操作符,加入到输出列表末尾;直到栈顶的操作符优先级低于它。
  • 中缀表达式单词列表扫描结束后,把opstack栈中的所有剩余操作符依次弹出,添加到输出列表末尾
  • 把输出列表再用join方法合并成后缀表达式字符串,算法结束。

代码实现

from pythonds.basic.stack import Stack

def infixToPostfix(infixexpr):
    prec = {
   }
    prec["*"] = 3  # 记录操作符优先级
    prec["/"] = 3
    prec["+"] = 2
    prec["-"] = 2
    prec["("] = 1
    opStack = Stack()
    postfixList = []
    tokenList = infixexpr.split() # 解析到单词列表

    for token in tokenList:
        if token in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or token in "0123456789":
            postfixList.append(token)
        elif token == '(':
            opStack.push(token)
        elif token == ')':
            topToken = opStack.pop()
            while topToken != '(':
                postfixList.append(topToken)
                topToken = opStack.pop()
        else:
            while (not opStack.isEmpty()) and \
                (prec[opStack.peek()] >= prec[token]):
                postfixList.append(opStack.pop())
            opStack.push(token)
    while not opStack.isEmpty(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值