【前端算法系列】栈

★20.有效的括号

1)新建一个栈
2)扫描字符串,
如果是左括号就入栈
如果是右括号,就取栈顶,用栈顶去跟右括号的看是否匹配,匹配就出栈
3)最后栈空了表示闭合

/*  时间复杂度:只有一个for循环,push/pop操作都是O(1),就可以认为时间复杂度为O(n),n为s的长度
	空间复杂度:定义一个数组,可能把s的长度都push进去,所以空间复杂度为O(n)
*/
var isVaild=function(s){
	if(s.length%2 === 1) return false // 判断长度是否为偶数,奇数表示不用继续下去了,已经为false
	for(let i=0;i<s.length;i++){
		const c=s[i]
		if(c==='('||c==='{'||c==='['){
			stack.push(c)  // ["(", "{"]
		}else{ // 右括号中的
			const t = stack[stack.length-1] // 取出栈顶
			if((t==='('&&c===')')||(t==='{'||c==='}')||(t==='['&&c===']')){
				stack.pop()  // 匹配就出栈
			}else{
				return false	// 否则结束
			}
		}
	}
	return stack.length === 0 // 最后判断长度是否为0,为0表示闭合
}
  • 另一种写法:借助栈实现,把括号自右向左看做栈结构,成对的括号需要左括号和右括号一一匹配
var isValid = function(s) {
    // 字符串无条件判断为true
    if(!s) return true
    let toRight={
    "(":")",
    "[":"]",
    "{":"}"
    }
    const stack=[]
    const len = s.length
    for(let i=0;i<len;i++){
        const ch = s[i]
        // 如果是左边的,是就把对应的右边括号push到stack里
        if(ch==='('||ch==='{'||ch==='[') stack.push(toRight[ch])
        else{
            // 右边的,是否于栈顶相同,是就true,否就false
            if(!stack.length || stack.pop()!==ch){
            return false
            }
        }
    }
    // 所有括号匹配成功,最后栈应为空
    return !stack.length
}
console.log(isValid("()]{}"))

括号匹配问题

示例
举例:输入 “(111(222)())”
输出:true
举例:输入 “((333)”
输出:false
思路同上

const isValid = str => {
    // 1.创建栈对象,用来存储左括号
    let stack = []
    // 2.从左往右遍历字符串
    for(let i=0;i<str.length;i++){
        let currChart = str.charAt(i)+''
        console.log(currChart)
        console.log(stack)
        if(currChart === '('){
            stack.push(currChart)
        }else if(currChart === ')'){
            let pop = stack.pop()
            if(pop==null){
                return false
            }
        }
    }
    if(stack.length === 0){
        return true
    }

    return false
}

console.log(isValid("(111(222)())"))

★用两个栈实现队列

使用栈实现队列的下列操作:
push(x) – 将一个元素放入队列的尾部
pop() – 从队列首部移除元素
peek() – 返回队列首部的元素
empty() – 返回队列是否为空

思路;设置两个栈,栈1做push操作,栈2用pop/peek等操作
只要碰到pop/peek,把所有输入栈的内容放到输出栈去
在这里插入图片描述

class MyQueue{
    constructor(){
        this.stack1 = []  
        this.stack2 = []
    }
    push(item){
        this.stack1.push(item)
    }
    pop(){
        this.swap()
        return this.stack2.pop() || -1
    }
    // peek(){
    //     this.swap()
    //     return this.stack2.peek()
    // }
    // empty(){
    //     this.swap()
    //     return this.stack2.isEmpty()
    // }
    swap(){
    	// 为空时才交换,不为空不交换
       if (!this.stack2.length) {
            while(this.stack1.length) {
                this.stack2.push(this.stack1.pop())
            }
        }
    }
}

const queue = new MyQueue()
queue.push(1)
queue.push(2) 
console.log( queue.peek() ) // 1
console.log( queue.pop() )  // 2
console.log( queue.empty() )  // false
queue.push(3) 
console.log( queue.peek() )
console.log( queue.pop() )  // 2
console.log( queue.pop() )  // 3

栈的压入、弹出序列

在这里插入图片描述

var validateStackSequences = function (pushed, popped) {
    if (pushed.length == 0 && popped.length == 0) {
        return true
    }
    if (pushed.length == 0 || popped.length == 0 || pushed.length != popped.length) {
        return false
    }
    let stack = [] // 辅助栈
    let j = 0 // 出栈序列下标
    /* 
        每push一个进去后,拿栈顶的元素去遍历出栈popped里的元素
        如果找到对应的,就从栈里pop出去,再把出栈下标j后移
    */
    for (let i = 0; i < pushed.length; i++) {
        stack.push(pushed[i])
        while(stack.length !== 0  && j<popped.length && stack[stack.length-1] === popped[j] ){
            j++
            stack.pop()
        }
    }
    return stack.length === 0
};
let pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
console.log(validateStackSequences(pushed, popped))

棒球比赛

你现在是棒球比赛记录员。
给定一个字符串列表,每个字符串可以是以下四种类型之一:
1.整数(一轮的得分):直接表示您在本轮中获得的积分数。
2. “+”(一轮的得分):表示本轮获得的得分是前两轮有效 回合得分的总和。
3. “D”(一轮的得分):表示本轮获得的得分是前一轮有效 回合得分的两倍。
4. “C”(一个操作,这不是一个回合的分数):表示您获得的最后一个有效 回合的分数是无效的,应该被移除。

每一轮的操作都是永久性的,可能会对前一轮和后一轮产生影响。
你需要返回你在所有回合中得分的总和。

var calPoints = (arr) => {
    // 用数组来实现堆栈结构,pop,push
    let result = []
    // 上一轮的数据
    let pre1
    // 上上轮的数据
    let pre2
    // 对数组进行遍历,遍历的目的是处理得分
    arr.forEach(item => {
        switch (item) {
            // 最后一个有效 回合的分数是无效的,应该被移除
            case 'C':
                if (result.length) {
                    result.pop()
                }
                break
            // 本轮获得的得分是前一轮有效 回合得分的两倍
            case 'D':
                pre1 = result.pop()
                result.push(pre1, pre1 * 2)
                break
            // 本轮获得的得分是前两轮有效 回合得分的总和
            case '+':
                pre1 = result.pop()
                pre2 = result.pop()
                result.push(pre2, pre1, pre2 + pre1)
                break
            // 直接表示您在本轮中获得的积分数
            default:
                result.push(item * 1)
        }
    })
    return result.reduce((total, num) => { return total + num })
}
console.log(calPoints(["5","2","C","D","+"]))

柱状图中最大的矩形

方法1:暴力解法,枚举左边和枚举右边,找出它的最小高度,算他们面积
for i=0;i<len-2
for j=i+1;i<len-1
比较i和j找出最小高度,算出面积
每次循环 记录面积

方法2:暴力解法,枚举柱子高度
for i=0;i<len-1
找到左边界left、右边界right 比它小的 —> 比如5的左边界1和右边界2比它小(6比5大不算),所以宽度就是1和2中间的差值
area = height[i] * (right-left)
记录更新面积

方法2:维护一个栈,从下到大排列
依次放入栈中,
比栈顶小的,拖你后腿了就不能入栈,这时候要计算面积了(依次弹出栈里面的元素计算),计算完记录起来
比栈顶大的,就继续放入栈中

在这里插入图片描述

var largestRectangleArea = function(heights) {
    if(!heights.length) return 0

    let stack=[] // 存储的是下标 0,1,2,3
    let area = 0

    heights.unshift(0), heights.push(0) // 两端添加0 巧妙处理下面遍历while的终止条件

    for(let i=0;i<heights.length;i++){
        while(stack.length!==0 &&  heights[stack[stack.length-1]] > heights[i] ){
            let j = stack.pop()
            let w = i-stack[stack.length-1]-1
            area = Math.max(area, w*heights[j])
        }
        stack.push(i)
    }
    return area
}

最大矩形

单调栈:
1)把二维数组转成一维数组
2)再求每个一维数组的最大矩形面级
在这里插入图片描述

const maxArea = (heights) => {
    console.log('heights', heights)
    const stack = [];
    let ans = 0;
    heights.unshift(0);
    heights.push(0);
    for (let i = 0; i < heights.length; ++i) {
    	// 比栈顶小的,此处做面积计算
        while (stack.length && heights[stack[stack.length-1]] > heights[i]) {
        	// 从栈删除
            const currentHeight = stack.pop();
            const right = i - 1, left = stack[stack.length-1] + 1;
            ans = Math.max(ans, (right-left+1)*heights[currentHeight])    
        }
        // 入栈
        stack.push(i);
    }
    return ans;
}

var maximalRectangle = function(matrix) {
    let res = 0;
    if (!matrix.length) return res;
    const dp = [] // 用来做累加
    for (let i = 0; i < matrix[0].length; ++i) {
        dp.push(0);
    }
    for (let i = 0; i < matrix.length; ++i) {
        for (let j = 0; j < matrix[0].length; ++j) {
      		// 如果此处值等于1的就累加1,转成1维数组
            dp[j] = matrix[i][j] === '1' ? dp[j] + 1 : 0;
        }
        console.log('dp', dp) 
        /* dp  [ 1, 0, 1, 0, 0 ]
			dp [ 2, 0, 2, 1, 1 ]
			dp [ 3, 1, 3, 2, 2 ]
			dp [ 4, 0, 0, 3, 0 ]
		*/
        res = Math.max(res, maxArea(dp.slice()));
    }
    return res;
};

★739.每日温度

在这里插入图片描述
在这里插入图片描述

const dailyTemperatures=(T)=>{
    const len = T.length 
    let stack=[] // 存下标
    let res=(new Array(len).fill(0)) // 初始化结果数组,注意数组定长,占位为0
    for(let i=0;i<len;i++){
        // 若栈不为0,且存在打破递减趋势的温度值
        while(stack.length && T[i] > T[stack[stack.length-1]]){
            // 将栈顶温度值对应的索引出栈
            let top = stack.pop()
            // 计算 当前栈顶温度值与第一个高于它的温度值 的索引差值
            res[top] = i - top
        }
        // 注意栈里存的不是温度值,而是索引值,这是为了后面方便计算
        stack.push(i)
    }
    return res
}

console.log(dailyTemperatures([73, 74, 75, 71, 69, 72, 76, 73]))

★155.最小栈

使用辅助栈来存放最小的值,当前值小于栈顶才会被push进去

const MinStack = function() {
    this.stack = [];
    // 定义辅助栈
    this.stack2 = [];
};

MinStack.prototype.push = function(x) {
    this.stack.push(x);
    // 若入栈的值小于当前最小值,则推入辅助栈栈顶,大于的不会被push进去
    if(this.stack2.length == 0 || this.stack2[this.stack2.length-1] >= x){
      console.log(x)
        this.stack2.push(x); // [6, 2]
    }
};

MinStack.prototype.pop = function() {
    // 若出栈的值和当前最小值相等,那么辅助栈也要对栈顶元素进行出栈,确保最小值的有效性
    if(this.stack.pop() == this.stack2[this.stack2.length-1]){
        this.stack2.pop();
    }
};

MinStack.prototype.top = function() {
    return this.stack[this.stack.length-1];
};

MinStack.prototype.getMin = function() {
    // 辅助栈的栈顶,存的就是目标中的最小值
    return this.stack2[this.stack2.length-1];
};

const s = new MinStack()
s.push(6)
s.push(2)
s.push(3)
s.push(4)
s.push(5)
s.push(8)
console.log(s.getMin()) // 2
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值