代码随想录算法训练营第十一天 | 栈与队列理论基础、232. 用栈实现队列、225. 用队列实现栈、20. 有效的括号、1047. 删除字符串中的所有相邻重复项

91 阅读4分钟

栈与队列理论基础

See geeks-for-geeks for more information

  • LIFO
  • 在Python中栈可以用listdeque实现

Implementation using list:

  • Python’s built-in data structure list can be used as a stack. Instead of push(), append()is used to add elements to the top of the stack while pop()removes the element in LIFO order.
  • Unfortunately, the list has a few shortcomings. The biggest issue is that it can run into speed issues as it grows. The items in the list are stored next to each other in memory, if the stack grows bigger than the block of memory that currently holds it, then Python needs to do some memory allocations. This can lead to some append() calls taking much longer than other ones.

Implementation using collections.deque:

  • Python stack can be implemented using the deque class from the collections module. Deque is preferred over the list in the cases where we need quicker append and pop operations from both the ends of the container, as deque provides an O(1) time complexity for append and pop operations as compared to list which provides O(n) time complexity.

队列

  • FIFO
  • 在Python中队列可以用listdeque实现

Implementation using list

  • List is a Python’s built-in data structure that can be used as a queue. Instead of enqueue() and dequeue(), append() andpop() function is used.
  • However, lists are quite slow for this purpose because inserting or deleting an element at the beginning requires shifting all of the other elements by one, requiring O(n) time.

Implementation using collections.deque

  • Queue in Python can be implemented using deque class from the collections module. Deque is preferred over list in the cases where we need quicker append and pop operations from both the ends of container, as deque provides an O(1) time complexity for append and pop operations as compared to list which provides O(n) time complexity. Instead of enqueue and deque, append() and popleft() functions are used.

232. 用栈实现队列

代码随想录视频讲解

代码随想录文章讲解

简单的实现

  • 始终使用s1来放数据,使用s2来调整输出的顺序
class MyQueue:
​
    def __init__(self):
        self.s1 = []
        self.s2 = []
        self.size = 0
        
    # Time complexity: O(1)
    def push(self, x: int) -> None:
        self.s1.append(x)
        self.size += 1
        
    # Time complexity: O(N)
    def pop(self) -> int:
        while self.s1:
            self.s2.append(self.s1.pop())
        res = self.s2.pop()
        self.size -= 1
        while self.s2:
            self.s1.append(self.s2.pop())
        return res
    
    # Time complexity: O(N)
    def peek(self) -> int:
        while self.s1:
            self.s2.append(self.s1.pop())
        res = self.s2[-1]
        while self.s2:
            self.s1.append(self.s2.pop())
        return res
      
    # Time complexity: O(1)
    def empty(self) -> bool:
        return self.size == 0

改进

  • push进s1,记录下s1中最前面的数
  • pop时,如果s2是空的,就将s1的数pop出并append到s2.如果s2不是空的,那么直接s2.pop
  • peek时,如果s2不是空的,就返回s2的栈顶,不然返回s1的栈底
class MyQueue:
    def __init__(self):
        self.s1 = []
        self.s2 = []
        self.size = 0
        self.front = None
    
    # Time complexity: O(1)
    def push(self, x: int) -> None:
        if self.s1 == []:
            self.front = x
        self.s1.append(x)
        self.size += 1
    
    # Time complexity: Amortized O(1)
    def pop(self) -> int:
        if self.s2 == []:
            while self.s1:
                self.s2.append(self.s1.pop())
        res = self.s2.pop()
        self.size -= 1
        return res
    
    # Time complexity: O(1)
    def peek(self) -> int:
        if self.s2:
            res = self.s2[-1]
        else:
            res = self.front
        return res
      
    # Time complexity: O(1)
    def empty(self) -> bool:
        return self.size == 0

只使用两个栈(复用方法)

class MyQueue:
    def __init__(self):
        self.s1 = []
        self.s2 = []
        
    # Time complexity: O(1)
    def push(self, x: int) -> None:
        self.s1.append(x)
    
    # Time complexity: Amortized O(1)
    def pop(self) -> int:
        if self.s2 == []:
            while self.s1:
                self.s2.append(self.s1.pop())
        res = self.s2.pop()
        return res
    
    # Time complexity: Amortized O(1)
    def peek(self) -> int:
        res = self.pop()
        self.s2.append(res)
        return res
    
    # Time complexity: O(1)
    def empty(self) -> bool:
        return not (self.s1 or self.s2)

225. 用队列实现栈

代码随想录视频讲解

代码随想录文章讲解

使用两个队列

  • pop:q1将除了最后一个数都弹出并append到q2,剩下的最后一个数就是答案,pop出最后一个数,交换q1和q2
from collections import deque
class MyStack:
    def __init__(self):
        self.q1 = deque()
        self.q2 = deque()
    # Time complexity: O(1)
    def push(self, x: int) -> None:
        self.q1.append(x)
    # Time complexity: O(N)
    def pop(self) -> int:
        for i in range(len(self.q1)-1):
            self.q2.append(self.q1.popleft())
        res = self.q1.popleft()
        self.q1, self.q2 = self.q2, self.q1
        return res
    # Time complexity: O(1)
    def top(self) -> int:
        return self.q1[-1]
    # Time complexity: O(1)
    def empty(self) -> bool:
        return not self.q1

使用一个队列

  • pop:将除了最后一个数都弹出并append自己的队尾,剩下的最后一个数就是答案,pop出最后一个数
from collections import deque
class MyStack:
    def __init__(self):
        self.q1 = deque()
​
    def push(self, x: int) -> None:
        self.q1.append(x)
​
    def pop(self) -> int:
        for i in range(len(self.q1)-1):
            self.q1.append(self.q1.popleft())
        return self.q1.popleft()
​
    def top(self) -> int:
        return self.q1[-1]
​
    def empty(self) -> bool:
        return not self.q1

20. 有效的括号

代码随想录视频讲解

代码随想录文章讲解

使用栈+字典匹配

  • 遇见左括号push进栈,遇到右括号,依次出栈比较
# Time complexity: O(N)
# Space complexity: O(1) 字典大小是个固定的数
class Solution:
    def isValid(self, s: str) -> bool:
        stack = []
        parentheses_dict = {
            '(': ')',
            '[': ']',
            '{': '}'
        }
        for c in s:
            if c in parentheses_dict:
                stack.append(c)
            else:
                if len(stack) != 0:
                    pop = stack.pop()
                    if parentheses_dict[pop] != c:
                        return False
                else:
                    return False
​
        return len(stack) == 0

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

代码随想录视频讲解

代码随想录文章讲解

使用栈

  • 如果栈不为空,和栈中的前一个字符进行比较,如果一样pop,不一样进栈
  • 如果栈为空,直接进栈
# Time complexity : O(N)
# Space complexity : O(N−D) where D is a total length for all duplicates.
class Solution:
    def removeDuplicates(self, s: str) -> str:
        if len(s) == 0 or len(s) == 1:
            return s
​
        stack = []
        stack.append(s[0])
        prev = s[0]
        for i in range(1, len(s)):
            if prev == s[i]:
                stack.pop()
                if stack:
                    prev = stack[-1]
                else:
                    prev = None
            else:
                prev = s[i]
                stack.append(prev)
​
        return ''.join(stack)
      
# A nicer way to write the code
class Solution:
    def removeDuplicates(self, S: str) -> str:
        output = []
        for ch in S:
            if output and ch == output[-1]: 
                output.pop()
            else: 
                output.append(ch)
        return ''.join(output)

双指针

# Time complexity : O(N)
# Space complexity : O(N)
class Solution:
    def removeDuplicates(self, s: str) -> str:
        if len(s) == 0 or len(s) == 1:
            return s
​
        s = list(s)
        # slow points to the last element in the result string
        slow = fast = 0
        while fast < len(s):
            # add qualified character to the result string
            s[slow] = s[fast]
​
            if slow > 0 and s[slow] == s[slow-1]:
                # need to reduce the result string length
                slow -= 1
            else:
                slow += 1
            fast += 1
​
        return ''.join(s[:slow])