2023/7/21--代码随想录算法训练营day10|第五章 栈与队列 part01 理论基础、232.用栈实现队列、225.用队列实现栈

理论基础

  • 了解栈和队列的内部实现机制
  • 队列:先进先出、栈:先进后出
    2023/7/21--代码随想录算法训练营day10|第五章 栈与队列 part01 理论基础、232.用栈实现队列、225.用队列实现栈_第1张图片
    2023/7/21--代码随想录算法训练营day10|第五章 栈与队列 part01 理论基础、232.用栈实现队列、225.用队列实现栈_第2张图片
  • Python中stack 是容器么? 不是,四大容器list、dict、set、tuple元组
  • 我们使用的stack是属于哪个版本的STL? SGI STL
    • 我们使用的STL中stack是如何实现的? push,pop接口
  • stack 提供迭代器来遍历stack空间么? 不提供

】:先进后出的数据结构。栈提供push和pop等接口,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器。(不像是set或者map提供迭代器iterator来遍历所有元素)
栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
所以STL中往往不被归类为容器,而被归类为container adapter (容器适配器)。
我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的底层结构。(deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。)
SGI STL中 队列底层实现缺省情况下一样使用deque实现的。

队列】:先进先出的数据结构,同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。
所以STL 队列也不被归类为容器,而被归类为container adapter( 容器适配器)
2023/7/21--代码随想录算法训练营day10|第五章 栈与队列 part01 理论基础、232.用栈实现队列、225.用队列实现栈_第3张图片

232. 用实现队列

【状态】:完全没想出来,看着答案写的,不对不说,需要注意对pop操作的代码,以及peek对pop代码的抽象运用。
【题目】:2023/7/21--代码随想录算法训练营day10|第五章 栈与队列 part01 理论基础、232.用栈实现队列、225.用队列实现栈_第4张图片
【思路】:用栈【先进后出】模拟队列【先进先出】的行为,需要两个栈,一个输入栈,一个输出栈,注意输出栈和输入栈的关系。
** 分析**:

  • push 进输入栈
  • peek和pop,输出栈为空,要把输入栈的所有元素都导入到输出栈,再从输出栈弹出数据;输出栈不为空,直接从输出栈弹出数据就行。
  • 队列为空,输出栈和输出栈都为空
    时间复杂度:push和empty O(1) ,pop和peek O(n)
    空间复杂度:O(n)
 class MyQueue:
    def __init__(self)    #in主要负责push,out主要负责pop
        self.stack_in = []
        self.stack_out = []

    def push(self, x: int) -> None:  #有新元素进来,就往in里面push
        self.stack_in.append(x)
        
    def pop(self) -> int:#Removes the element from in front of queue and returns that element.
        if self.empty():
            return None
        
        if self.stack_out:  #输出栈有,就直接弹出输出栈的元素就行
            return self.stack_out.pop()
        else:
            for i in range(len(self.stack_in)):#否则,输入栈弹出,进到输入栈
                self.stack_out.append(self.stack_in.pop())
            return self.stack_out.pop()

    def peek(self) -> int:  #掌握代码的抽象复用,peek()目的得到栈顶的元素
        ans = self.pop()
        self.stack_out.append(ans)
        return ans

    def empty(self) -> bool: #只要in或者out有元素,说明队列不为空
        return not (self.stack_in or self.stack_out)

225. 用队列实现

  • 建议大家掌握一个队列的方法,更简单一些
    【状态】:完全不会,直接看着答案的,注意这里popleft,另外在peek,直接用的que[-1]
    【题目】:2023/7/21--代码随想录算法训练营day10|第五章 栈与队列 part01 理论基础、232.用栈实现队列、225.用队列实现栈_第5张图片
    【思路】:如果考虑之前的在用两个栈,实现队列的方法。现在考虑两个队列,一个输出队列,一个输出队列,实现栈。因为队列,不改变进入进入顺序,所以这样的想法不行。
    队列模拟栈,其实一个队列就够了。一个队列作用,一个队列备份保存信息。
    用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。
    时间复杂度:push为O(n),其他O(1)
    空间复杂度:O(n)

优化:其实这道题目就是用一个队列就够了。一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时再去弹出元素就是栈的顺序
时间复杂度:push为O(n),其他O(1)
空间复杂度:O(n)
【一个队列实现栈】

class MyStack:
    def __init__(self):
        self.que = deque()
    def push(self, x: int) -> None:
        self.que.append(x)

    def pop(self) -> int:
        if self.empty():
            return None
        for i in range(len(self.que)-1):
            self.que.append(self.que.popleft())
        return self.que.popleft()

    def top(self) -> int:
        if self.empty():
            return None
        return self.que[-1]

    def empty(self) -> bool:
        return not self.que

【两个队列实现栈】

from collections import deque

class MyStack:

    def __init__(self):
        """
        Python普通的Queue或SimpleQueue没有类似于peek的功能
        也无法用索引访问,在实现top的时候较为困难。

        用list可以,但是在使用pop(0)的时候时间复杂度为O(n)
        因此这里使用双向队列,我们保证只执行popleft()和append(),因为deque可以用索引访问,可以实现和peek相似的功能

        in - 存所有数据
        out - 仅在pop的时候会用到
        """
        self.queue_in = deque()
        self.queue_out = deque()

    def push(self, x: int) -> None:
        """
        直接append即可
        """
        self.queue_in.append(x)


    def pop(self) -> int:
        """
        1. 首先确认不空
        2. 因为队列的特殊性,FIFO,所以我们只有在pop()的时候才会使用queue_out
        3. 先把queue_in中的所有元素(除了最后一个),依次出列放进queue_out
        4. 交换in和out,此时out里只有一个元素
        5. 把out中的pop出来,即是原队列的最后一个
        
        tip:这不能像栈实现队列一样,因为另一个queue也是FIFO,如果执行pop()它不能像
        stack一样从另一个pop(),所以干脆in只用来存数据,pop()的时候两个进行交换
        """
        if self.empty():
            return None

        for i in range(len(self.queue_in) - 1):
            self.queue_out.append(self.queue_in.popleft())
        
        self.queue_in, self.queue_out = self.queue_out, self.queue_in    # 交换in和out,这也是为啥in只用来存
        return self.queue_out.popleft()

    def top(self) -> int:
        """
        1. 首先确认不空
        2. 我们仅有in会存放数据,所以返回第一个即可
        """
        if self.empty():
            return None
        
        return self.queue_in[-1]


    def empty(self) -> bool:
        """
        因为只有in存了数据,只要判断in是不是有数即可
        """
        return len(self.queue_in) == 0

你可能感兴趣的:(力扣刷题,算法)