【Python 数据结构 7.栈和队列】

目录

[一、用 栈 实现 队列](#一、用 栈 实现 队列)

1.概念

2.入队操作

3.出队操作

4.获取队首元素

5.队列判空

[二、面试题 03.04. 化栈为队](#二、面试题 03.04. 化栈为队)

思路与算法

[三、用 队列 实现 栈](#三、用 队列 实现 栈)

1.概念

2.入栈操作

3.出栈操作

4.获取栈顶元素

5.栈判空

[四、225. 用队列实现栈](#四、225. 用队列实现栈)

思路与算法


于是我停下脚步,痛定思痛

------ 25.3.4

一、用 栈 实现 队列

1.概念

所谓用栈实现队列,就是要求实现一个队列,只不过这个队列的底层容器,不是顺序表,也不是链表,而是栈。要满足这个需求,必须用到两个栈,一个叫 入队栈 s1,一个叫 出队栈 s2

2.入队操作

将元素压住 入队栈 s1中

3.出队操作

首先,判断 出队栈 s2 是否为空,如果为空,则将 入队栈 s1 中的元素全部弹出并依次压入 出队栈 s2

然后,弹出 出队栈 s2 的栈顶元素并返回

4.获取队首元素

首先,判断 出队栈 s2 是否为空,如果为空,则将 入队栈 s1 中的元素全部弹出并依次压入 出队栈 s2 (和出队操作一致)

然后,获取 出队栈 s2 栈顶元素并返回

5.队列判空

如果 入队栈 s1 和 出队栈 s2 为空,则返回true,否则返回false


二、面试题 03.04. 化栈为队

实现一个MyQueue类,该类用两个栈来实现一个队列。

示例:

复制代码
MyQueue queue = new MyQueue();

queue.push(1);
queue.push(2);
queue.peek();  // 返回 1
queue.pop();   // 返回 1
queue.empty(); // 返回 false

说明:

  • 你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, sizeis empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。

思路与算法

双栈结构设计Stack1作为输入栈 ,负责接收新元素(push操作)Stack2作为输出栈 ,负责弹出队列头部元素(pop/peek操作)通过栈的反转特性 实现队列的先进先出(FIFO)逻辑

关键操作逻辑

入队(push)​ :直接压入Stack1,时间复杂度为 O(1)

出队(pop)​ :若Stack2为空,将Stack1所有元素依次弹出并压入Stack2(实现顺序反转),弹出Stack2栈顶元素(即队列头部元素),均摊时间复杂度 O(1)

查看队首(peek)​ :逻辑与pop类似,但仅查看不弹出元素。

判空(empty)​:同时检查两个栈是否为空

python 复制代码
class Stack:
    def __init__(self):
        self.data = []

    # 入栈操作 直接相当于列表的append操作
    def push(self, val):
        self.data.append(val)

    # 出栈操作 直接相当于列表的pop操作
    def pop(self):
        if self.empty():
            return "Stach is empty"
        return self.data.pop()

    # 获取栈顶元素 直接相当于列表的索引[-1]操作
    def top(self):
        if self.empty():
            return "Stach is empty"
        return self.data[-1]

    def size(self):
        return len(self.data)

    def empty(self):
        return len(self.data) == 0

class MyQueue:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.Stack1 = Stack()
        self.Stack2 = Stack()


    def push(self, x: int) -> None:
        """
        Push element x to the back of queue.
        """
        self.Stack1.push(x)
        

    def pop(self) -> int:
        """
        Removes the element from in front of queue and returns that element.
        """
        if self.Stack2.empty():
            while not self.Stack1.empty():
                self.Stack2.push(self.Stack1.pop())
        return self.Stack2.pop()
        

    def peek(self) -> int:
        """
        Get the front element.
        """
        if self.Stack2.empty():
            while not self.Stack1.empty():
                self.Stack2.push(self.Stack1.pop())
        return self.Stack2.top()
        

    def empty(self) -> bool:
        """
        Returns whether the queue is empty.
        """
        return self.Stack1.empty() and self.Stack2.empty()        


# 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()

三、用 队列 实现 栈

1.概念

所谓用队列实现栈,就是要求实现一个栈,只不过这个栈的底层容器,不是顺序表,也不是链表,而是队列。要满足这个需求,必须用到两个队列,一个叫 主队列 q1,一个叫 辅助队列 q2

**注意:**辅助队列每次操作完毕一定会变成空的。


2.入栈操作

将元素在 主队列 q1 中入队

3.出栈操作

第1步: 如果 主队列 q1 没有元素,那么这个操作非法

第2步: 判断 主队列 q1 的元素个数是否大于 1,如果大于 1,则将 主队列 q1 的元素全部按照顺序在 辅助队列 q2 入队

第3步: 获取 主队列 q1 的队首元素,保存起来用于最后返回;并且将 主队列 q1 执行出队操作;

第4步: 将 辅助队列 q2 的元素全部按照顺序在 主队列 q1 入队

第5步: 返回第3步中 保存起来的 主队列 q1 的队首元素

4.获取栈顶元素

第1步: 如果 主队列 q1 没有元素,那么这个操作非法。

第2步: 判断 主队列 q1 的元素个数是否大于 1,如果大于 1,则将 主队列 q1 的元素全部按照顺序在 辅助队列 q2 入队

第3步: 获取 主队列 q1 的队首元素,保存起来用于最后返回;并且将 主队列 q1 执行出队操作,并且将这个元素 在辅助队列 q2 入队

第4步: 将 辅助队列 q2 的元素全部按照顺序在 主队列 q1 入队

第5步: 返回第3步中 保存起来的 主队列 q1 的队首元素

5.栈判空

由于 辅助队列 q2 每次操作完毕必然会变成空的,所以只需要判断 主队列 q1 是否会空,如果 主队列 q1 为空,则返回 true;否则,返回 false。


四、225. 用队列实现栈

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

实现 MyStack 类:

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

注意:

  • 你只能使用队列的标准操作 ------ 也就是 push to backpeek/pop from frontsizeis 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

提示:

  • 1 <= x <= 9
  • 最多调用100pushpoptopempty
  • 每次调用 poptop 都保证栈不为空

思路与算法

双队列策略

主队列(Queue1)​:存储栈的当前元素。

辅助队列(Queue2)​:在出栈操作时临时存储元素。

核心操作分析:​

入栈(push)​ :直接将元素压入 主队列 Queue1(时间复杂度O(1))​

出栈(pop)​ :将 主队列 Queue1的前n-1个元素转移到 辅助队列 Queue2。弹出主队列 Queue1的最后一个元素作为栈顶。将 辅助队列 Queue2的元素转移回 主队列 Queue1(时间复杂度O(n))。

查看栈顶(top)​:类似出栈操作,但需将最后一个元素重新压回队列(时间复杂度O(n))。​

设计特点:​

模拟LIFO :通过队列的FIFO特性逆向实现栈的先进后出。

均摊时间复杂度:单次出栈/查看栈顶操作最坏O(n),但均摊后仍可视为O(1)

python 复制代码
class Queue:
    def __init__(self):
        self.data = []
        # head 和 tail 分别表示队头和队尾的索引,是一个左闭右开的区间
        self.head = 0
        self.tail = 0

    def push(self, val):
        self.data.append(val)
        self.tail += 1

    def pop(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        val = self.data[self.head]
        self.head += 1
        return val

    def  front(self):
        if self.is_empty():
            raise Exception("Queue is empty")
        return self.data[self.head]

    def size(self):
        return self.tail - self.head

    def is_empty(self):
        return self.size() == 0

def test():
    q = Queue()
    q.push(1)
    q.push(2)
    q.push(3)
    q.push(4)

    while not q.is_empty():
        print(f'Front element: {q.front()}')
        print(f'Popped element: {q.pop()}')
        print(f'Size: {q.size()}')

if __name__ == '__main__':
    test()

class MyStack:

    def __init__(self):
        self.Queue1 = Queue()
        self.Queue2 = Queue()

    def push(self, x: int) -> None:
        self.Queue1.push(x)

    def pop(self) -> int:
        while self.Queue1.size() > 1:
            self.Queue2.push(self.Queue1.pop())
        ret = self.Queue1.pop()
        while self.Queue2.size() > 0:
            self.Queue1.push(self.Queue2.pop())
        return ret


    def top(self) -> int:
        while self.Queue1.size() > 1:
            self.Queue2.push(self.Queue1.pop())
        ret = self.Queue1.pop()
        self.Queue2.push(ret)
        while self.Queue2.size() > 0:
            self.Queue1.push(self.Queue2.pop())
        return ret
        

    def empty(self) -> bool:
        return self.Queue1.is_empty() and self.Queue2.is_empty()
        


# 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()
相关推荐
开心比对错重要5 小时前
leetcode69.x 的平方根
数据结构·算法·leetcode
Doopny@6 小时前
数字组合(信息学奥赛一本通-1291)
数据结构·算法·动态规划
君莫愁。6 小时前
【Unity】搭建基于字典(Dictionary)和泛型列表(List)的音频系统
数据结构·unity·c#·游戏引擎·音频
原来是猿7 小时前
蓝桥备赛(13)- 链表和 list(上)
开发语言·数据结构·c++·算法·链表·list
总斯霖8 小时前
题解:士兵排列
数据结构·c++·算法
平谷一勺9 小时前
go切片定义和初始化
数据结构·golang·数组·切片
誓约酱9 小时前
(每日一题) 力扣 283 移动零
linux·c语言·数据结构·c++·算法·leetcode
橘子真甜~10 小时前
34.二叉树进阶3(平衡二叉搜索树 - AVL树及其旋转操作图解)
数据结构·c++·二叉搜索树·avl树·平衡搜索树
仟濹10 小时前
【算法 C/C++】一维前缀和
数据结构·c++·算法
tt55555555555511 小时前
每日一题——搜索二维矩阵
数据结构·算法·leetcode