✅✅代码随想录算法训练营Day12 || 347.前 K 个高频元素,剑指 Offer II 038. 每日温度,155. 最小栈

137 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情🚀🚀

347. 前 K 个高频元素 - 力扣(LeetCode)

image.png

哈希map

var topKFrequent = function(nums, k) {
    // 遍历数组,将所有结果存储到 map 中
    let map = new Map()
    nums.forEach(n => {
        map.set(n, map.has(n) ? map.get(n)+1 : 1)
    })
    // 把数据存入一个对象数组
    let array = []    
    for (let [key, value] of map) {
        array.push({
            key,
            value
        })
    }
    //对对象数组根据 value 从大到小排序
    array.sort((a, b) => {
        return b.value - a.value
    })

    // 截取前k个,返回其key值组成的数组
    return array.slice(0,k).map(item => {
        return item.key
    })
};

剑指 Offer II 038. 每日温度

image.png

思路分析

最简单的就是暴力解法了:

直接两层遍历,第一层定位一个温度,第二层定位离这个温度最近的一次升温是哪天,然后求出两个温度对应索引的差值即可。

问题

在这个暴力遍历的过程中,我们其实做了很多“多余”的事情。

拿第三个索引位上这个 75 来说,我们在定位比 75 高的第一个温度的过程中,就路过了 71、69、72 这三个温度,其中,72 正是 71 对应的目标温度,可我们却像没看见它一样、啥也没干。只有等最外层遍历走到 71 时,我们才又重复了一遍刚刚走过的路、确认了 71 和 72 之间的关系——像这种不必要的重复,我们要想办法把它干掉。

栈结构可以帮我们避免重复操作

所以这个题目,我们可以:尝试去维持一个递减栈

代码如下:

var dailyTemperatures = function(temperatures) {
    let stack = []
    let len = temperatures.length;
    let res = new Array(len).fill(0);
    for(let i = 0;i < len; i++){
        while(stack.length && temperatures[i] > temperatures[stack[stack.length-1]]){
            const top = stack.pop()
            let diff = i - top;
            res[top] = diff 
        }
            stack.push(i)
    }
    return res
};

155. 最小栈 - 力扣(LeetCode)

image.png

O(n)复杂度

getMin 要做的事情,是从一个栈里找出其中最小的数字。我们仍然是抛砖引玉,先说一个大部分人都能想到的思路:

初始化一个最小值变量,它的初始值可以设一个非常大的数(比如 Infinity),然后开始遍历整个栈。在遍历的过程中,如果遇到了更小的值,就把最小值变量更新为这个更小的值。这样遍历结束后,我们就能拿到栈中的最小值了。
这个过程中,我们对栈进行了一次遍历,时间复杂度无疑是 O(n)

按照这个思路,整个栈的设计我们可以这样写:

const MinStack = function() {
  this.stack = []
};

/** 
 * @param {number} x
 * @return {void}
 */
// 栈的入栈操作,其实就是数组的 push 方法
MinStack.prototype.push = function(x) {
  this.stack.push(x)
};

/**
 * @return {void}
 */
// 栈的入栈操作,其实就是数组的 pop 方法
MinStack.prototype.pop = function() {
  this.stack.pop()
};

/**
 * @return {number}
 */
// 取栈顶元素,咱们教过的哈,这里我本能地给它一个边界条件判断(其实不给也能通过,但是多做不错哈)
MinStack.prototype.top = function() {
  if(!this.stack || !this.stack.length) {
      return 
  }
  return this.stack[this.stack.length - 1]
};

/**
 * @return {number}
 */
// 按照一次遍历的思路取最小值
MinStack.prototype.getMin = function() {
    let minValue = Infinity  
    const  { stack } = this
    for(let i=0; i<stack.length;i++) {
        if(stack[i] < minValue) {
            minValue = stack[i]
        }
    }
    return minValue
};

O(1)复杂度

在这道题里,如果继续沿着栈的思路往下走,我们可以考虑再搞个栈(stack2)出来作为辅助,让这个栈去容纳当前的最小值。

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

/** 
 * @param {number} x
 * @return {void}
 */
MinStack.prototype.push = function(x) {
    this.stack.push(x);
    // 若入栈的值小于当前最小值,则推入辅助栈栈顶
    if(this.stack2.length == 0 || this.stack2[this.stack2.length-1] >= x){
        this.stack2.push(x);
    }
};

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

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

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