第三章 基本结构(上)
301 什么是线性结构
- 线性结构:是指有序的数据项集合,每个数据项都有唯一的前驱和后继。
(1)除了第一个没有前驱和最后一个没有后继。
(2)数据插入数据集中都是在某一个数据之后或者某一个数据之前。 - 不同的线性结构关键在于数据项的增减方式,主要有四种:栈(stack)、队列(queue)、
双端队列(Deque)、列表(list)
302 栈的python实现
- 栈的特性:
(1)加入和移除只发生在栈顶,另外一端为栈底。
(2)后进先出(LIFO):进入栈的时间越短离栈顶越近,进入栈的时间越久离栈底越近。
(3)主要特性:实现了数据次序的反转
(3)主要方法:
- 栈的python实现
采用list数据类型来实现栈。可以选择列表的末尾作为栈顶或者选择列表的开头作为栈顶,但是两种方案的时间复杂度不同,选择列表末尾作为栈顶的方案时间复杂度较优。
选择末尾作为栈顶:
class Stack(object):
def __init__(self):
# 采用列表定义栈
self.stack = list()
def is_empty(self):
# 判断栈是否为空
return self.stack == []
def push(self, value):
# 压入栈顶
return self.stack.append(value)
def peek(self):
# 返回栈顶数字
return self.stack[-1]
def size(self):
# 返回栈的长度
return self.stack.__len__()
def pop(self):
# 弹出栈顶数据
return self.stack.pop()
选择开头作为栈顶
class Stack(object):
def __init__(self):
# 采用列表定义栈
self.stack = list()
def is_empty(self):
# 判断栈是否为空
return self.stack == []
def push(self, value):
# 压入栈顶
return self.stack.insert(0, value)
def peek(self):
# 返回栈顶数字
return self.stack[0]
def size(self):
# 返回栈的长度
return self.stack.__len__()
def pop(self):
# 弹出栈顶数据
return self.stack.pop(0)
栈顶为list首端的版本,其push/pop的复杂度为O(n),栈顶为list尾端的实现,其push/pop的复杂度为O(1)
303 简单的括号匹配
思路:最先遇到的左括号,一定是匹配最后的右括号。同样,最后遇到的左括号,一定是匹配最先的右括号。即左括号和右括号的次序是相反的。
from Stack.Stack import Stack
def parcheck(string):
s = Stack()
for str in string:
if str is '(':
s.push(str)
else:
if s.is_empty():
return False
else:
s.pop()
return True if s.is_empty() else False
通用匹配:
from Stack.Stack import Stack
def universal_parcheck(string):
s = Stack()
for str in string:
if str in '([{':
s.push(str)
else:
if s.is_empty():
return False
else:
if not match(s.pop(), str):
return False
return True if s.is_empty() else False
def match(open, close):
opener = '([{'
closer = ')]}'
return opener.index(open) == closer.index(close)
304 十进制转二进制
除二余数法:将整数不断除以2,得到的余数就是从低到高的二进制数。得到的余数是从低到高的过程,但是输出确实从高到低的过程,这就是一种反序。
from Stack.Stack import Stack
def baseconvert(number):
s = Stack()
news_tring = ''
while number:
rem = number % 2
number = number // 2
s.push(rem)
# return ''.join(str(i) for i in s.stack[::-1])
while not s.is_empty():
news_tring += str(s.pop())
return news_tring
将除2改为除N就是将十进制转换为N进制的算法。
305 表达式转换
中缀表达式:操作符介于操作数中间的表达式。例:A+BC+D
前/后缀表达式:操作符位于操作数的前面或者后面的表达式,操作符决定次序。例:ABC*+D+
全括号表达式:在所有表达式的两边加上括号的表达式 例:(A+(B*C)+D)
将中缀表达式转化为前/后缀表达式:
- 将中缀表达式转换为全括号表达式
- 前缀表达式:遍历全括号表达式将操作符移到左括号,删除右括号。
后缀表达式:遍历全括号表达式将操作符移到右括号,删除左括号。
306 中缀表达式转化为后缀表达式
1.遍历中缀表达式时,需要暂存操作符,并根据优先级倒序操作符,即可能需要反转操作符,就可以利用栈的反转特性,用栈来暂存操作符。
2. 栈顶的操作符就是最近暂存进去的,当遇到一个新的操作符,就需要跟栈顶的操作符比较下优先级,再行处理。
3. 流程:
(1)创建空栈、空列表用来暂存操作符、操作数。
(2)遍历全括号表达式,如果是操作数就存入列表,如果是操作符、左括号就存入栈(需要比较优先级),如果是右括号,就弹出栈顶到列表,直到遇到了左括号。
(3)全括号表达式遍历完时,将栈里剩余的所有操作符弹出到列表中。
(4)列表里面的各个元素连接就是所求的后缀表达式。
import string
from Stack.Stack import Stack
def infixtopostfix(infixexpr):
prec = {'*': 3, '/': 3, '+': 2, '-': 2, '(': 1}
s = Stack()
l = list()
for token in infixexpr:
if token in string.ascii_uppercase or token in '0123456789':
l.append(token)
elif token is '(':
s.push(token)
elif token is ')':
top_token = s.pop()
while top_token != '(':
l.append(top_token)
top_token = s.pop()
else:
while (not s.is_empty()) and (prec[s.peek()] >= prec[token]):
l.append(s.pop())
s.push(token)
while not s.is_empty():
l.append(s.pop())
return ''.join(l)
307 后缀表达式求值
遍历后缀表达式时,需要暂存操作数,再读取操作符,但是在遇到操作符为-或者/时,需要将操作数顺序对调。计算的结果也需要推入栈中用于后续的数据操作,最后栈中只剩了一个数字,就是最后的结果。
流程:从左到右扫描单词列表如果单词是一个操作数,将单词转换为整数int,压入栈顶。如果单词是一个操作符(*/±),就开始求值,从栈顶弹出2个操作数,先弹出的是右操作数,后弹出的是左操作数,计算后将值重新压入栈顶
代码:
from Stack.Stack import Stack
def do_math(token, o1, o2):
if token is '/':
return o2 / o1
elif token is '-':
return o2 - o1
elif token is '+':
return o2 + o1
elif token is '*':
return o2 * o1
def postfixEval(postfixexpr):
s = Stack()
for token in postfixexpr:
if token in '0123456789':
s.push(int(token))
else:
o1 = s.pop()
o2 = s.pop()
s.push(do_math(token, o1, o2))
return s.pop()