JS:单调栈——更大元素,每日温度,柱状图矩阵,移掉数字,去重复字母

开头

下面三个都要掌握的技巧性数据结构
小顶堆 找出第k小的数 (找一天去背一下,因为js没有,也怕被问到手写)
单调队列 处理 滑动窗口最大值(困难)

单调栈:单调栈用途不太广泛,只处理一种典型的问题,叫做 Next Greater Element。
在这里插入图片描述

496. 下一个更大元素 I (简单)

这里多了一步是,在nums2中找到目标值nums1。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
在这里插入图片描述

var nextGreaterElement = function(nums1, nums2) {
    let stack = [], nge = new Array(nums2.length)
    // nums[i] 身后的 next great number
    for(let i=nums2.length-1; i>=0; i--) {
        // 倒着往栈里放
        while(stack.length && stack[stack.length-1] <= nums2[i]){
            // 矮个起开
            stack.pop()
        }
        nge[i] = stack.length==0 ? -1 : stack[stack.length-1]
        // 最后才把自己加进去
        stack.push(nums2[i])
    }
    // 索引在nums2的位置
    ans = nums1.map(val => {
        return nge[nums2.indexOf(val)]
    })
    return ans
};

739. 每日温度 (中等)

来一个简单的变形

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指在第 i 天之后,才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。

这个问题本质上也是找 Next Greater Number,只不过现在不是问你 Next Greater Number 是多少,而是问你当前距离 Next Greater Number 的距离而已。

stack放元素索引,而不是元素
而res不是存放更大数的索引,而是与更大数的索引距离

var dailyTemperatures = function(temperatures) {
    let nums = temperatures
    let stack = [], res = new Array(nums.length)
    for (let i=nums.length-1; i>=0; i--) {
        while(stack.length && nums[stack[stack.length-1]] <= nums[i]) stack.pop()
        res[i] = stack.length==0 ? 0 : (stack[stack.length-1] - i)
        stack.push(i)
    }
    return res;
};

503. 下一个更大元素 II (中等)

给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。
数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。
在这里插入图片描述

处理环形数组
在这里插入图片描述
看了这个图就可以动手写了:

var nextGreaterElements = function(nums) {
    let pre = nums.length - 1
    for(let i=0; i<pre; i++) nums.push(nums[i])
    let stack = [], res = new Array(nums.length)
    for (let i=nums.length-1; i>=0; i--) {
        while(stack && stack[stack.length-1]<=nums[i]) stack.pop()
        res[i] = stack.length==0 ? -1 : stack[stack.length-1]
        stack.push(nums[i])
    }
    // slice不改变原数组,splice改变
    res.splice(pre+1,)
    return res
};

但是,我们可以不用构造新数组,而是利用循环数组的技巧来模拟数组长度翻倍的效果。
在这里插入图片描述

代码随想录

84. 柱状图中最大的矩形 (困难)

与盛雨水、盛最多水的容器差不多

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
在这里插入图片描述

看了前面两题关于双指针,再加上单调栈,我是完成了该困难题目。因为前面给了我启发:

  1. 单独看每一个柱子应该做什么
  2. 以该柱子为高,那它最左和最右的边界是什么——就是第一个比它小的柱子
  3. 第一个比自身小的数的索引,不就是每日温度,只不过每日温度求比自身大,而且是索引距离。

你不会单调栈,直接循环(并不能通过leetcode,超时了)。

但发现42.接雨水,如果使用单调栈是从横向出发
那这一题是结合了单调栈 与 以列为单位

var largestRectangleArea = function(heights) {
    // 找到第一个比自己低的柱子的索引
    let stack = [], len = heights.length
    let lmin = new Array(len), rmin = new Array(len)
    for(let i=0; i<len; i++) {
        while(stack.length && heights[stack[stack.length-1]] >= heights[i]) stack.pop()
        lmin[i] = stack.length==0 ? -1 : stack[stack.length-1]
        stack.push(i)
    }
    stack = []
    for(let i=len-1; i>=0; i--) {
        while(stack.length && heights[stack[stack.length-1]] >= heights[i]) stack.pop()
        rmin[i] = stack.length==0 ? len : stack[stack.length-1]
        stack.push(i)
    }
    // console.log(lmin,rmin)
    let max = 0
    for(let i=0; i<len; i++){
        max = Math.max(max, heights[i] * (rmin[i] - lmin[i] - 1))
    }
    return max;
};

402. 移掉 K 位数字 (困难)

给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。

在这里插入图片描述

这道题不是说把较大的数删除就可以了,因为要考虑他的位置,位置比数值重要。
实例1:把低位的3代替高位的4
实例2:低位的0代替高位的1
可得出结果:遇到前一个比自己值要大的,把他替换掉。(前一个更大的元素),这题特别一点,看到比自己小的留着,而不是把他扔掉。比自己大的,不是作为答案,而是扔掉。

最后还要删的话,因为是递增序列,肯定从最后删除
如果删除次数够了,后面数字就补上

一步步来,难度不大:

var removeKdigits = function(num, k) {
    let stack = [], i = 0
    for(; i<num.length && k; i++) {
        while(stack.length && stack[stack.length-1] > num[i] && k) {
            stack.pop()
            k--
        }
        if(k==0) break
        stack.push(num[i])
    }
    while(k && stack.length) {
        stack.pop()
        k--
    }
    while(i<num.length) {
        stack.push(num[i])
        i++
    } 
    //"0200"
    while(stack[0] == '0') stack.shift()
    return stack.length==0 ? "0" : stack.join("")
};

算法小抄:一道数组去重的算法题

316. 去除重复字母

给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

与1081. 不同字符的最小子序列 题目完全一样:

返回 s 字典序最小的子序列,该子序列包含 s 的所有不同字符,且只包含一次。

在这里插入图片描述
要去重
不能打乱s中字符出现的相对顺序
字典序最小
在这里插入图片描述

在这里插入图片描述

注意:dic和count 的操作都是对应的。

var removeDuplicateLetters = function(s) {
    let dic = {} // 记录是否重复
    let count = {} // 记录个数
    let stack = [] // 答案栈
    for(c of s) {
        count[c] = (count[c] || 0) + 1
    }
    // console.log(count)
    for(c of s) {
        count[c]--
        if(dic[c]) continue //已经存在
        while(stack.length && stack[stack.length-1] > c && count[stack[stack.length-1]]) {
            let x = stack.pop()
            dic[x] = false // 栈保证只会出现一次, 删除了就不存在
        }
        stack.push(c)
        dic[c] = true
        // console.log(stack)
    }
    return stack.join("")
};

展望

下一步就看一下 二叉堆区间问题。这两个解决完就正式进入三大算法(回溯,贪心,动态规划

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值