第一题:设计一个有get_min(get_max)功能的栈:
要求:实现栈的基本功能,并可以返回栈最小(大)元素的操作。并且pop,push,get_min(get_max)操作的时间复杂度都是O(1)。
1.先用python实现基本的栈功能,包括push,pop,peek,is_empty功能
# python栈
class Stack(object):
def __init__(self):
self.stack = []
def push(self, value): # 进栈
self.stack.append(value)
def pop(self): # 出栈
if self.stack:
return self.stack.pop()
else:
raise LookupError('stack is empty!')
def is_empty(self): # 如果栈为空
return not bool(self.stack)
def peek(self):
# 取出目前stack中最新的元素
return self.stack[-1]
2.使用两个栈来实现所要求的功能,有以下两种最优方案:
# 方案一
class MyStack1(object):
def __init__(self):
# 使用两个栈,stackData保存栈的元素,stackMin保存元素中的最小值
self.stackData = Stack()
self.stackMin = Stack()
def push(self, new_num):
# push规则:如果stackMin为空,或者new_num小于stackMin的栈顶值,则将new_num压入stackMin。
# 可以保证stackMin栈顶始终是最小值,并且从栈顶到栈底保持从小到大。
if self.stackMin.is_empty() or new_num<= self.get_min():
self.stackMin.push(new_num)
self.stackData.push(new_num)
def pop(self):
# pop规则:弹出stackData的栈顶元素记为value,如果stackMin栈顶和value相等(不可能小于),则弹出
if self.stackData:
raise LookupError('stack is empty')
value = self.stackData.pop()
if value == self.get_min():
self.stackMin.pop()
return value
def get_min(self):
# 由上面的压入和弹出规则,可以保证始终会取出stackData中的最小值
if self.stackMin:
raise LookupError('stack is empty')
return self.stackMin.peek()
# 方案二
class MyStack2(object):
def __init__(self):
self.stackData = Stack()
self.stackMin = Stack()
def push(self, new_num):
# push规则:与第一种相比,当stackMin不为空且new_num不小于stackMin栈顶时,重复压入stackMin栈顶
if self.stackData.is_empty() or new_num< self.get_min():
self.stackMin.push(new_num)
else:
newMin = self.stackMin.peek()
self.stackMin.push(new_num)
self.stackData.push(new_num)
def pop(self):
# pop规则:因为push保证stackMin始终有值,所以可以直接弹出stackMin的栈顶
if self.stackData.is_empty():
raise LookupError('stack is empty!')
self.stackMin.pop()
return self.stackData.pop()
def get_min(self):
if self.stackMin.is_empty():
raise LookupError('stack is empty!')
return self.stackMin.peek()
方案选择:方案一和二时间复杂度都是O(1),空房复杂度都是O(n)。第一种方案在压入stackMin时更省空间,而弹出时更废时间;第二种方案在压入时更废空间,而弹出时稍快。
第二题:由两个栈实现一个队列
要求:支持基本队列操作(add,poll,peek)
设计:使用两个栈,压入第一个栈时,先进后出,再把第一个栈的数据压入第二个栈,顺序就再反过来变成先进先出了。
关键点:1.如果在用户弹出操作之前,第一个栈没有一次性压入第二个栈,那么就会错乱。2.如果第一个栈在传入第二个栈之前,第二个栈有残留值还没被弹出,那么也可能会错乱。
简单的设计极易发生上面错误,为了避免出现错误,设计最优解如下
class TwoStackQueue(object):
def __init__(self):
self.stackPush = Stack()
self.stackPop = Stack()
def _push_to_pop(self):
# 如果第二个栈为空,那么将第一个栈的值全部压入第二个栈,不为空,则等待下次全部被弹出
if self.stackPop.is_empty():
while not self.stackPush.is_empty():
self.stackPop.push(self.stackPush.pop())
def add(self, push_int):
# 每次添加,两个栈都进行一次判断和相应操作
self.stackPush.push(push_int)
self._push_to_pop()
def poll(self):
if self.stackPop.is_empty() and self.stackPush.is_empty():
raise LookupError('queue is empty!')
self._push_to_pop()
return self.stackPop.pop()
def peek(self):
if self.stackPop.is_empty() and self.stackPush.is_empty():
raise LookupError('queue is empty')
self._push_to_pop()
return self.stackPop.peek()
第三题:逆序一个栈
要求:只能用递归函数和栈,不能使用其他数据结构
设计:先实现一个递归函数,功能为返回栈底元素并移除。再实现第二个递归函数,每次调用第一个函数都会返回栈底元素,并递归推入栈。
def get_and_remove_last_element(stack):
result = stack.pop()
if stack.is_empty():
return result
else:
last = get_and_remove_last_element(stack)
stack.push(result)
return last
def reverse(stack):
if stack.is_empty():
return
i = get_and_remove_last_element(stack)
reversed(stack)
stack.push(i)
第四题:通过栈实现另一个栈的排序
def sort_stack_by_stack(stack: Stack):
auxilliary = Stack()
while not stack.is_empty():
temp= stack.pop()
while not auxilliary.is_empty() and auxilliary.peek() < temp:
stack.push(auxilliary.pop())
auxilliary.push(temp)
while not auxilliary.is_empty():
stack.push(auxilliary.pop())