js-剑指刷题记录(栈与队列)

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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值