leetcode2021年度刷题分类型总结(四)栈与队列 (python)

说明:这个系列是个人的刷题感想和笔记,主要参考自己的刷题记录和代码随想录

例一:232. 用栈实现队列

232. 用栈实现队列

方法一:仅靠pop\append函数

但是这个方法在self.pop()函数时用self._list.pop(0)、self.peek()函数用了self._list[0],明显违反了题目里面只能 使用标准的栈操作,属于用list的函数开了外挂

class MyQueue(object):

    def __init__(self):
        self._list=[]


    def push(self, x):
        """
        :type x: int
        :rtype: None
        """
        self._list.append(x)

    def pop(self):
        """
        :rtype: int
        """
        return self._list.pop(0)


    def peek(self):
        """
        :rtype: int
        """
        return self._list[0]



    def empty(self):
        """
        :rtype: bool
        """
        return not self._list



# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

在这里插入图片描述
(不知道为何,状态通过却显示22/21个通过测试用例,有知道的小伙伴欢迎评论区留言)

方法二:栈操作实现队列

这个方法属于用两个开口不同的栈,一个作为输入栈,一个作为输出栈来实现一个先进先出结构的队列。
push时只用把数压到输入栈中;
pop时先判断输入栈或者输出栈中是否有数,如果都没有可以直接返回None;如果输出栈中有值,则直接从输出栈中pop;否则的话将输入栈的数全部转移到输出栈中。但要注意,由于要求先入先出的结构,所以要求后压入输入栈的数要后输出,所以输入栈的数转移到输出栈中时,需要把顺序都调转过来,也就是self.stack_out.append(self.stack_in.pop())
peek时要求返回队列开头的元素,可以直接使用self.pop()得到答案,但是pop后需要把数补回去,直接self.stack_out.append(ans),因为输出栈的数后入先出,所以ans依旧排在队列第一个出的位置。

class MyQueue(object):

    def __init__(self):
        self.stack_in=[]
        self.stack_out=[]


    def push(self, x):
        """
        :type x: int
        :rtype: None
        """
        self.stack_in.append(x)

    def pop(self):
        """
        :rtype: int
        """
        if self.empty():
            return None
        if self.stack_out:
            return self.stack_out.pop()
             
        else:
            if self.stack_in:
                for i in range(len(self.stack_in)):
                    self.stack_out.append(self.stack_in.pop())
                return self.stack_out.pop()
                       
    def peek(self):
        """
        :rtype: int
        """
        ans=self.pop()
        self.stack_out.append(ans)
        return ans



    def empty(self):
        """
        :rtype: bool
        """
        return not (self.stack_in or self.stack_out)



# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

在这里插入图片描述

例二:225. 用队列实现栈

225. 用队列实现栈
写这题时确实最开始想的是能不能同样用输入输出队列来实现一个栈,但是仔细想想,由于队列先进先出的特性,没有办法像栈那样可以利用两个栈来把数的顺序调换。所以输入输出队列来实现栈的设想不可行。

方法一:双队列实现栈

用两个队列来实现一个栈的正确思路是:一个输入队列一个暂存队列,将暂存队列作为暂存和输出区域。
push操作时正常对输入队列append
当遇到pop操作时,将输入队列中最后一个数前面的数都放到暂存队列,然后输入队列中的数(也就是要输出的数)和暂存队列中的数交换,最后输出暂存队列中的数
top() 返回栈顶元素就是返回最后输入的元素
!!!需要注意的有输入队列的数转移到暂存队列中时,由于先进先出的关系不能变,所以要写成self.queue_in.popleft()(写成self.queue_in.pop()就是先进后出了)

class MyStack(object):

    def __init__(self):
        self.queue_in=deque()
        self.queue_backup=deque()


    def push(self, x):
        """
        :type x: int
        :rtype: None
        """
        self.queue_in.append(x)


    def pop(self):
        """
        :rtype: int
        """
        if self.empty():
            return None
        for i in range(len(self.queue_in)-1):
            self.queue_backup.append(self.queue_in.popleft())
        self.queue_in,self.queue_backup=self.queue_backup,self.queue_in


        return self.queue_backup.pop()
        

    def top(self):
        """
        :rtype: int
        """
        if self.empty():
            return None
        return self.queue_in[-1]


    def empty(self):
        """
        :rtype: bool
        """
        return not self.queue_in

说明

为什么不用Queue或者list
Python普通的Queue或SimpleQueue没有类似于peek的功能
也无法用索引访问,在实现top的时候较为困难。
用list可以,但是在使用pop(0)的时候时间复杂度为O(n)
因此这里使用双向队列,我们保证只执行popleft()和append(),因为deque可以用索引访问,可以实现和peek相似的功能
在这里插入图片描述

方法二:单队列实现栈

其他部分跟方法一代码一致,只是__init__时只用初始化一个 self.queue_in队列,还有pop()时把队列前len(self.queue_in)-1个数popleft()后添加到队列尾部,然后popleft()最尾部的一个数,也就是“栈顶”元素

class MyStack(object):

    def __init__(self):
        self.queue_in=deque()
        

    def push(self, x):
        """
        :type x: int
        :rtype: None
        """
        self.queue_in.append(x)


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

    def top(self):
        """
        :rtype: int
        """
        if self.empty():
            return None
        return self.queue_in[-1]


    def empty(self):
        """
        :rtype: bool
        """
        return not self.queue_in



# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

在这里插入图片描述

例三:347. 前 K 个高频元素

347. 前 K 个高频元素

方法一:字典+sorted函数

初始化一个字典,字典的键是nums中的不同数,字典的值是不同数分别出现的频次。按照字典值降序排列的顺序来取出字典中的items。最后取出前k个items中的键即可

class Solution(object):
    def topKFrequent(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """

        n=len(nums) 
        m=max(nums)
        cnt=dict.fromkeys(nums,0)
       
        for i in range(n):
            cnt[nums[i]]+=1
        #cnt键代表nums中数值,cnt中值代表此值出现频次,现在相当于求数组中最大的K个值对应的下标
        #cnt[1]=3 cnt[2]=2 cnt[3]=1
        #字典对值降序排序,注意可能有多个值相等,返回键
        desc_value=sorted(cnt.items(), key=lambda k: k[1],reverse=True)  # k[1] 取到字典的值。
        
        res = []
        for i in range(k):
            res.append(desc_value[i][0])

        return res

在这里插入图片描述

方法二:字典+桶排序

  1. 初始化一个字典,字典的键是nums中的不同数,字典的值是不同数分别出现的频次,同方法一
  2. 桶排序,将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标
  3. 此时数组下标越靠后,数的出现频次越高,所以由后往前取k个元素
class Solution(object):
    def topKFrequent(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        "想使用桶排序,但是问题在于nums中的数可以为负数"
        n=len(nums) 
        m=max(nums)
        cnt=dict.fromkeys(nums,0)
        #cnt键代表nums中数值,cnt中值代表此值出现频次,现在相当于求数组中最大的K个值对应的下标
        #cnt[1]=3 cnt[2]=2 cnt[3]=1       
        for i in range(n):
            cnt[nums[i]]+=1
        #桶排序
        #将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标
        setlist=[[] for _ in range(n+1)]
        for key in cnt.keys():
            i=cnt[key]
            setlist[i].append(key)

        res=[]

        for i in range(len(setlist)-1,-1,-1):
            if len(res)<k:
                if setlist[i]==None:
                    continue
                res+=setlist[i]
            else:
                break
        return res

在这里插入图片描述

方法三:堆

例四:20. 有效的括号

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

输入:s = “([)]”
输出:false

class Solution(object):
    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """
        stack=[]
        stack_size=0 #记录栈中数据多少
        n=len(s)

        for ss in s:
            if stack_size==0: #当栈中为空的时候,填数;不能直接stack[stack_size-1]访问,会报错
                stack.append(ss)
                stack_size+=1
                continue

            if (ss==")" and stack[stack_size-1]=="(") or (ss=="}" and stack[stack_size-1]=="{") or (ss=="]" and stack[stack_size-1]=="["):
                stack.pop()
                stack_size-=1
            else:
                stack.append(ss)
                stack_size+=1

        return stack_size==0

说明

括号匹配是使用栈解决的经典问题。就像我们在写代码的过程中,要求括号的顺序是一样的,有左括号,相应的位置必须要有右括号。
如果还记得编译原理的话,编译器在 词法分析的过程中处理括号、花括号等这个符号的逻辑,也是使用了栈这种数据结构。

例五:1047. 删除字符串中的所有相邻重复项

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

输入:“abbaca”
输出:“ca”
解释:
例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

class Solution(object):
    def removeDuplicates(self, s):
        """
        :type s: str
        :rtype: str
        """
        #栈,十年如一日专注于从前到后的消消乐
        stack=[]
        # s=list(s)

        for ss in s:
            n=len(stack)
            if n==0:
                stack.append(ss)
                continue
            if ss==stack[n-1]:
                stack.pop()

            else:
                stack.append(ss)

        return "".join(stack)

例六:150. 逆波兰表达式求值

根据 逆波兰表示法,求表达式的值。

有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

注意 两个整数之间的除法只保留整数部分。

可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

输入:tokens = [“10”,“6”,“9”,“3”,“+”,“-11”,““,”/“,””,“17”,“+”,“5”,“+”]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

class Solution(object):
    def evalRPN(self, tokens):
        """
        :type tokens: List[str]
        :rtype: int
        """
        ##跟图灵机的运算原理一样
        tmp=[] #用作存储中间计算数
        computes=[] #遇到 +、-、*、/才开始计算,每个算符总是对应最近的两个数据
        #遇到-和/号要注意,先被pop()的数算作被除数或者被减数

        for token in tokens:
            if token=="+":
                x=int(tmp.pop())
                y=int(tmp.pop())
                tmp.append(x+y) 
                # print(tmp)
                continue
            elif token=="-":
                x=int(tmp.pop())
                y=int(tmp.pop())
                tmp.append(y-x)
                continue
            elif token=="*":
                x=int(tmp.pop())
                y=int(tmp.pop())
                tmp.append(x*y)
                # print(tmp)
                continue
            elif token=="/":
                x=int(tmp.pop())
                y=int(tmp.pop())
                if y//x<0 and y%x!=0: #之所以加这个,是因为6/-132算作0而不是-1。y//x向下取整得到-1
                    tmp.append(y//x+1)
                else:
                    tmp.append(y//x)
                # print(tmp)
                continue
            else:
                tmp.append(token)
                # print(tmp)

        # print(tmp)

        return int(tmp[0])
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值