栈和队列相关题目

栈先进后出,队列先进先出。

题号题目难度
leetcode 20有效的括号简单
剑指 Offer 09用两个栈实现队列简单
leetcode 225用队列实现栈简单
leetcode 1047删除字符串中的所有相邻重复项简单
leetcode 1021删除最外层的括号简单

1 剑指 Offer 09. 用两个栈实现队列

题目描述

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

示例 1:

输入:
[“CQueue”,”appendTail”,”deleteHead”,”deleteHead”]
[[],[3],[],[]]
输出:[null,null,3,-1]

解题思路

题目只要求实现 加入队尾appendTail() 和 删除队首deleteHead() 两个函数的正常工作,因此我们可以设计栈 A 用于加入队尾操作,栈 B 用于将元素倒序,从而实现删除队首元素。

加入队尾 appendTail()函数: 将数字 val 加入栈 A 即可。
删除队首deleteHead()函数: 有以下三种情况。
当栈 B 不为空: B中仍有已完成倒序的元素,因此直接返回 B 的栈顶元素。
否则,当 A 为空: 即两个栈都为空,无元素,因此返回 -1−1 。
否则: 将栈 A 元素全部转移至栈 B 中,实现元素倒序,并返回栈 B 的栈顶元素。

twostackTobequeue

代码

# -*- coding: utf-8 -*-
class CQueue(object):
    def __init__(self):
        self.A=[]
        self.B=[]
    def appendTail(self, value):
        """
        :type value: int
        :rtype: None
        """
        self.A.append(value)
    def deleteHead(self):
        """
        :rtype: int
        """
        if self.B: return self.B.pop()#如果B栈有元素  则弹出栈顶元素即可
        if not self.A: return -1#B为空 并且A 为空  则返回-1
        #当A不为空的时候
        while self.A:
            self.B.append((self.A.pop()))#将A栈弹出  入B栈
        return self.B.pop()

复杂度分析

时间复杂度: appendTail()函数为 O(1) ;deleteHead() 函数在 N 次队首元素删除操作中总共需完成 N个元素的倒序。
空间复杂度: O(N)最差情况下,栈 A 和 B 共保存 N个元素。

2 leetcode225. 用队列实现栈

题目描述

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

注意:

你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:

输入:
[“MyStack”, “push”, “push”, “top”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

代码

class MyStack(object):
    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.A=deque()
        self.B=deque()
    def push(self, x):
        """
        Push element x onto stack.
        :type x: int
        :rtype: None
        """
        self.A.append(x)
    def pop(self):
        """
        Removes the element on top of the stack and returns that element.
        :rtype: int
        """
        size=len(self.A)-1#留一个元素
        while size>0:
            self.B.append(self.A.popleft())#将A的元素除了最左边的都弹入B中
            size=size-1
        res=self.A.popleft()#返回
        self.A,self.B=self.B,self.A##将que2和que1交换 que1经过之前的操作应该是空了
        #一定注意不能直接使用que1 = que2 这样que2的改变会影响que1 可以用浅拷贝
        return res
    def top(self):
        """
        Get the top element.
        :rtype: int
        """
        return self.A[-1]
    def empty(self):
        """
        Returns whether the stack is empty.
        :rtype: bool
        """
        if len(self.A)==0:
            return True
        else:
            return False

复杂度分析

时间复杂度:入栈操作O(n),其余操作都是 O(1)。
入栈操作需要将A 中的 n个元素出队,并入队 n+1个元素到B ,共有 2n+1 次操作,每次出队和入队操作的时间复杂度都是 O(1),因此入栈操作的时间复杂度是O(n)。
出栈操作对应将A的前端元素出队,时间复杂度是 O(1)。
获得栈顶元素操作对应获得 A的前端元素,时间复杂度是 O(1)。
判断栈是否为空操作只需要判断A是否为空,时间复杂度是 O(1)。

空间复杂度:O(n),其中 n 是栈内的元素。需要使用两个队列存储栈内的元素。

3 leetcode 20. 有效的括号

题目描述

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

有效000字符串需满足:

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

示例 1:

输入:s = “()”
输出:true
示例 2:

输入:s = “()[]{}”
输出:true

解题思路

1.1 待入栈元素在字典中无匹配元素即都是左括号 则 入栈

1.2 若待入栈元素在字典中有匹配元素

​ 1.2.1 stack不为空(防止s=”]”的情况) 并且 在栈顶匹配元素成功

​ 则 将栈顶元素pop出

​ 1.2.2 否则 直接返回False

isvalid

代码

class Solution(object):
    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """
        dic={')':'(','}':"{","]":"["}
        stack=[]
        for i in s:
            if dic.get(i) is None:#待入栈元素在字典中无匹配元素即都是左括号 则 入栈
                stack.append(i)
            else:##若待入栈元素在字典中有匹配元素
                if  stack and stack[-1]==dic.get(i): #并且stack不为空(防止s="]"的情况) 在栈顶匹配元素成功 则 将栈顶元素pop出
                    stack.pop()
                else:##例如    s="(]"
                    return False
        return len(stack)==0

复杂度分析

时间复杂度:O(n),其中 n是字符串 s的长度。

空间复杂度:O(n+∣Σ∣),其中 Σ 表示字符集,本题中字符串只包含 6 种括号,6∣Σ∣=6。栈中的字符数量为 O(n),而哈希表使用的空间为 O(∣Σ∣),相加即可得到总空间复杂度。

4 leetcode1047. 删除字符串中的所有相邻重复项

题目描述

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

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

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

示例:

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

解题思路

充分理解题意后,我们可以发现,当字符串中同时有多组相邻重复项时,我们无论是先删除哪一个,都不会影响最终的结果。因此我们可以从左向右顺次处理该字符串。

而消除一对相邻重复项可能会导致新的相邻重复项出现,如从字符串abba 中删除 bb 会导致出现新的相邻重复项aa 出现。因此我们需要保存当前还未被删除的字符。一种显而易见的数据结构呼之欲出:栈。我们只需要遍历该字符串,如果当前字符和栈顶字符相同,我们就贪心地将其消去,否则就将其入栈即可。

本题要删除相邻相同元素,其实也是匹配问题,相同左元素相当于左括号,相同右元素就是相当于右括号,匹配上了就删除。类似于leetcode 20. 有效的括号

那么再来看一下本题:可以把字符串顺序放到一个栈中,然后如果相同的话 栈就弹出,这样最后栈里剩下的元素都是相邻不相同的元素了。

delete

代码

def removeDuplicates(s):
    """
    :type s: str
    :rtype: str
    """
    stack=[]
    for i in s:
        if stack and i==stack[-1]:#栈不为空  当和栈顶元素相等时候,pop栈顶元素
            stack.pop()
        else:#否则元素入栈
            stack.append(i)
    return "".join(stack)

复杂度分析

时间复杂度:O(n),其中 n是字符串的长度。我们只需要遍历该字符串一次。

空间复杂度:O(n) 或 O(1),取决于使用的语言提供的字符串类是否提供了类似「入栈」和「出栈」的接口。注意返回值不计入空间复杂度。

5 1021. 删除最外层的括号

题目描述

有效括号字符串为空 “”、”(“ + A + “)” 或 A + B ,其中 A 和 B 都是有效的括号字符串,+ 代表字符串的连接。

例如,””,”()”,”(())()” 和 “(()(()))” 都是有效的括号字符串。
如果有效字符串 s 非空,且不存在将其拆分为 s = A + B 的方法,我们称其为原语(primitive),其中 A 和 B 都是非空有效括号字符串。

给出一个非空有效字符串 s,考虑将其进行原语化分解,使得:s = P_1 + P_2 + … + P_k,其中 P_i 是有效括号字符串原语。

对 s 进行原语化分解,删除分解中每个原语字符串的最外层括号,返回 s 。

示例 1:

输入:s = “(()())(())”
输出:”()()()”
解释:
输入字符串为 “(()())(())”,原语化分解得到 “(()())” + “(())”,
删除每个部分中的最外层括号后得到 “()()” + “()” = “()()()”。

解题思路

思路:遍历字符串,遇到左括号就入栈,遇到右括号就出栈,每次栈空的时候,都说明找到了一个原语,记录下每个原语的起始位置和结束位置,取原字符串在原语的起始位置+1到原语的结束位置的子串便得到原语删除了最外层括号的字符串,拼接,即可解出答案。sssss

这道题本身并不麻烦,主要是理解这句原语的含义:

如果有效字符串 s 非空,且不存在将其拆分为 s = A + B 的方法,我们称其为原语

其实就是每获取一组左右括号相等的搭配后,将左右括号各删除一个,并保存即可。
由于这道题目提供的用例都是满足括号匹配关系的内容,使得这道题的难度就更低了。

我们创建一个字符串用于接收每次获取的原语进行拼接
然后创建一个栈,开始循环s,进行栈的入栈操作,每次入栈的是s的下标
左括号直接入栈,右括号时弹出栈顶,并判断栈是否为空,为空则代表找到一对匹配内容
此时获取栈顶index以及当前循环的下标i,ret += s[left+1:i](删除最外层的左右括号)即可。

代码

class Solution(object):
    def removeOuterParentheses(self, s):
        """
        :type s: str
        :rtype: str
        """
        ret=""
        stack=[]#记录索引
        for i in range(len(s)):
            if s[i]=='(':
                stack.append(i)
            else:
                left=stack.pop()
                if not stack:
                    ret+=s[left+1:i]
        return ret
文章目录
  1. 1 剑指 Offer 09. 用两个栈实现队列
    1. 题目描述
    2. 解题思路
    3. 代码
    4. 复杂度分析
  2. 2 leetcode225. 用队列实现栈
    1. 题目描述
    2. 代码
    3. 复杂度分析
  3. 3 leetcode 20. 有效的括号
    1. 题目描述
    2. 解题思路
    3. 代码
    4. 复杂度分析
  4. 4 leetcode1047. 删除字符串中的所有相邻重复项
    1. 题目描述
    2. 解题思路
    3. 代码
    4. 复杂度分析
  5. 5 1021. 删除最外层的括号
    1. 题目描述
    2. 解题思路
    3. 代码