算法篇一(单调栈)

220 阅读2分钟

前言

本篇作为小编总结算法的开端,后续会做成系列。算法体现一个人的思维能力,对程序员至关重要。

评估标准

时间复杂度

定义

定性来描述该算法的运行时间,用O表示。布吉岛大家还是否记得高中的让我们头疼的各种函数:二次、三次、幂函数、指数函数、log函数、各种组合函数...... 没错! 就是一直陪伴我们学习和工作的她们。不好依稀,俺还记得,但是呢如果大家不清楚原理了,那就生猛一些吧,把下面的图印在脑海~

二维图

ps: 看过之后是不是头大了,这是啥!哈哈,跟大家开个玩笑,如果你真的记不住那我们只需要记住简单的几个就可以啦,如下:

  • O(1):代表只执行一次,没有任何循环
let i = 0;
i += 1 //只执行了一次
  • O(n):循环,执行了n次
for(let i = 0; i < n; i += 1) {
//DO SOMETHIGN
}
  • O(1) + O(n) = O(n);
let i = 0;
i += 1;
for(let i = 0; i < n; i += 1) {
//DO SOMETHING
}
  • O(n) * O(n) = O(n^2)
for(let i = 0; i < n; i += 1) {
  for(let j = 0; j < n; i += 1) {
    // DO SOMETHING
  }
}
  • O(logN)
let i = 1;
while(i < n) {
  i *= 2
  //DO SOMETHING
}

空间复杂度

定义

算法在运行过程中临时占用存储空间大小的量度

示例

  • O(1)
let i = 0;
i += 1;
  • O(n)
const list = [];
for(let i = 0; i < n; i +=) {
list.push(i);
}
  • O(n^2) 其实就是我们学的线性代数中的矩阵,而在前端是用数组来表示,存储了2^n个变量
const matrix = [];
for(let i = 0; i < n; i += 1) {
matrix.push([])
  for(let j = 0; j < n; j += 1) {
  matrix[i].push(j);
  }
}

数据结构

栈(单调栈)

基本的栈的概念在这里就不细聊了,单调栈的含义就是栈内的元素是单调的(就是成正比或反比),下面的题目是递减栈,我们依次将元素压入栈,如果当前元素小于等于栈顶元素则入栈,如果大于栈顶元素则将栈顶不断出栈,直到当前元素小于或者等于栈顶元素为止,再将当前元素入栈。就比如下图的4,想入栈的话则需要2,3出栈之后才能入栈,因为4大于他俩。

示意图

应用场景

下一个更大元素

来源:力扣(LeetCode)

给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。

示例 1:

输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
    对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
    对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
    对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

思路:其实就是将num2中的数据形成一个哈希映射,最后遍历num1

var nextGreaterElement = function(nums1, nums2) {
  // 先创建一个栈
  const stack = [nums2[0]];
  const map = new Map();
  nums2.slice(1).forEach((value) => {
    if(value > stack[stack.length - 1]) {
      while(value > stack[stack.length - 1]){
        map.set(stack.pop(), value);
      }
      stack.push(value)
    }else {
      stack.push(value)
    }
  })
  return nums1.map((v, i) => map.get(v) ? map.get(v) : -1)
  // nums2[1] > nums[0]   //直到满足此条件, nums[0]代表栈顶,nums[1]代表新元素
  // stack.pop();  //将栈循环清空,并依次map.set(栈中元素, 新元素)
  // map.set(nums[0], nums[1])
  // nums2[1] <= nums[0];
  // stack.push(nums2[1]);  //循环判断此逻辑,满足条件便push
}

ps: 既然能存到栈顶,说明比下面的数字小,那就是不是其对应的映射数字。

接雨水

javascript题解

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    let left = 0;
  let right = height.length - 1;
  let res = 0;
  let leftMax = 0;
  let rightMax = 0;
  while (left < right) {
    if (height[left] < height[right]) {
      leftMax = Math.max(height[left], leftMax);
      res += leftMax - height[left];
      left++;
    } else {
      rightMax = Math.max(height[right], rightMax);
      res += rightMax - height[right];
      right--;
    }
  }
  return res;
};

这部分原理比较复杂,建议看这里 : leetcode-cn.com/problems/tr…

当然最简单的方案是双指针法,也是我们必须要掌握的,算法篇下一节会细讲(并会以接雨水为例)。