【JavaScript数据结构与算法】一、栈及leetcode实战

2023-11-10

栈是一种遵从后进先出(LIFO)原则的有序集合。新添加或待删除的元素都保存在栈的同一端,称作栈顶,另一端就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。

栈数据结构

我们需要一种数据结构来保存栈里的元素。可以选择数组。数组允许我们在任何位置添加或删除元素。由于栈遵循 LIFO 原则,需要对元素的插入和删除功能进行限制。下面基本使用js实现一些栈的方法:

  • push(element(s)):添加一个(或几个)新元素到栈顶。
  • pop():移除栈顶的元素,同时返回被移除的元素。
  • peek():返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶的元素,仅仅返回它)。
  • isEmpty():如果栈里没有任何元素就返回true,否则返回false。
  • clear():移除栈里的所有元素。
  • size():返回栈里的元素个数。该方法和数组的length属性很类似
class Stack{
    constructor(){
        this.items = []
    }
    push(ele){
        this.items.push(ele)
    }
    pop(){
        return this.items.pop()
    }
    peek(){
        return this.items[this.items.length-1]
    }
    isEmpty(){
        return this.items.length===0
    }
    size(){
        return this.items.length
    }
    clear(){
        this.items=[]
    }
}

到这,我们基本对栈的数据结构及其方法都有了比较清晰的认识,下面直接进行实现联系吧。

leetcode应用

1、 20. 有效的括号 (easy)

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

提示:

1 <= s.length <= 104
s 仅由括号 ‘()[]{}’ 组成

  • 思路:首先如果字符串能组成有效的括号,则长度一定是偶数,我们可以遍历字符串,遇到左括号则暂存,期待后面有右括号可以和它匹配,如果遇到右括号则检查是否能和最晚暂存的做括号匹配。这就和栈这种数据结构先进后出的特性相吻合了。所以我们可以准备一个栈存放括号对,遍历字符串的时候,如果遇到左括号入栈,遇到右括号则判断右括号是否能和栈顶元素匹配,在循环结束的时候还要判断栈是否为空,如果不为空,则不是有效括号匹配的字符串
  • 复杂度分析:
    • 时间复杂度:O(n),其中 nn是字符串 s 的长度。
    • 空间复杂度:O(n + ∣Σ∣),其中 Σ 表示字符集,本题中字符串只包含 6 种括号,∣Σ∣=6。栈中的字符数量为 O(n),而哈希表使用的空间为 O(∣Σ∣),相加即可得到总空间复杂度
var isValid = function(s) {
    const n = s.length;
    if (n % 2 === 1) {//如果字符串能组成有效的括号,则长度一定是偶数
        return false;
    }
    const pairs = new Map([//用栈存储括号对
        [')', '('],
        [']', '['],
        ['}', '{']
    ]);
    const stk = [];
    for (let ch of s){//循环字符串
        if (pairs.has(ch)) {
          	//遇到右括号则判断右括号是否能和栈顶元素匹配
            if (!stk.length || stk[stk.length - 1] !== pairs.get(ch)) {
                return false;
            }
            stk.pop();
        } else {
            stk.push(ch);//如果遇到左括号入栈
        }
    };
    return !stk.length;//循环结束的时候还要判断栈是否为空
};

2、 232. 用栈实现队列 (easy)

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
  • 思路:用栈模拟队列,不涉及到具体算法,主要考察栈和队列的掌握程度。使用先进后出的栈来模拟先进先出的队列的,一个栈来模拟肯定不能满足需要,所以这里使用两个栈:一个输入栈,一个输出栈

    • 在push数据时,数据仅存入输入栈;
    • pop时候,若输出栈为空,则就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。最后如果进栈和出栈都为空的话,说明模拟的队列为空了。
  • 复杂度分析:

    • 时间复杂度:push 和 empty 为 O(1),pop 和peek 为均摊 O(1)。对于每个元素,至多入栈和出栈各两次,故均摊复杂度为O(1)。
    • 空间复杂度:O(n)。其中 n 是操作总数。对于有 n 次push 操作的情况,队列中会有 n 个元素,故空间复杂度为 O(n)。
var MyQueue = function () {
    //准备两个栈
    this.stackIn = [];
    this.stackOut = [];
};

/** 
 * @param {number} x
 * @return {void}
 */
MyQueue.prototype.push = function (x) {
    this.stackIn.push(x);
};

/**
 * @return {number}
 */
MyQueue.prototype.pop = function () {
    // pop的时候判断输出栈是否为空
    const size = this.stackOut.length;
    if (size) return this.stackOut.pop(); //不为空则输出栈出栈
    
    while (this.stackIn.length) { //输出栈为空,则把输入栈所有的元素加入输出栈
        this.stackOut.push(this.stackIn.pop());
    }
    return this.stackOut.pop();
};

/**
 * @return {number}
 */
MyQueue.prototype.peek = function () {
    //查看队头的元素 复用pop方法,由于元素已经从输出栈pop,需要再次push到输出栈
    const x = this.pop(); 
    this.stackOut.push(x);
    return x;
};

/**
 * @return {boolean}
 */
MyQueue.prototype.empty = function () {
    return !this.stackIn.length && !this.stackOut.length
};

3、155. 最小栈 (easy)

设计一个支持 pushpoptop 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。
  • 思路:定义两个栈stack和min_stack,stack正常push,min_stack只会push需要入栈和min_stack栈顶中较小的元素,这样每次删除min_stack均可以保证其栈顶元素为最小值。getMin返回min_stack栈顶元素,top返回stack栈顶元素。
  • 复杂度分析
    • 时间复杂度:对于题目中的所有操作,时间复杂度均为 O(1)。因为栈的插入、删除与读取操作都是 O(1),我们定义的每个操作最多调用栈操作两次。
    • 空间复杂度:O(n),其中 n 为总操作数。最坏情况下,我们会连续插入 n 个元素,此时两个栈占用的空间为 O(n)。
var MinStack = function() {
    this.stack = [];
    this.min_stack = [Infinity];
};

/** 
 * @param {number} val
 * @return {void}
 */
MinStack.prototype.push = function(val) {
    this.stack.push(val);
    // min_stack只会push需要入栈和栈顶中较小的元素
    this.min_stack.push(Math.min(this.min_stack[this.min_stack.length - 1], val));
};

/**
 * @return {void}
 */
MinStack.prototype.pop = function() {
    this.stack.pop();
    this.min_stack.pop();
};

/**
 * @return {number}
 */
MinStack.prototype.top = function() {
    // 返回stack栈顶元素
    return this.stack[this.stack.length - 1];
};

/**
 * @return {number}
 */
MinStack.prototype.getMin = function() {
    // 返回min_stack栈顶元素
    return this.min_stack[this.min_stack.length - 1];
};

4、946. 验证栈序列 (medium)

给定 pushedpopped 两个序列,每个序列中的 值都不重复,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true;否则,返回 false

提示:

  • 1 <= pushed.length <= 1000
  • 0 <= pushed[i] <= 1000
  • pushed 的所有元素 互不相同
  • popped.length == pushed.length
  • poppedpushed 的一个排列
  • 思路:用栈模拟出栈入栈的过程,当poppedindex指向的位置的元素和stack栈顶的元素一致时,出栈且 index++,最后判断stack是否完全遍历popped。
  • 复杂度分析:
    • 时间复杂度O(n),pushed中的元素入栈出栈一次;
    • 空间复杂度O(n)
/**
 * @param {number[]} pushed
 * @param {number[]} popped
 * @return {boolean}
 */
var validateStackSequences = function(pushed, popped) {
    const stack = [];//用栈模拟出栈入栈的过程
    let index = 0;
    const len = pushed.length;
    for (let i = 0; i < len; i++) {
        stack.push(pushed[i]);
      	//当popped中index指向的位置的元素和stack栈顶的元素一致时,出栈 并且 index++
        while (stack.length && index<len && popped[index] === stack[stack.length - 1]) {
            stack.pop();
            index++;
        }
 
    }
    return index === len; // 最后判断 index是否完全遍历popped
};

5、445. 两数相加 II (medium)

给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

提示:

  • 链表的长度范围为 [1, 100]
  • 0 <= node.val <= 9
  • 输入数据保证链表代表的数字无前导 0
  • 思路:将两个链表的节点都推入栈中,然后不断出栈,计算每个位置的值和进位,串连成一个新的链表

  • 复杂度分析

    • 时间复杂度:O(max(m,n)),其中 m 和 n 分别为两个链表的长度。我们需要遍历两个链表的全部位置,而处理每个位置只需要 O(1) 的时间。

    • 空间复杂度:O(m + n),其中 m和 n 分别为两个链表的长度。空间复杂度主要取决于我们把链表内容放入栈中所用的空间

var addTwoNumbers = function(l1, l2) {
    const stack1 = [];
    const stack2 = [];
    while (l1 || l2) {//两链表入栈
        if (l1) {
            stack1.push(l1.val);
            l1 = l1.next;
        }
        if (l2) {
            stack2.push(l2.val);
            l2 = l2.next;
        }
    }
    let carry = 0;
    let ansList = null;
    while (stack1.length || stack2.length || carry !== 0) {//不断出栈
        const s1 = stack1.length ? stack1.pop() : 0;
        const s2 = stack2.length ? stack2.pop() : 0;
        let val = s1 + s2 + carry;
        carry = parseInt(val / 10);//计算进位
        val = val % 10;//计算当前节点的值
        const curNode = new ListNode(val);
        curNode.next = ansList;//向链表前插入新节点
        ansList = curNode;//重新赋值ansList
    }
    return ansList;
};

6、682. 棒球比赛 (easy)

你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。

比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:

  1. 整数 x - 表示本回合新获得分数 x
  2. "+" - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
  3. "D" - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
  4. "C" - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。

请你返回记录中所有得分的总和。

  • 复杂度:时间复杂度O(n),空间复杂度O(n)
/**
 * @param {string[]} ops
 * @return {number}
 */
var calPoints = function (ops) {
    let ans = []
    for (let i = 0; i < ops.length; i++) {
        switch(ops[i]) {
            case 'C':
                ans.pop();
                break;
            case 'D':
                ans.push(+ans[ans.length - 1] * 2);
                break;
            case '+':
                ans.push(+ans[ans.length - 1] + +ans[ans.length - 2]);
                break;
            default:
                ans.push(+ops[i])
        }
    }
    return ans.reduce((a, b) => a + b, 0);
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【JavaScript数据结构与算法】一、栈及leetcode实战 的相关文章

  • D3.js分组条形图

    I am making a bar chart using D3 js like this source statcan gc ca http www statcan gc ca pub 12 593 x 2007001 figures f
  • 目前最好的 Javascript 模板引擎是什么? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 无法实现模块模式

    我正在尝试重现 Douglas Crockford 所著的 Javascript The Good Parts 一书中的一些代码 这个想法是使用闭包进行对象封装并避免Javascript固有的全局变量 var serial maker fu
  • jQuery 模式窗口从我的表单中删除元素

    jQuery 当我用它创建一个包含表单元素的模式窗口时 当我提交表单时 它会取出这些元素 表格示例
  • 定时器内嵌套异步等待 - 不返回所需的值

    我必须使用 Mocha 和 chai 测试来测试端点的响应 下面是相同的代码 async function getData userId let response let interval setInterval async gt resp
  • Ng Bootstrap 日期范围选择器 [markDisabled] 不适用于输入

    我正在尝试禁用某些日期ng 引导范围选择器 https ng bootstrap github io components datepicker overview 目前 我在弹出窗口中有一个范围选择器 并且我正在使用 markDisable
  • document.registerElement - 为什么我们需要指定“prototype”和“extends”?

    考虑我想扩展本地button元素 并创建我自己的super button元素 据我所知 它必须遵循以下模式 var SuperButton document registerElement super button prototype Ob
  • Javascript:打乱数组中的对象组

    我有一个对象数组 我已按键排序 group如下 使得所有具有相同值的对象group在索引中彼此相邻data 例如 var data foo cat group house foo cat group house foo cat group
  • 开始使用 Three.js 中的行进立方体

    我是 Three js 的新手 正在寻找教程来帮助我开始了解如何在 Three js 中使用 Marching Cubes 到目前为止 我在 Three js 中看到的一些使用它的项目对我来说有点复杂 所以一个简单的教程会很好 谢谢 像您一
  • 检测 iPad Safari 用户的最佳方法

    添加用于检测 iPad Safari 用户的代码的最佳方法是什么 我的意思是我们应该使用 1 CSS 通过链接媒体 2 JS 通过navigator对象 我听说使用用户代理字符串并不是检测 iPad 的最佳方法 因为存在不一致的情况 请建议
  • 使用点符号将数字传递到函数中

    如果我有一个对象和函数 var obj 1234 example sample 5678 example sample function example num str if obj num hasOwnProperty str manip
  • 如何在 AWS Amplify 上运行 React/Redux 应用程序的代理

    我最近实施了Proxy 在 Express js 中 对于我的反应应用程序发出请求时隐藏 API URL 当我在本地主机上运行代理和应用程序时 它工作得非常好 现在我已准备好将我的应用程序部署到AWS 放大 我对如何让我的代理在那里运行有点
  • 无法安装js-bson

    我正在使用Windows 7 64位 尝试安装bson作为mongodb的依赖项 我收到此错误 npm WARN package json email protected cdn cgi l email protection No READ
  • 将罗马数字转换为阿拉伯数字--recursiv

    我是 JavaScript 新手 正在网站的帮助下学习https www jshero net koans roman1 html https www jshero net koans roman1 html 本练习是编写一个转换器 将罗马
  • jQuery clone() 复制数据...有时...?

    使用下面的示例 我有一个tr我正在复制 它包含一个 jQueryautocomplete 第一次克隆时 自动完成功能不起作用 因为附加的data items 一片空白 第二次单击 添加 按钮时 自动完成功能将起作用 此后 再次单击 添加 会
  • 如何使用 Javascript 从 Chrome iOS 下载 blob 文件?

    如何使用 Javascript 从 Chrome iOS 下载 blob 文件 我正在从 iOS 下载文件 pdf excel txt png iOS 没有文件系统 这对下载来说是一个问题 我创建了一个代码 根据操作系统和导航器 如果需要
  • 如何在javascript中设置从数据库输入的最大数量?

    我希望根据数据库中的数量设置 输入类型 数字 中输入的最大数量 目前 我正在尝试让它在数据最大的基础上工作 然后再尝试从数据库中获取最大值 但它似乎无法工作 之前已经在这里问过 但我仍然无法理解 在 php javascript 中设置数据
  • Escape String - 在 Javascript 中输出rails字符串[重复]

    这个问题在这里已经有答案了 我正在尝试将字符串值分配给 erb 文件中的 javascript 对象 如下所示 var data name 问题是 如果name is Tom s small ears 的输出data name将会Tom x
  • 有没有用 Javascript 编写的开源 JSDoc 解析器?

    我正在寻找一个可以在我的项目中使用的 JSDoc 解析器 我正在寻找可以传递 JSDoc 注释并接收该注释含义的结构化描述的东西 我见过的大多数工具似乎都能够将 JSDoc 注释转换为 HTML 或其他格式 我正在寻找能够提供可用于输入其他
  • CSS 未使用 req.params 或其他内容加载

    我对节点 表达等非常陌生 我制作了一个博客应用程序 但遇到了问题 我正在使用 mongoose node express 和 ejs 当我打电话时 router get posts function req res Post find fu

随机推荐

  • 使用CDN服务时遇到【HTTP PUT PATCH DELETE等请求方法不支持】【请求未到源站】【CDN直接返回404】【Cloudreve无法删除文件】的问题及解决方案

    异想之旅 本人原创博客完全手敲 绝对非搬运 全网不可能有重复 本人无团队 仅为技术爱好者进行分享 所有内容不牵扯广告 本人所有文章仅在CSDN 掘金和个人博客 一定是异想之旅域名 发布 除此之外全部是盗文 给赶时间的朋友们一句话总结 阿里
  • React 实现井字棋游戏 (tic-tac-toe) 教程 (5) <译自官方文档>

    React 实现井字棋游戏 tic tac toe 教程 1 lt 译自官方文档 gt React 实现井字棋游戏 tic tac toe 教程 2 lt 译自官方文档 gt React 实现井字棋游戏 tic tac toe 教程 3 l
  • 如何解决VS中scanf使用时报错或无法使用的问题

    目录 前言 1 问题 2 问题原因 3 如何解决 4 如何设置 总结 前言 新手上手VS想必都会遇到这个问题 在使用scanf时会出现警告 或者直接报错导致程序终止的问题 今天我就向大家讲解一下如何解决这个问题 1 问题 初识c语言的同学在
  • 解决gateway报错org.springframework.cloud.gateway.support.NotFoundException: Unable to find instance for

    gateway报错 org springframework cloud gateway support NotFoundException Unable to find instance for localhost 配置 浏览器访问 htt
  • Python实现中国移动提出的ABCDNETS和DSSN数联网技术介绍

    Python实现中国移动提出的ABCDNETS和DSSN数联网技术介绍 随着网络技术的发展 数联网技术成为了未来网络的关键技术之一 在这个方向上 中国移动提出了ABCDNETS和DSSN两种不同的数联网技术 分别针对不同应用场景进行优化 本
  • textarea文字垂直居中_word小技巧:制作封面时将文字置于页面中心

    有时侯我们需要将文字放置于页面的中间 水平居中和垂直居中 特别是在做封面的时侯 水平居中比较简单 但是发现如果要将一个或多个文字垂直居中处理确没有直接的办法 诸如PS CDR等专业排版软件要垂直居中非常容易 word里面要垂直居中可以通过下
  • django保存表单数据到数据库中

    这一部分涉及一下几个模块 前端HTML表单 表单验证的Form 数据库结构Model 后端处理的View 文章目录 前端HTML 表单验证Form 数据库结构Model 后端处理的View 更多数据库操作 前端HTML 网页文件 save
  • 【校招VIP】java开源框架之Zookeeper

    考点介绍 ZooKeeper是一个分布式的 开放源码的分布式应用程序协调服务 主要为了解决分布式架构下数据一致性问题 典型的应用场景有分布式配置中心 分布式注册中心 分布式锁 分布式队列 集群选举 分布式屏障 发布 订阅等场景 本期分享的j
  • python自动化访问百度地图

    要在 Python 中自动化访问百度地图 你可以使用第三方库 selenium 来实现 Selenium 是一种自动化测试工具 可以模拟用户在浏览器上执行操作 首先 你需要安装 Selenium 可以通过运行以下命令来安装它 pip ins
  • Go语言面试题--基础语法(16)

    文章目录 1 f1 f2 f3 函数分别返回什么 2 下面代码段输出什么 3 关于channel的特性 下面说法正确的是 1 f1 f2 f3 函数分别返回什么 func main fmt Println f1 fmt Println f2
  • 【HDLBits 刷题 13】Buliding Larger Circuits

    目录 写在前面 Buliding Larger Circuits count1k shiftcount fsm seq fsmshift fsm fancytimer fsm onehot 写在前面 以下的解题方法不一定为最佳解决方案 有更
  • 构造树型结构数据

    构造树型结构数据 param data 数据源 param id id字段 默认 id param parentId 父节点字段 默认 parentId param children 孩子节点字段 默认 children export fu
  • AUC的计算、物理意义,

    文章目录 一 定义 二 性质 三 计算 3 1 方法一 根据定义 3 2 方法二 根据意义 3 3 方法三 方法二优化 3 4 方法四 工业场景 四 物理意义推导 一 定义 ROC曲线与坐标轴围成的面积 ROC曲线由不同阈值下 TPR Y轴
  • Linux内核在I386架构下的内存管理

    转载自 http blog csdn net li shyng article details 5545973 同类型的 http www kerneltravel net journal ii I386是Intel的x86系列CUP中一个
  • 前端页面有那三层构成,分别是什么?作用是什么?

    结构 表现和行为 其中结构主要是有HTML标签组成 结构即在页面body里面我们写入的标签都是为了页面的结构 表现即指css样式表 通过css可以是页面的结构标签更具美感 行为是指页面和用户具有一定的交互 同时页面结构或者表现发生变化 主要
  • 接口测试工具-apifox

    Apifox 是 API 文档 API 调试 API Mock API 自动化测试一体化协作平台 定位 Postman Swagger Mock JMeter 通过一套系统 一份数据 解决多个系统之间的数据同步问题 只要定义好 API 文档
  • 将一个网页设置为屏保

    有没有试过将一个网页作为屏保 最近我正好有这个需求 一些需要给家里人经常看到的提示信息 如果定闹钟多了大家嫌烦 我口头提示多了比闹钟还烦 印在A4纸上贴墙上既影响美观又不容易修改内容 打印还要花银子 自己写字又不好看 突然想到我几乎每天早上
  • Unity播放音频

    在Unity中 可以在物体上添加AudioSource组件来播放音频 AudioSource组件可以控制音频文件的播放 音量 音调 空间效果等属性 以下是在物体上添加AudioSource组件的步骤 1 在Unity中打开场景 选择您想要添
  • tensorflow-gpu 2.3.0安装 及 相关对应版本库安装(Anaconda安装)

    目录 如需转载 请标明出处 谢谢 一 安装tensorflow gpu2 3 0 二 配置其他相关的库 很多人以为安装完tensorflow gpu就是一切都结束了 但是殊不知 python中的很多库 比如numpy matplotlib等
  • 【JavaScript数据结构与算法】一、栈及leetcode实战

    栈 栈是一种遵从后进先出 LIFO 原则的有序集合 新添加或待删除的元素都保存在栈的同一端 称作栈顶 另一端就叫栈底 在栈里 新元素都靠近栈顶 旧元素都接近栈底 栈数据结构 我们需要一种数据结构来保存栈里的元素 可以选择数组 数组允许我们在