1. 首页

前端小二教你学会栈

理解栈 Stack

前端Js中文网里的店小二儿对栈的理解很深刻,我们来听听他是怎样理解栈的。

店小二儿十分勤奋,前台后厨的活儿他都干,每天都要跑前跑后端顾客吃完的盘子。栈就像叠在一起的盘子,客人美滋滋的吃完饭,店小二去收拾桌子捡起盘子时,都是从下往上一个一个的放盘子。而他在后厨橱柜上取盘子给厨师时,是从上往下一个一个依次的取。

这也就是所谓的 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 真题

点击上方链接即可跳转读题,这是一道难度为困难的题,不过大家不要被它所迷惑,其实它不是很难。

解决这道题,最直观的办法就是暴力求解。我们可以先来分析一波:

读题的第一遍,实际上就是要求在宽度为 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)); } } }

方法二:确定一根柱子后,分别向前后两个方向遍历。

Js中文网 – 前端进阶资源教程 www.javascriptC.com,typescript 中文文档
一个帮助开发者成长的社区,你想要的,在这里都能找到


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 的左边界!

Js中文网 – 前端进阶资源教程 www.javascriptC.com,typescript 中文文档
一个帮助开发者成长的社区,你想要的,在这里都能找到

又是以空间换时间的套路!

如何确定右边界?

只需遍历一次柱体数组,将大于等于当前柱体的柱子们推入栈中,而当遍历到的柱子高度小于栈顶的柱子高度时,这时我们找到了右边界,可以将栈顶的柱子弹出栈
来计算矩形面积了!

处理特殊边界情况?

引入前后边界,在柱体数组前后各放入一根高度为 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)
  • 码农进阶题库,每天一道面试题 or Js小知识 https://www.javascriptc.com/interview-tips/

希望本文可以让你对栈的理解更深一层,如果你还没看过瘾,可以移步我的主页有其他算法系列的文章。

作者:童欧巴
链接:https://segmentfault.com/a/1190000023927302

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:前端小二教你学会栈

链接:https://www.javascriptc.com/4247.html

« 大佬教你VS Code Disco
LeetCode 026. 删除排序数组中的重复项»
Flutter 中文教程资源

相关推荐

QR code