20. 有效的括号 https://leetcode-cn.com/problems/valid-parentheses/

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

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

解:

如果来一个左括号,还不能判断是否合法,push进栈;来一个右括号,看一下栈的peek是否匹配,如果匹配就pop出来,否则不合法;如果合法,最后栈应该是空的。

class Solution:
def isValid(self, s: str) -> bool:
if s is None:
return True
str_map = {")": "(",
"]": "[",
"}": "{"
} # 这里用hashmap存对应关系,右括号放前面
stack = []
for char in s:
if char in str_map:
if not stack or stack[-1] != str_map[char]:
return False
stack.pop()
else:
stack.append(char)
if stack:
return False
return True

  

232. 用栈实现队列 https://leetcode-cn.com/problems/implement-queue-using-stacks/

使用栈实现队列的下列操作:

push(x) -- 将一个元素放入队列的尾部。
pop() -- 从队列首部移除元素。
peek() -- 返回队列首部的元素。
empty() -- 返回队列是否为空。

说明:

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

解:

队列先进先出,栈后进先出。用两个栈s1和s2实现队列。

入队O(n),push的元素要后出,那就要把push的元素放到栈底。所以每次入队一个元素,要先把s1中的元素移到s2中,把入队元素push到s1中,再把s2中的元素移回s1。

出队O(1),直接从s1中pop出去即可,因为后入队的元素在s1的底部,先入队的元素在s1顶部。

取队首元素,s1的顶部元素,即最先入队的元素。

判空,元素全部存在s1中,判空s1即可。

class MyQueue:

    def __init__(self):
"""
Initialize your data structure here.
"""
self.s1 = []
self.s2 = [] def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
if self.empty():
self.s1.append(x)
else:
while self.s1:
self.s2.append(self.s1.pop()) self.s1.append(x) while self.s2:
self.s1.append(self.s2.pop()) def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if not self.empty():
return self.s1.pop() def peek(self) -> int:
"""
Get the front element.
"""
if not self.empty():
return self.s1[-1] def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
return False if self.s1 else True

 

第二种方法,入队O(1),出队摊还复杂度O(1)。

入队,直接push进s1的栈顶。

出队,把s1中全部元素弹出,压入s2,这样s1的栈底元素变成了s2的栈顶元素,直接pop出去即可。一旦s2变空了,只需要把s1中的元素再一次转移到s2即可。在最坏情况下,s2为空,需要从s1中弹出n个元素,然后再把这n个元素压入s2,在这里n代表队列的大小。这个过程产生了2n步操作,时间复杂度为 O(n)。但当s2非空时,就只有 O(1) 的时间复杂度。

取队首元素,定义front变量来存队首元素,如果s2为空,front就是队首元素(即s1的栈底元素),否则s2的栈顶元素为队首元素。

判空,s1和s2都有队列的元素,都为空则队列空。

class MyQueue:

    def __init__(self):
"""
Initialize your data structure here.
"""
self.s1 = []
self.s2 = []
self.front = None def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
if not self.s1:
self.front = x
self.s1.append(x) def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if not self.empty():
if not self.s2: # 如果s2为空,把s1中元素全部转移过来,再pop; s2不空,直接pop
while self.s1:
self.s2.append(self.s1.pop())
return self.s2.pop() def peek(self) -> int:
"""
Get the front element.
"""
if not self.empty():
return self.s2[-1] if self.s2 else self.front def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
if not self.s1 and not self.s2:
return True
return False

  

225. 用队列实现栈 https://leetcode-cn.com/problems/implement-stack-using-queues/

使用队列实现栈的下列操作:

push(x) -- 元素 x 入栈
pop() -- 移除栈顶元素
top() -- 获取栈顶元素
empty() -- 返回栈是否为空

注意:

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

解:

两个队列 q1 和 q2 实现栈。

压栈 O(1),进栈元素就直接入队q1

出栈 O(n),要pop的元素是最后入队的元素,

个队列用作临时存储 q1 中出队的元素。q2中最后入队的元素将作为新的栈顶元素。接着将q1中最后剩下的元素出队。通过把q1和q2互相交换的方式来避免把q2中的元素往q1中拷贝。

取栈顶元素:栈的top始终为q1的队尾,即最后一个压栈的元素。

判空:栈中元素都在q1中

class MyStack:

    def __init__(self):
"""
Initialize your data structure here.
"""
self.q1 = []
self.q2 = []
self.top_elem = None def push(self, x: int) -> None:
"""
Push element x onto stack.
"""
self.q1.append(x)
self.top_elem = x def pop(self) -> int:
"""
Removes the element on top of the stack and returns that element.
"""
while len(self.q1) > 1:
self.top_elem = self.q1.pop(0)
self.q2.append(self.top_elem) # q1中最后一个元素之前的元素全部出队,入队q2,栈的新top就是q2的队尾 tmp = self.q1.pop(0) # q1原队尾出队,相当于栈pop出去栈顶元素
self.q1, self.q2 = self.q2, self.q1
return tmp def top(self) -> int:
"""
Get the top element.
"""
return self.top_elem def empty(self) -> bool:
"""
Returns whether the stack is empty.
"""
return False if self.q1 else True

  

第二种方法,两个队列,压入O(n),弹出O(1)

压栈:让每一个新元素从q2入队,同时把这个元素作为栈顶元素保存。当q1非空(也就是栈非空),让q1中所有的元素全部出队,再将出队的元素从 q2 入队。通过这样的方式,新元素(栈中的栈顶元素)将会在q2的前端。通过将q1,q2互相交换的方式来避免把q2中的元素往q1中拷贝。

出栈:栈不空(q1不空),q1队首元素出队即可。更新一下栈顶元素为新的q1队首元素

判空:q1不空即可

取栈顶元素:如果q1为空,那栈为空,栈顶元素为null;如果q1不空,栈顶元素就是q1的队首元素。(因为q1中元素是后进栈的放队首)

class MyStack:

    def __init__(self):
"""
Initialize your data structure here.
"""
self.q1 = []
self.q2 = []
self.top_elem = None def push(self, x: int) -> None:
"""
Push element x onto stack.
"""
self.q2.append(x)
self.top_elem = x
while self.q1:
self.q2.append(self.q1.pop(0))
self.q1, self.q2 = self.q2, self.q1 def pop(self) -> int:
"""
Removes the element on top of the stack and returns that element.
"""
if not self.empty():
tmp = self.q1.pop(0)
if self.q1:
self.top_elem = self.q1[0]
else:
self.top_elem = None
return tmp def top(self) -> int:
"""
Get the top element.
"""
return self.top_elem def empty(self) -> bool:
"""
Returns whether the stack is empty.
"""
return False if self.q1 else True

  

 703. 数据流中的第K大元素 https://leetcode-cn.com/problems/kth-largest-element-in-a-stream/

设计一个找到数据流中第K大元素的类(class)。注意是排序后的第K大元素,不是第K个不同的元素。

你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含数据流中的初始元素。每次调用 KthLargest.add,返回当前数据流中第K大的元素。

说明: 
你可以假设 nums 的长度≥ k-1 且k ≥ 1。

解:

始终维护第一个数组保存前K大的数据,每次来一个新数据就重新排序K,仍然保留前K大元素。O(NKlogK)

class KthLargest:

    def __init__(self, k: int, nums: List[int]):
self.nums = nums
self.k = k
self.nums.sort(reverse=True)
self.nums = self.nums[: self.k] def add(self, val: int) -> int:
self.nums.append(val)
self.nums.sort(reverse=True)
self.nums = self.nums[: self.k]
return self.nums[-1]

  

优先队列,维护一个小顶堆,堆的size始终为K,每次来一个新的数据,如果大于堆顶就pop出堆顶,把新数据push进堆再维护一下即可。O(NlogK)

import heapq

class KthLargest:

    def __init__(self, k: int, nums: List[int]):
self.nums = nums
heapq.heapify(self.nums)
self.k = k
while len(self.nums) > k:
heapq.heappop(self.nums) def add(self, val: int) -> int:
if len(self.nums) < self.k:
heapq.heappush(self.nums, val)
elif val > self.nums[0]:
heapq.heapreplace(self.nums, val)
return self.nums[0]

  

239. 滑动窗口最大值  https://leetcode-cn.com/problems/sliding-window-maximum/

给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

提示:

你可以假设 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小。

进阶:

你能在线性时间复杂度内解决此题吗?

解:

暴力解,直接遍历每个窗口,找最大值。 O(N * k)

class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if nums is None or k <= 0:
return []
n = len(nums)
return [max(nums[i: i+k]) for i in range(n - k + 1)]

  

优先队列,维护一个大小为窗口长度k的大顶堆,每次滑动都用新元素替换掉刚离开的元素,然后维护大顶堆。堆顶元素是当前窗口的最大值。O(N * logk)

直接用双端队列维护一个单调队列,头尾两端都是O(1)的查询、操作和删除,双端队列最常用的地方就是实现一个长度动态变化的窗口或者连续区间。

用双端队列window保存当前窗口中数的下标(并不需要存全部的数的下标),window新的头总是当前窗口中最大的那个数。处理前 k 个元素,初始化window。遍历整个数组。在每一步清理双向队列:

  只保留当前滑动窗口中有的元素的索引(因为滑动出去一个,要看一下window中的索引是不是都在i-k到i);

  移除比当前元素小的所有元素,它们不可能是最大的;

  将当前元素添加到双向队列中;

  将 deque[0] 添加到输出中。

比如 [1, 3, -1, -3, 5, 3, 6, 7],k=3

1进队,window: [0];3进队,比1大,1出队,window: [1];-1进队,window: [1, 2]。 res: [3]

-3进队,window: [1, 2, 3]。res:[3]

5进队,window: [4]。res:[5]

...

class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if not nums:
return []
window = [] # 下标
res = []
for i, x in enumerate(nums):
if i >= k and window[0] <= i-k: # 从k下标开始有元素滑出去了,而且每次可能要出去的那个元素下标为window[0],如果其下标在i-k+1之前,说明不在窗口内
window.pop(0)
while window and nums[window[-1]] <= x: # 如果新进入窗口的元素x比当前窗口内的某个元素大,这个元素就不起作用pop出去;从窗口右向左检查
window.pop()
window.append(i) # 记录新元素的下标进窗口
if i >= k-1: # 从第一个窗口开始记录最大值
res.append(nums[window[0]])
return res

 

动态规划。将n个数据分成大小为k的块,定义left[i]为i所处的块的开始位置到i位置的最大值,right[j]为j所处的块的结束位置到j的最大值。那么滑动窗口就两种情况,要么没有跨越两个块,要么跨越了两个块。窗口从i到j,最大值为max( left[j], right[i] )

class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
n = len(nums)
if n * k == 0:
return []
if k == 1:
return nums left, right = [0]*n, [0]*n left[0] = nums[0]
right[n - 1] = nums[n - 1] # left和right赋值
for i in range(1, n):
if i % k == 0: # 从左向右,块的起始位置,left[i] = nums[i]
left[i] = nums[i]
else:
left[i] = max(left[i - 1], nums[i]) # left[i] = max(nums[block_start:i+1]) j = n - i - 1 # 从右向左,块的结束位置,right[j] = nums[j]
if (j + 1) % k == 0:
right[j] = nums[j]
else:
right[j] = max(right[j + 1], nums[j]) # right[j] = max(right[j:block_end+1]) output = []
for i in range(n - k + 1): # 最后一个窗口起点位置是n-k
output.append(max(left[i + k - 1], right[i])) return output

  

Leetcode-栈&队列的更多相关文章

  1. Leetcode栈&队列

    Leetcode栈&队列 232.用栈实现队列 题干: 思路: 栈是FILO,队列是FIFO,所以如果要用栈实现队列,目的就是要栈实现一个FIFO的特性. 具体实现方法可以理解为,准备两个栈, ...

  2. 栈&队列&并查集&哈希表(julyedu网课整理)

    date: 2018-11-25 08:31:30 updated: 2018-11-25 08:31:30 栈&队列&并查集&哈希表(julyedu网课整理) 栈和队列 1. ...

  3. java 集合 Connection 栈 队列 及一些常用

    集合家族图 ---|Collection: 单列集合 ---|List: 有存储顺序 , 可重复 ---|ArrayList: 数组实现 , 查找快 , 增删慢 ---|LinkedList: 链表实 ...

  4. Java 容器之 Connection栈队列及一些常用

    集合家族图 ---|Collection: 单列集合 ---|List: 有存储顺序 , 可重复 ---|ArrayList: 数组实现 , 查找快 , 增删慢 ---|LinkedList: 链表实 ...

  5. java面向对象的栈 队列 优先级队列的比较

    栈 队列 有序队列数据结构的生命周期比那些数据库类型的结构(比如链表,树)要短得多.在程序操作执行期间他们才被创建,通常用他们去执行某项特殊的任务:当完成任务之后,他们就会被销毁.这三个数据结构还有一 ...

  6. C++实现一个简单的双栈队列

    双栈队列的原理是用两个栈结构模拟一个队列, 一个栈A模拟队尾, 入队的元素全部压入此栈, 另一个栈B模拟队首, 出队时将栈A的元素弹入栈B, 将栈B的栈顶元素弹出 此结构类似汉诺塔, 非常经典, 这里 ...

  7. leetcode 栈和队列类型题

    1,Valid Parentheses bool isVaild1(string& s) { // 直接列举,不易扩展 stack<char> stk; ; i < s.le ...

  8. leetcode 栈 括号匹配

    https://oj.leetcode.com/problems/valid-parentheses/ 遇到左括号入栈,遇到右括号出栈找匹配,为空或不匹配为空, public class Soluti ...

  9. 【图解数据结构】 栈&队列

    [TOC] 勤于总结,持续输出! 1.栈 1.1栈的定义 栈(stack)是限定在表尾进行插入和删除的操作的线性表. 我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不包 ...

  10. 数据结构 栈&队列

    2-4 依次在初始为空的队列中插入元素a,b,c,d以后,紧接着做了两次删除操作,此时的队头元素是( ) 删除,移动头指针: 增加,移动尾指针: 删除a,b ,队头c 2-3 在一个链队列中,fron ...

随机推荐

  1. 初识ABP vNext(7):vue身份认证管理&租户管理

    Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 按钮级权限 身份认证管理 R/U权限 权限刷新 租户管理 租户切换 效果 最后 前言 上一篇介绍了vue+ABP国际化 ...

  2. .NET Core3.1 Dotnetty实战第三章

    一.概要 本章主要内容就是讲解如何在dotnetty的框架中进行网络通讯以及编解码对象.数据包分包拆包的相关知识点. 后续会专门开一篇避坑的文章,主要会描述在使用dotnetty的框架时会遇到的哪些问 ...

  3. C++标准模板库(STL)常用介绍

    1. 输入输出 C++既可以用C的scanf和printf,也可以用新增的的cin与cout,后者速度慢 1.1 C程序中输入输出 int a; scanf("%d",&a ...

  4. android开发之使edittext输入弹出数字软键盘。亲测可用。手机号登陆注册常用。

    <EditText android:id="@+id/edit_digit_input" android:layout_width="wrap_content&qu ...

  5. 利用分块传输吊打所有WAF--学习笔记

    在看了bypassword的<在HTTP协议层面绕过WAF>之后,想起了之前做过的一些研究,所以写个简单的短文来补充一下文章里“分块传输”部分没提到的两个技巧. 技巧1 使用注释扰乱分块数 ...

  6. python笔记-dumps()与loads()的使用

    json.dumps是将一个Python数据类型列表进行json格式的编码解析, 示例如下: >>> import json #导入python 中的json模块 >>& ...

  7. RPC之总体架构

    要完成一个高可用.高性能的RPC框架,需要对其架构的设计进行梳理,这里参考xxl-rpc框架,对整个项目进行梳理. 以上就是项目的整个构架,分为四个部分: 第一个是服务发布与引入,基于JDK动态代理以 ...

  8. 12_进程,线程,协程,IO多路复用的区别

    1.进程 1.进程可以使用计算机多核 2.进程是资源分配的单位 3.进程的创建要比线程消耗更多的资源效率很低 4.进程空间独立,数据安全性跟好操作有专门的进程间通信方式 5.一个进程可以包含多个线程, ...

  9. [转载] 微软发布 SURFACE DUO ANDROID SDK 和模拟器

    模拟器截图 微软今天发布了双屏折叠设备 Surface Duo Android 开发工具(SDK 和模拟器),Windows 10X 开发工具和模拟器之后 2 月 11 日发布,并宣布了新的针对双屏体 ...

  10. pytest(3):pytest运行参数介绍

    前言 pytest 带有很多参数,可以使用 pytest --help  来查看帮助文档,下面介绍几种常用的参数: 无参数 读取路径下所有符合规则的文件,类,方法,函数全部执行.使用方法如下:  py ...