理论基础
学过js数据结构,这一部分关于c++的直接略过
232.用栈实现队列
题目链接
说明:
你只能使用标准的栈操作 – 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。
讲解
因为栈是先进后出,队列先进先出,就是用两个栈完成一个元素的反转:判断出栈为控,入栈不为空;然后将所有的元素放到出栈里,再取出来;注意需要把所有元素都放到出栈,否则若有新增元素,顺序就会乱。
代码
225. 用队列实现栈
题目链接
使用队列实现栈的下列操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空
注意:
你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)
讲解
一个队列模拟栈(视频):
将队列里除了最后一个元素(size-1个)都弹出,重新放入队列里,就能弹出最后一个元素了。
获取栈顶元素就是队列入栈的最后一个元素(que.back
)js需要看有没有对应的函数实现
两个队列模拟栈
代码
20. 有效的括号
题目链接
讲解
由于栈结构的特殊性,非常适合做对称匹配类的题目。
题意其实就像我们在写代码的过程中,要求括号的顺序是一样的,有左括号,相应的位置必须要有右括号。
实际上只有三种情况:
- 左边括号多了
- 括号类型不匹配
- 右边括号多了
思路就是遍历字符串时遇到左括号,拿一个栈储存对应类型的右括号;直到遍历到右括号,开始拿字符串中的右括号与栈顶元素进行匹配(这里就是遍历左括号储存右括号的好处,不用写额外的逻辑匹配,直接消消乐就行),相同就删除这个元素,去遍历下一个括号;若出现栈为空||不匹配
,返回false;栈为空说明右括号多了情况3,不匹配情况2;情况1就是字符串遍历结束栈里还有元素。
剪枝:字符串为奇数直接返回false
代码
1047. 删除字符串中的所有相邻重复项
题目链接
讲解
可以定义一个字符串来模拟栈,这样最后结果就不用转换了,只要注意字符串的头部和尾部,需要对应正确的字符串顺序。
栈为空或遍历元素与栈顶不相等时放入栈里;
遍历一个,放入栈,第二个的时候与栈顶元素匹配,不匹配就放入继续重复;如果匹配就消除,然后往下一边遍历,一边匹配栈顶元素,到最后取出正确顺序的字符串(字符串模拟可以直接得到)
递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
相信大家应该遇到过一种错误就是栈溢出,系统输出的异常是Segmentation fault(当然不是所有的Segmentation fault 都是栈溢出导致的) ,如果你使用了递归,就要想一想是不是无限递归了,那么系统调用栈就会溢出。
代码
150. 逆波兰表达式求值
题目链接
讲解
中序表达式就是正常的算式;
后序(后缀)表达式是左右中;是计算机运算时使用的方式;不需要给加减运算加括号,只要按顺序遍历就能得到正确结果;
用栈来计算:遇到数字存到栈里;遇到操作符就取出栈头两个数字进行运算(二叉树),然后把结果放到栈里。
其实也是消除:两个元素遇到操作符就消除了。
题目中说的逆波兰表达式没有异常的表达,所以不用考虑异常
代码
day12休息
day13
239. 滑动窗口最大值
题目链接
讲解
大顶堆无法保证弹出的值一定是对的,因为顺序会变会变成从大到小,在往后移动时就无法确定谁是应该弹出去的元素;
但单调队列(单调递增或单调递减)可以自己定义怎么弹出元素;
这道题目需要实现的是这三个函数:弹出之前的元素,压入下一个元素,每次移动判断哪个是最大值;
pop和push都需要判断队列非空
思路:
没必要维护三个元素;只需要把最大值放在队列出口;
push元素的时候,如果比队列入口元素大,就可以把之前的元素都卷走不用维护,比入口元素小的就成为新的入口元素(后续当最大值被弹出时也可能成为新的最大值,所以要留下,再新加入元素时如果比出口小但是比队列里其他元素大,就把其他元素卷走,排在最大值后面),出口元素就是最大值;
pop的时候如果要pop的元素与最大值(队列出口)相等,就需要把队列出口pop出去(1.弹走了所以不是最大值了,队列出口不能是它了2.因为之前不是最大值的元素push新元素时已经被卷走了没有实际执行pop操作)。
js的不同之处:
dq的出口(front)和入口(back)都可以实现出元素和入元素
题解中单调队列里的pop和push接口,仅适用于本题哈。单调队列不是一成不变的,而是不同场景不同写法,总之要保证队列里单调递减或递增的原则,所以叫做单调队列。 不要以为本题中的单调队列实现就是固定的写法
代码
347.前 K 个高频元素
题目链接
讲解
快排算法时间复杂度是O(nlogn)?
大顶堆是根节点是最大的元素;小顶堆是根节点是最小的元素
用堆遍历map(key元素,value出现频率)里的所有元素
只维护k次的元素。
堆是一种二叉树结构;
用大顶堆时,push元素会把堆顶,即二叉树根节点即最大的元素弹出去(最大的:k次的值),所以剩下的是前k个低频元素。
所以用小顶堆。
时间复杂度,大/小顶堆遍历一边是n,在堆中加入一个元素是logk(维护k个元素,只对k个元素进行排序)。所以是O(nlogk)优于nlogn
需要实现compare函数。
代码