1.两个栈实现队列
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
我的解法
栈后进先出,队列先进先出,入队操作没啥可说的,直接压入instack就行了。出队操作把instack里面的数据从栈顶一个个弹出,然后outstack一个个接收,这样instack最先压入的数据就跑到了outstack的栈顶,把栈顶数据弹出返回就是一次pop操作了。
同时要注意两个栈都为空的情况,做好判断。
let instack = []
let outstack = []
function push(node)
{
instack.push(node)
}
function pop()
{
if(outstack.length == 0){
while(instack.length !=0){
outstack.push(instack.pop())
}
}
return outstack.pop()
}
一点补充
那么问题来了?如何用队列模仿栈呢?
入栈操作还是用push完成,但是出栈需要再来一个队列。队列1shfit数据存入队列2中,到队列1只剩一个数据时,shift出去,然后队列2的数据要移回队列1
function stackpush(num){
inqueue.push(num)
}
function stackpop(queue){
let res = null
if(queue.length != 0){
while(queue.length !=1){
outqueue.push(queue.shift())
}
res = queue.shift()
while(outqueue.length != 0){
queue.push(outqueue.shift())
}
}
return res
}
let inqueue=[]
let outqueue =[]
stackpush(1)
stackpush(2)
stackpush(3)
stackpush(4)
console.log(inqueue); //[1,2,3,4]
console.log(stackpop(inqueue)); //4
console.log(stackpop(inqueue)); //3
2.滑动窗口最大值
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
我的解法
当然比较蠢了,两层for循环,一次把size个数取出来放入空数组arr中,然后比较大小,最大值保存在res中输出
function maxInWindows(num, size)
{
let res = []
if(size == 0 ||num == null){
return res
}
for(let i=0;i<=num.length-size;i++){
let arr = []
for(let j =i;j<i+size;j++){
arr.push(num[j])
}
arr.sort((a,b)=>{
return a-b
})
res.push(arr[arr.length-1])
}
return res
}
其他分析
双端队列:两头都可以入队和出队,js中就是push和pop,shift和unshift。双端队列保存当前窗口中最大那个数的下标,双端队列新的头总是当前窗口中最大的那个数。
有了这个下标,我们可以知道新的窗口是否还包含原来那个最大的数,如果不再包含,我们就把旧的数从双端队列的头删除。
function maxInWindows(num, size)
{
let res = []
if(size == 0){
return res
}
let queue = []
for(let i=0;i<num.length;i++){
let begin = i-size+1 //窗起始位置
//新输入的num数据比queue标识的进入滑动窗的数据大
//就把小的数弹出不要,保证queue头部标示的数是滑动窗口中最大的
while((queue.length !=0)&&(num[queue[queue.length-1]]<=num[i])){
queue.pop()
}
//判断窗有没有移到最大值位置的后面,也就是说当前最大值已经不在窗内了,需要弹出
if(queue.length !=0&&begin>queue[0]){
queue.shift()
}
queue.push(i)
if(begin>=0){
res.push(num[queue[0]])
}
}
return res
}
https://leetcode-cn.com/problems/sliding-window-maximum/solution/shi-pin-jie-xi-shuang-duan-dui-lie-hua-dong-chuang/
力扣给出的视频详细说明了queue的作用
3.包含min函数的栈
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。
我的解法
对于min函数,一开始的思路是对栈遍历,弄个变量记录当前最小值,遍历结束返回变量即可。但是题目中有个要求是时间复杂度为O(1)。遍历的时间复杂度为O(n)而且一旦最小值被弹出栈,需要再次遍历。
其他分析
通过建立辅助栈将 min() 函数复杂度降为 O(1):
- 数据栈A:栈 A 用于存储所有元素,保证push() 、pop() 函数、 top() 函数的正常逻辑。
- 辅助栈B:每次压入A栈时,把A栈里面最小的值压入B栈当中,则栈A中的最小元素始终对应栈B的栈顶元素,即 min() 函数只需返回栈B的栈顶元素即可。
四个函数:
- push(node):保证B中元素非严格递减,一个元素对应A中的一个元素
- node压入A栈中
- B栈为空,node压入栈;若不为空,node小于B栈顶元素,压入栈
- pop():A栈顶元素和B栈顶元素相同时,也就是说A栈顶刚好是最小值,B要同步弹出栈顶元素
- top():A.pop()
- min():返回B栈顶元素
let A = []
let B = []
function push(node)
{
A.push(node)
//注意这里一定包含等于的情况,如果A栈压入3,2,4,5,2,B栈就是3,2,2
//如果不包含等于,那么B栈就是3,2;这是A栈弹出2,它的最小值还是2,但是B栈以及不包含2了,会出错
if(B.length == 0||node<=B[B.length-1])
B.push(node)
}
function pop()
{
//B栈弹出是有条件的,如果A栈顶刚好是最小值,那么B栈顶肯定也是这个值,如果A弹出,B不弹出,A的最小值就会出错
//B栈也不能随便跟着A栈弹
if(A[A.length-1]==B[B.length-1]) {B.pop()}
A.pop()
}
function top()
{
return A[A.length-1]
}
function min()
{
return B[B.length-1]
}
4.栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
我的解法
遍历压入序列元素,都压入栈中,直到与弹出序列某个元素相同,栈弹出栈顶元素,弹出序列后移移位,继续比较。
但是继续分析的时候,发现比较压入序列元素和弹出序列是否相等决定是否入栈,当需要出栈时,代码不好写(反正我没写出来。)
其他分析
我觉得一个关键就是,弹出序列就是曾经的栈顶元素的集合。应该是栈顶元素和弹出序列比较,来决定压入序列元素是否入栈:不相等就一直进栈,相等时就出栈,弹出序列后移一位。
function IsPopOrder(pushV, popV)
{
if(pushV == null||popV == null) return false
let stack = []
let j = 0
for(let i=0;i<pushV.length;i++){ //其实一直在入栈,然后再判断栈顶元素
stack.push(pushV[i])
//这里一定是while
while(stack.length!=0&&stack[stack.length-1]==popV[j]){
stack.pop()
j++
}
}
//入栈序列遍历完了,如果弹出序列匹配应该stack一边入栈一边出栈,最后stack应该是空的
if(stack.length == 0)
return true
return false
}