大多数人都被这样简单的一道栈面试题给摩擦过?是吗

欢迎关注微信公众号:简说Python
关注后回复:1024,可以领取精选编程学习电子书籍。

这两天和几个朋友组了个互相督促学习群,想着督促一下自己学习,也督促自己的原创输出,其实很多时候都是懒,真不是没有东西可以写了,这不,我在我的免费知识星球简说编程里开了个新的标签日常编程问题,后面我会把自己学习工作中遇到的一些问题和解决方法记录到里面,有些可以扩展的点,我会写到微信公众号里。
我定的目标是:

我简单写了个规则,大家说可以,然后,我们就开始吧,我习惯把该做的事情提前一天做(如果有时间的话)。

今天给大家分享的书籍《Python程序员面试算法宝典》第二章第三小节:如何翻转栈。

如果你是第一次看,也许,你可以看看本系列下面的文章:
Python数据结构:链表合集(12+7),复现几遍,包你学会
Smaller Python数据结构:自己动手实现栈
Smaller Python数据结构:自己动手实现队列
Smarter Python数据结构:如何翻转栈
Smarter Python数据结构:根据入栈序列来判断可能的出栈序列
Smarter Python数据结构:利用O(1)的时间复杂度求栈中最小元素

今日问题

"""
目标:写一个程序,如何用两个栈模拟队列操作

Goal: Write a program, how to simulate queue operation with two stacks.
"""

老板: 今天考考你,怎么用两个栈来实现队列功能?
老表: 额~我想想。

解题

首先我们之前已经写好了栈的基本操作,在b_1_implementation_stack.py文件中,所以我们先导入这两个包。

from StackQueueHash.b_1_implementation_stack import LinkStack

另外我们要知道栈与队列的异同点,这样才知道该如何用栈实现队列或者用队列实现栈:

方法:一个栈入队列,一个栈出队列

"""
Method One : 一个栈入队列,一个栈出队列

核心思想:入队列时,直接把元素压入入队列栈;出队列时,先把入队列栈元素转移到出队
列栈,获取出队列栈栈顶,即为出队列元素,将剩余元素从出队列栈再转移回入队列栈。


Method one: a stack in queue, a stack out queue

Core idea: when entering the queue, directly press the elements into
 the queue stack; when leaving the queue, first transfer the elements 
 into the queue stack to get the top of the queue stack, that is, the 
 elements out of the queue, and then transfer the remaining elements 
 back to the queue stack.
"""

思路图解

代码

class MyQueue:
    def __init__(self):
        self.entry = LinkStack()  # 入队列 栈
        self.out = LinkStack()  # 出队列 栈

    # 不a栈的元素转移到b栈
    def transfer_stack(self, a, b):
        while not a.stack_is_empty():  # 将a栈元素倒出,存入b栈
            top = a.get_stack_top()  # 获取a栈栈顶
            a.stack_pop()  # 出栈
            b.stack_push(top)  # 栈顶压入b栈

    # 入队列
    def queue_push(self, x):
        self.entry.stack_push(x)  # 元素直接加入 入队列栈

    # 出队列
    def queue_pop(self):
        # 出队列时,先把入队列栈转移到出队列栈
        self.transfer_stack(self.entry, self.out)
        # 获取出队列栈栈栈顶,即为出队列元素
        out_element = self.out.get_stack_top()
        self.out.stack_pop()
        # 出栈,实现出队列操作
        self.transfer_stack(self.out, self.entry)
        # 将剩余元素从出队列栈再转移回入队列栈
        return out_element  # 返回出队列元素(队首)

老板: 嗯,小伙子不错,知道把有些共通的方法封装提炼出来(说的是栈数据倒入函数transfer_stack),那你还有办法可以把算法优化一下吗?你觉得你的代码里有没有不必要的步骤?
老表: 我想想~

优化1

"""
优化1:入队列时,先判断入队列栈是否为空,如果为空,则把出队列栈元素倒回
入队列栈,然后再入栈,如果不为空,说明元素全在入队列栈,则直接入栈即可;
出队列时,先判断出队列栈是否为空,如果为空,则将入队列栈元素倒入出队列栈,
然后取出栈顶即可,如果不为空,说明上次操作也是出队列,则直接获取出队列栈
栈顶即可。

这样做的好处是:如果连续操作是相同时,我们不需要来会把元素在入队列栈和出队
列栈倒来倒去。

Optimization 1: when entering the queue, first judge whether the
 queue stack is empty. If it is empty, then pour the elements out 
 of the queue stack back into the queue stack, and then push the 
 elements in the queue stack. If it is not empty, it means that 
 all elements are in the queue stack, then directly push the elements 
 in the queue stack. When leaving the queue, first judge whether the 
 queue stack is empty. If it is empty, then pour the elements in the 
 queue stack into the queue stack, and then take out the top of the 
 stack , if it is not empty, it means that the last operation was also 
 out of the queue, then you can directly get out of the top of the queue 
 stack.

The advantage of this is that if the continuous operation is the same,
 we don't need to pour the elements back and forth in the queue stack 
 and out of the queue stack.
"""

思路图解

代码

class MyQueue1:
    def __init__(self):
        self.entry = LinkStack()  # 入队列 栈
        self.out = LinkStack()  # 出队列 栈

    # 不a栈的元素转移到b栈
    def transfer_stack(self, a, b):
        while not a.stack_is_empty():  # 将a栈元素倒出,存入b栈
            top = a.get_stack_top()  # 获取a栈栈顶
            a.stack_pop()  # 出栈
            b.stack_push(top)  # 栈顶压入b栈

    # 入队列
    def queue_push(self, x):
        if self.entry.stack_is_empty():
            self.transfer_stack(self.out, self.entry)
            # 把元素从出队列栈倒入入队列栈
            self.entry.stack_push(x)  # 元素入队列
        else:
            self.entry.stack_push(x)  # 元素直接加入入队列栈,入队列

    # 出队列
    def queue_pop(self):
        if self.out.stack_is_empty():
            self.transfer_stack(self.entry, self.out)
            # 把元素从入队列栈倒入出队列栈
        out_element = self.out.get_stack_top()
        # 获取栈顶,就是要出队列的元素
        self.out.stack_pop()
        # 出栈,完成出队列
        return out_element  # 返回出队列元素

老板: 嗯,小伙子不错,有点小灵敏哈~你觉得还能再优化吗?
老表: 可以,我想想~

优化2

"""
优化2:入队列时,直接入栈即可;
出队列时,先判断出队列栈是否为空,如果为空,则将入队列栈元素倒入出队列栈,
然后取出栈顶即可,如果不为空,说明上次操作也是出队列,则直接获取出队列栈
栈顶即可。

这样做的好处是:进一步简化代码,去掉不必要的转换。


Optimization 2: when entering the queue, you can directly enter the stack;
When leaving the queue, first judge whether the queue stack is empty. If
 it is empty, pour the elements of the queue stack into the queue stack, 
 and then take out the top of the stack. If it is not empty, it means that 
 the last operation was also out of the queue, then take out the top of 
 the queue stack directly.
 
The advantage of this is to further simplify the code and eliminate unnecessary transformations.
"""

思路图解

当然,如果你要遍历整个链表,就需要栈A和B的配合了,出队列时,首先你得判断B栈是否为空,不为空的话,先pop B栈,然后再吧A 栈元素push B栈再pop ,不难看出,出队列是无法修改的(我个人觉得),因为由于队列先进先出,所以始终都要经过B栈才能出队列,除非只有一个元素的时候。

代码

class MyQueue2:
    def __init__(self):
        self.entry = LinkStack()  # 入队列 栈
        self.out = LinkStack()  # 出队列 栈

    # 不a栈的元素转移到b栈
    def transfer_stack(self, a, b):
        while not a.stack_is_empty():  # 将a栈元素倒出,存入b栈
            top = a.get_stack_top()  # 获取a栈栈顶
            a.stack_pop()  # 出栈
            b.stack_push(top)  # 栈顶压入b栈

    # 入队列
    def queue_push(self, x):
        self.entry.stack_push(x)  # 元素直接加入入队列栈,入队列

    # 出队列
    def queue_pop(self):
        if self.out.stack_is_empty():
            self.transfer_stack(self.entry, self.out)
            # 把元素从入队列栈倒入出队列栈
        out_element = self.out.get_stack_top()
        # 获取栈顶,就是要出队列的元素
        self.out.stack_pop()
        # 出栈,完成出队列
        return out_element  # 返回出队列元素

老板: 嗯,小伙子很不错。
老表: 谢谢老板。

测试代码

# 当然,也许还有别的方法,比如建一个辅助的链表
# 欢迎你说出你的想法

# 程序入口,测试函数功能
if __name__ == "__main__":
    s = MyQueue2()  # 初始化一个MyQueue
    s.queue_push(1)
    s.queue_push(2)
    s.queue_push(3)
    s.queue_push(4)
    # 入队列
    print("当前队首元素(出队列)为:", s.queue_pop())
    s.queue_push(666)
    print("当前队首元素(出队列)为:", s.queue_pop())
    print("当前队首元素(出队列)为:", s.queue_pop())

运行结果

大多数人都被这样简单的一道栈面试题给摩擦过?是吗_第1张图片

本文代码思路来自书籍《Python程序员面试宝典》,书中部分代码有问题,文中已经修改过了,并添加上了丰厚的注释,方便大家学习,后面我会把所有内容开源放到Github上,包括代码,思路,算法图解(手绘或者电脑画),时间充裕的话,会录制视频。
希望大家多多支持。

大家好,我是老表
觉得本文不错的话,转发、留言、点赞,是对我最大的支持。

欢迎关注微信公众号:简说Python
关注后回复:1024,可以领取精选编程学习电子书籍。

你可能感兴趣的:(数据结构,Python)