食堂店小二儿教你学会栈
理解栈 Stack
前端食堂里的店小二儿对栈的理解很深刻,我们来听听他是怎样理解栈的。
店小二儿十分勤奋,前台后厨的活儿他都干,每天都要跑前跑后端顾客吃完的盘子。栈就像叠在一起的盘子,客人美滋滋的吃完饭,店小二去收拾桌子捡起盘子时,都是从下往上一个一个的放盘子。而他在后厨橱柜上取盘子给厨师时,是从上往下一个一个依次的取。
这也就是所谓的 LIFO (Last In, First Out, 后进先出)。
在 JavaScript 中,我们可以通过数组来简单模拟下栈:
function Stack() {
var items = [];
// 添加元素到栈顶
this.push = function(element) {
items.push(element);
}
// 移除栈顶的元素,同时返回它们
this.pop = function() {
return items.pop();
}
// 返回栈顶的元素,不对栈做任何修改
this.peek = function() {
return items[items.length - 1];
}
// 判断栈是否为空,为空则返回true,否则返回false
this.isEmpty = function() {
return items.length === 0;
}
// 返回栈里的元素个数
this.size = function() {
return items.length;
}
// 清空栈
this.clear = function() {
items = [];
}
}
栈的特点
- 栈是一种“操作受限”的线性表,只能从一端插入或删除数据。
- 入栈和出栈的时间复杂度、空间复杂度都是 O(1)。
栈的应用
栈的应用有很多,比如我们熟知的函数调用栈、浏览器的前进后退还有汉诺塔小游戏等。
LeetCode 真题
84. 柱状图中最大的矩形
https://leetcode-cn.com/problems/largest-rectangle-in-histogram/
这是一道难度为困难的题,不过大家不要被它所迷惑,其实它不是很难。
解决这道题,最直观的办法就是暴力求解。我们可以先来分析一波:
读题的第一遍,实际上就是要求在宽度为 1 的 n 个柱子能勾勒出来的矩形的最大面积。
这不就是个幼儿园的数学问题吗?
面积 = 底 * 高
撸它!
暴力法
方法一:双重循环遍历出所有的可能性,在遍历的过程中我们还可以求出最小的高度。
const largestRectangleArea = function(heights) {
let maxArea = 0;
let len = heights.length;
for (let i = 0; i < len; i++) {
let minHeight = heights[i];
for (let j = i; j < len; j++) {
minHeight = Math.min(minHeight, heights[j]);
maxArea = Math.max(maxArea, minHeight * (j - i + 1));
}
}
}
方法二:确定一根柱子后,分别向前后两个方向遍历。
const largestRectangleArea = function(heights) {
let maxArea = 0;
let len = heights.length;
for (let i = 0; i < len; i++) {
let left = i;
let right = i;
while (left >= 0 && heights[left] >= heights[i]) {
left--;
}
while (right < len && heights[right] >= heights[i]) {
right++;
}
maxArea = Math.max(maxArea, (right - left - 1) * heights[i]);
}
return maxArea;
}
但是这两种方法的时间复杂度都是 O(n^2),空间复杂度是 O(1)。时间复杂度太高了,我们需要想办法进行优化。
使用单调递增栈
我们来思考一个问题,我们究竟想要求的最大面积是什么?
不妨拿起笔画画图,我这里帮你画好了,观察上图,便可以得出:
其实就是以 i 为中心,分别向左、向右找到第一个小于 heighs[i] 的两个边界,也就是以当前这根 i 柱子所能勾勒出的最大面积。
那么,我们为什么要借助单调递增栈这种数据结构呢?
单调递增,也就是我们可以通过 O(1) 的时间复杂度确定柱体 i 的左边界!
又是以空间换时间的套路!
如何确定右边界?
只需遍历一次柱体数组,将大于等于当前柱体的柱子们推入栈中,而当遍历到的柱子高度小于栈顶的柱子高度时,这时我们找到了右边界,可以将栈顶的柱子弹出栈 来计算矩形面积了!
处理特殊边界情况?
引入前后边界,在柱体数组前后各放入一根高度为 0 的柱子。这样便无需考虑栈空以及栈中还有剩余柱子的情况啦!
ok,上代码!
const largestRectangleArea = function(heights) {
let maxArea = 0;
let stack = [];
heights.push(0);
heights.unshift(0);
// heights = [0, ...heights, 0]; 你也可以这样写
for (let i = 0; i < heights.length; i++) {
while (stack.length > 0 && heights[stack[stack.length - 1]] > heights[i]) {
maxArea = Math.max(maxArea, heights[stack.pop()] * (i - stack[stack.length - 1] - 1));
}
stack.push(i);
}
return maxArea;
}
- 时间复杂度:O(N)
- 空间复杂度:O(N)
希望本文可以让你对栈的理解更深一层,如果你还没看过瘾,可以移步上方专辑中看我的其他算法系列文章。
- 区块链入门教程
- python爬虫beautifulsoup4系列2
- python爬虫beautifulsoup4系列3
- 多元回归模型
- C++ STL之min_element()与max_element()(取容器中的最大最小值)
- RESTful API 设计最佳实践
- python爬虫beautifulsoup4系列4-子节点
- 元胞自动机实现多数分类算法
- 51Nod 1289 大鱼吃小鱼(模拟,经典好题)
- 用Metaclass实现一个精简的ORM框架
- HDU 2504 又见GCD(最大公约数与最小公倍数变形题)
- Selenium2+python自动化63-二次封装(click/send_kesy)
- Selenium2+python自动化65-js定位几种方法总结
- HDU 2502 月之数(二进制,规律)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- troubleshoot之:用control+break解决线程死锁问题
- Docker 三剑客之docker-compose
- 腾讯云 Severless-Express 项目开发和灰度发布最佳实践
- 在Docker中使用Redis
- 基于实际业务场景下的Flume部署
- troubleshoot之:使用JFR解决内存泄露
- 一个ABAP和JavaScript这两种编程语言的横向比较
- WebRTC & Android 开发学习环境搭建~
- word模板和XML数据源是如何合并生成最后的word文档的详细过程
- Angular路由跳转时,如何传递信息
- Angular里的购物车页面实现
- CentOS7部署WeADMIN监控主机交换机和URL(无坑版)
- JsonPath实践(一)
- 开源测试服务
- Charles报错Failed to install helper解决方案