栈的实现与应用.md

一、栈的实现

  • 方法一:就使用list即可


先进后出

  • 方法二:定义Stack,抽象出栈
class Stack:
	#栈的python实现
	def __init__(self):
		self.items = []

	def push(self, item):
		#append操作O(1)
		self.items.append(item)

	def pop(self):
		return self.items.pop()

	def peek(self):
		return self.items[-1]

	def isEmpty(self):
		return self.items == []

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

二、栈的应用

2.1平衡括号问题
#经典使用stack的场景:平衡括号问题
from stack import Stack

def is_balance(mystr):
	s = Stack()
	#默认是对称的
	balance = True
	for item in mystr:
		if item == '(':
			s.push(item)
		elif item == ')':
			if s.isEmpty():
				#右括号多于左括号的情况
				balance = False
			s.pop()
	if not s.isEmpty():
		#左括号多于右括号的情况
		balance = False
	return balance

if __name__ == '__main__':
	print(is_balance('((()))'))
	print(is_balance('(()'))
2.2广义的平衡问题
#经典使用stack的场景:平衡括号问题
from stack import Stack

def is_balance(mystr):
	s = Stack()
	#默认是对称的
	balance = True

	for item in mystr:
		if item in sym_dict.keys():
			s.push(item)
		elif item in sym_dict.values():
			if s.isEmpty():
				#右括号多于左括号的情况
				balance = False
			elif sym_dict[s.peek()] == item:
				s.pop()
				
	if not s.isEmpty():
		#左括号多于右括号的情况
		balance = False
	return balance

if __name__ == '__main__':
	sym_dict = {
		'(': ')',
		'{': '}',
		'[': ']',
	} 
	print(is_balance('{{([][])}()}'))
	print(is_balance('[{()]'))
2.3 任意进制转换问题

十进制转换到其他进制时,存在取余最后求反的过程,这个求反过程如果利用stack这种数据结构,可以节省reverse(list)的O(n)操作!

from stack import Stack
from functools import reduce

def dec_to_any(num, base=2):
	s = Stack()
	symbol_bank = '0123456789ABCDE'
	#取余数,压入stack
	while num > 0:
		item = num % base
		s.push(item)
		#除法取整必须使用 //
		num = num // base

	#pop并拼凑出字符串
	result = ''
	for i in range(s.size()):
		result += symbol_bank[s.pop()]
	return result

def any_to_dec(num, base=2):
	s = Stack()
	symbol_bank = '0123456789ABCDE'
	result = 0
	i = 0
	for item in str(num):
		s.push(item)
	for i in range(s.size()):
		item = s.pop()
		result += symbol_bank.index(item) * base ** i
	return result

def conversion(num, from_base, to_base):
	temp = any_to_dec(num, from_base)
	return dec_to_any(temp, to_base)

if __name__ == '__main__':
	print(dec_to_any(25,2))
	print(dec_to_any(25,16))
	print(any_to_dec(11001,2))
	print(any_to_dec(19,16))
	print(conversion(11001,2,8))
  • 注意:

使用reverse来翻转list比使用stack快10000倍,单纯的翻转操作肯定选reverse,但是stack在入栈以及出栈的时候可以进行一系列的操作,对于某一类问题非常合适!!

from stack import Stack
import timeit
from timeit import Timer

def reverse_list1(mylist):
	reversed(mylist)

def reverse_list2(mylist):
	s = Stack()
	result = []
	for i in mylist:
		s.push(i)
	for i in range(s.size()):
		mylist[i] = s.pop()

if __name__ == '__main__':
	a = list(range(10000))
	t1 = Timer('reverse_list1(a)', 'from __main__ import reverse_list1,a')
	print(t1.timeit(number=100))
	t2 = Timer('reverse_list2(a)', 'from __main__ import reverse_list2,a')
	print(t2.timeit(number=100))

2.4中缀表达式转换
2.4.1 转为后缀表达式

AB+CD -> ABCD+

特征:

  • ABCD这些操作数的相对位置不变,直接用一个list逐个存储即可
  • 操作符’+’优先级小于C与D之间的*,故结果中这两个操作符位置肯定要颠倒,想到用stack的特性
  • 括号也要压入stack,括号内的操作符相当于一个子过程,只有当‘)‘出现,才可以pop从左括号到右括号的所有元素,继续进行程序

解题思路:

以 "A * B + C * D"为例

#stack综合运用
#启发:stack不仅在入栈、出栈过程中可以做文章,比如筛选等操作,
#同时,不一定要全部元素入栈后全部再出栈,可以入一部分、出一部分、再入
from stack import Stack

def postpix(exp):
	opstack = Stack()
	out_list = []
	exp = exp.split(' ')
	for item in exp:
		if item in operator_bank:
			if opstack.isEmpty():
				opstack.push(item)
			elif operator_bank[opstack.peek()] <= operator_bank[item]:
				opstack.push(item)
			else:
				#当opstack非空,且peek优先值大于当前item,则将opstack'('之后所有操作符pop并添加到out_list,不包括左括号
				while not opstack.isEmpty():
					top = opstack.pop()
					if top == '(':
						opstack.push('(')
						break
					out_list.append(top)
				#别忘了先把当前item push进去
				opstack.push(item)
		#当遇到')',将opstack中元素pop并添加到out_list,知道遇到‘(’,左括号也要pop
		elif item == ')':
			while not opstack.isEmpty():
				top = opstack.pop()
				if top == '(':
					break
				out_list.append(top)
		#当字符为操作数
		else:
			out_list.append(item)
	
	while(not opstack.isEmpty()):
		top = opstack.pop()
		out_list.append(top)
	return ''.join(out_list)


if __name__ == '__main__':
	operator_bank = {
		'(': 2,
		'*': 1,
		'/': 1,
		'+': 0,
		'-': 0,
	}
	operand_bank = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
	# print(postpix("A*B+C*D"))
	print(postpix("( A + B ) * C - ( D - E ) * ( F + G )"))
2.4.2 后缀表达式求值

思路:

以 "7 8 + 3 2 + /"为例

实现:

from stack import Stack

def _do_math(stack, operator):
	#先pop出的数字为第二个操作数!!!一定要注意顺序
	operand2 = int(stack.pop())
	operand1 = int(stack.pop())
	if operator == '+':
		return operand1 + operand2
	if operator == '-':
		return operand1 - operand2
	if operator == '*':
		return operand1 * operand2
	if operator == '/':
		return operand1 / operand2

def evaluate_postfix(exp):
	"""求后缀算数表达式的值"""
	operand_stack = Stack()
	exp_list = exp.split(' ')
	for item in exp_list:
		if item in operand_bank:
			operand_stack.push(item)
		else:
			temp = _do_math(operand_stack,item)
			operand_stack.push(temp)
	return operand_stack.pop()


if __name__ == '__main__':
	operand_bank = '123456789'
	print(evaluate_postfix('7 8 + 3 2 + /'))

三、参考

http://interactivepython.org/runestone/static/pythonds/BasicDS/toctree.html

猜你喜欢

转载自blog.csdn.net/qq_27366789/article/details/82947631
今日推荐