栈与递归——385、341、394

news2024/11/13 9:22:56

385. 迷你语法分析器(中等)

给定一个字符串 s 表示一个整数嵌套列表,实现一个解析它的语法分析器并返回解析的结果 NestedInteger 。

列表中的每个元素只可能是整数或整数嵌套列表

示例 1:

输入:s = "324",
输出:324
解释:你应该返回一个 NestedInteger 对象,其中只包含整数值 324。

示例 2:

输入:s = "[123,[456,[789]]]",
输出:[123,[456,[789]]]
解释:返回一个 NestedInteger 对象包含一个有两个元素的嵌套列表:
1. 一个 integer 包含值 123
2. 一个包含两个元素的嵌套列表:
    i.  一个 integer 包含值 456
    ii. 一个包含一个元素的嵌套列表
         a. 一个 integer 包含值 789

解法一、栈

ph是一个检测用的符号,它前面为空,告诉后续检测到这里为止。对于每个字符

  1. 是[,入栈一个空的实例,加ph作为标识
  2. 是],把ph前放入list,把ph移除,把list放进那个空实例
  3. 是数字,while循环摘出数字,放进栈
  4. 是, continue

需要注意的细节:while后面i是超出了数字范围的,需要一个i--。每次i++检测还需要判断i<len,防止越界。

class Solution {
    static NestedInteger ph = new NestedInteger(0);
    public static NestedInteger deserialize(String s) {
        Deque<NestedInteger> st = new ArrayDeque<>();
        boolean negative = false;
        int len = s.length(),num = 0;
        for(int i = 0;i < len;i++){
            if(s.charAt(i) == ','){
            }else if(s.charAt(i) == '['){
                st.addLast(new NestedInteger());
                st.addLast(ph);
            }else if(s.charAt(i) == ']'){
                List<NestedInteger> list = new ArrayList<>();
                while(st.peekLast() != ph){
                    list.add(st.removeLast());
                }
                st.removeLast();
                for(int j = list.size() - 1;j >=0;j--)st.peekLast().add(list.get(j));
            }else{
                num = 0;
                if(s.charAt(i) == '-'){
                    i++;
                    negative = true;
                }
                while(Character.isDigit(s.charAt(i))){
                    num = num * 10 + s.charAt(i) - '0';
                    i++;
                }
                i--;
                st.addLast(negative ? new NestedInteger(-num):new NestedInteger(num));
                negative = false;
            }
        }
        return st.pop();
    }
}

解法二、递归

对于一个字符串,它无非是数值型或者列表型。那么分出一个if语句,分别处理列表和数字。对于列表,循环解析列表内所含的数字与列表,添入对象。对于数字,返回解析好的对象。

class Solution {
    static int index = 0; // 当前解析字符串的位置索引

    public static NestedInteger deserialize(String s) {
        // 如果当前字符是 '[',表示开始解析一个嵌套列表
        if(s.charAt(index) == '[') {
            index++; // 跳过 '['
            NestedInteger ni = new NestedIntegerImpl(); // 创建一个新的空的 NestedInteger
            while (s.charAt(index) != ']') { // 继续解析直到遇到 ']'
                ni.add(deserialize(s)); // 递归解析嵌套的部分,并将结果添加到当前 NestedInteger 中
                if(s.charAt(index) == ','){ // 如果遇到逗号,则跳过
                    index++;
                }
            }
            index++; // 跳过 ']'
            return ni; // 返回解析好的 NestedInteger 对象
        } else {
            // 如果当前字符不是 '[',表示这是一个单独的整数
            boolean negative = false;
            if (s.charAt(index) == '-') { // 检查是否是负数
                negative = true;
                index++;
            }
            int num = 0;
            // 解析数字部分
            while (index < s.length() && Character.isDigit(s.charAt(index))) {
                num = num * 10 + s.charAt(index) - '0'; // 生成数字
                index++;
            }
            if (negative) {
                num *= -1; // 如果是负数,乘以 -1
            }
            return new NestedIntegerImpl(num); // 返回解析好的单个整数的 NestedInteger 对象
        }
    }
}

341. 扁平化嵌套列表迭代器(中等)

给你一个嵌套的整数列表 nestedList 。每个元素要么是一个整数,要么是一个列表;该列表的元素也可能是整数或者是其他列表。请你实现一个迭代器将其扁平化,使之能够遍历这个列表中的所有整数。

实现扁平迭代器类 NestedIterator :

  • NestedIterator(List<NestedInteger> nestedList) 用嵌套列表 nestedList 初始化迭代器。
  • int next() 返回嵌套列表的下一个整数。
  • boolean hasNext() 如果仍然存在待迭代的整数,返回 true ;否则,返回 false 。

你的代码将会用下述伪代码检测:

initialize iterator with nestedList
res = []
while iterator.hasNext()
    append iterator.next() to the end of res
return res

如果 res 与预期的扁平化列表匹配,那么你的代码将会被判为正确。  

解法一、DFS

public class NestedIterator implements Iterator<Integer> {
    private List<Integer> vals; // 用于存储扁平化后的整数列表
    private Iterator<Integer> cur; // 当前遍历的迭代器

    // 构造函数,接收一个嵌套的列表并初始化迭代器
    public NestedIterator(List<NestedInteger> nestedList) {
        vals = new ArrayList<Integer>(); // 初始化存储整数的列表
        dfs(nestedList); // 调用深度优先搜索方法,将嵌套列表扁平化
        cur = vals.iterator(); // 初始化当前迭代器
    }

    // 返回下一个整数
    @Override
    public Integer next() {
        return cur.next(); // 使用迭代器获取下一个整数
    }

    // 判断是否还有下一个整数
    @Override
    public boolean hasNext() {
        return cur.hasNext(); // 判断迭代器是否还有下一个元素
    }

    // 深度优先搜索方法,用于将嵌套列表扁平化
    private void dfs(List<NestedInteger> nestedList) {
        for (NestedInteger nest : nestedList) { // 遍历嵌套列表中的每个元素
            if (nest.isInteger()) { // 如果是整数,则将其加入到vals列表中
                vals.add(nest.getInteger());
            } else { // 如果是列表,则递归处理该列表
                dfs(nest.getList());
            }
        }
    }
}

解法二、栈+递归 

public class NestedIterator implements Iterator<Integer> {
    // 栈用于存储当前遍历的列表迭代器
    private Deque<Iterator<NestedInteger>> stack;

    // 构造函数,接收一个嵌套列表并将其迭代器推入栈中
    public NestedIterator(List<NestedInteger> nestedList) {
        stack = new LinkedList<Iterator<NestedInteger>>(); // 初始化栈
        stack.push(nestedList.iterator()); // 将嵌套列表的迭代器推入栈
    }

    // 返回下一个整数
    @Override
    public Integer next() {
        // 由于保证调用 next 之前会调用 hasNext,直接返回栈顶列表的当前元素
        return stack.peek().next().getInteger();
    }

    // 判断是否还有下一个整数
    @Override
    public boolean hasNext() {
        while (!stack.isEmpty()) {
            Iterator<NestedInteger> it = stack.peek(); // 获取栈顶迭代器
            if (!it.hasNext()) { // 如果当前迭代器遍历完了,出栈
                stack.pop();
                continue;
            }
            NestedInteger nest = it.next(); // 获取下一个元素
            if (nest.isInteger()) { // 如果元素是整数
                List<NestedInteger> list = new ArrayList<NestedInteger>();
                list.add(nest); // 将该整数封装为一个列表
                stack.push(list.iterator()); // 将该列表的迭代器推入栈中
                return true; // 由于找到一个整数,下次调用 next 时可以返回它
            }
            // 如果元素是一个列表,将该列表的迭代器推入栈中,继续展开
            stack.push(nest.getList().iterator());
        }
        return false; // 如果栈为空,则表示没有更多元素可供迭代
    }
}

都不是自己写的 不了解迭代器,只是看了一下。

394. 字符串解码(中等)

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

解法一、栈

  1. 如果c是字母,更新res
  2. 如果c是数字,更新multi
  3. 如果c是[,入栈,然后重置res和multi
  4. 如果c是],出栈,得到旧res,和栈后的拼接新的res
class Solution {
    public static String decodeString(String s) {
        int len = s.length(),mulit = 0;;
        StringBuffer res = new StringBuffer();
        Deque<Integer> multi_st = new ArrayDeque<>();
        Deque<String> res_st = new ArrayDeque<>();
        for(int i = 0;i < len;i++){
            if(i<len && s.charAt(i) == '['){
                multi_st.push(mulit);
                mulit = 0;
                res_st.push(res.toString());
                res = new StringBuffer();
            }else if(i < len && s.charAt(i) == ']'){
                int t = multi_st.pop();
                StringBuffer temp = new StringBuffer();
                while(t != 0){
                    temp.append(res);
                    t--;
                }
                res =  new StringBuffer(res_st.pop() + temp);
            }else if(i < len && Character.isDigit(s.charAt(i))){
                while(Character.isDigit(s.charAt(i))){
                    mulit = mulit * 10 + s.charAt(i) - '0';
                    i++;
                }
                i--;
            }else{
                while(i < len && Character.isLetter(s.charAt(i))){
                    res.append(s.charAt(i));
                    i++;
                }
                i--;
            }
        }
        return res.toString();
    }
}

解法二、递归 

见注释

class Solution {
    // 主函数,调用递归函数进行字符串解码
    public String decodeString(String s) {
        return dfs(s, 0)[0]; // 从字符串的第一个字符开始进行递归解码
    }

    // 递归函数,解码从索引 i 开始的子字符串
    private String[] dfs(String s, int i) {
        StringBuilder res = new StringBuilder(); // 存储解码后的结果
        int multi = 0; // 用于存储当前的数字倍数
        while(i < s.length()) {
            if(s.charAt(i) >= '0' && s.charAt(i) <= '9') {
                // 如果当前字符是数字,更新 multi 值(支持多位数)
                multi = multi * 10 + Integer.parseInt(String.valueOf(s.charAt(i))); 
            } else if(s.charAt(i) == '[') {
                // 遇到 '[' 表示要开始处理括号内的字符串,递归处理括号内的部分
                String[] tmp = dfs(s, i + 1);
                i = Integer.parseInt(tmp[0]); // 更新索引 i 为括号结束后的索引
                // 按照 multi 值将解码后的字符串重复相应的次数
                while(multi > 0) {
                    res.append(tmp[1]);
                    multi--;
                }
            } else if(s.charAt(i) == ']') {
                // 遇到 ']' 表示当前括号内的部分处理完毕,返回结果和当前索引
                return new String[] { String.valueOf(i), res.toString() };
            } else {
                // 如果当前字符是字母,直接添加到结果中
                res.append(String.valueOf(s.charAt(i)));
            }
            i++; // 移动到下一个字符
        }
        // 返回完整解码的字符串(用于最外层调用)
        return new String[] { res.toString() };
    }
}


作者:Krahets
链接:https://leetcode.cn/problems/decode-string/solutions/19447/decode-string-fu-zhu-zhan-fa-di-gui-fa-by-jyd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

碎碎念

  • 385学到了一堆东西,包括复习了词法分析器,了解到了DFS,然后了解了抽象的接口。
  • 今天的对我来说还是有点太复杂了,需要很多前置知识,对类和接口这种基础都一窍不通····花了差不多快三四个小时
  • 感觉能用栈的也可以用递归做。这类的标识:嵌套,有对立的括号型标识

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2049235.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

8个Python实用技巧,让你的代码效率飞起来

想要成为一名Python编程高手&#xff0c;不仅需要掌握语言的基础知识&#xff0c;还需要了解一些高效编程的技巧。 一、列表推导式 列表推导式是Python中一种快速生成列表的方法&#xff0c;相比传统的循环方式&#xff0c;代码更简洁、更高效。 # 使用列表推导式生成0到9的平…

vuex刷新数据丢失解决方式(插件)

1、下载插件 npm install vuex-persistedstate --save2、引入即可 import Vue from vue import Vuex from vuex import createPersistedState from vuex-persistedstate Vue.use(Vuex)const store new Vuex.Store({state: {login: null},mutations: {setlogin(state, login) …

总结HashMap, HashTable, ConcurrentHashMap 之间的区别

HashMap, HashTable, ConcurrentHashMap都是使用同一种数据结构&#xff08;数组链表&#xff09;&#xff1b; HashMap多线程状态下是不安全的&#xff1b;HashTable和ConcurrentHashMap是在HashMap的基本的数据结构上进行优化&#xff0c;使他们在多线程下是安全的&#xff…

24/8/17算法笔记 AC算法

AC算法通常指的是Actor-Critic&#xff08;演员-评论家&#xff09;算法&#xff0c;它是强化学习中的一种算法框架&#xff0c;用于解决代理&#xff08;agent&#xff09;在环境中的决策问题。Actor-Critic方法结合了价值估计和策略优化&#xff0c;通常比纯粹的价值迭代或策…

Nginx 配置文件中 location、proxy_pass最后的斜杠/作用

1.搭建一个简单的sprigbootthymeleaf项目 写个controller RequestMapping("/login") public String toLogin() {return "login"; } 写一个login.html页面 <!DOCTYPE html> <html xmlns:th"http://www.thymeleaf.org"> <head&…

瑞吉外卖后续笔记

Git学习 下载地址 Githttps://git-scm.com/ 常用的git代码托管服务 git常用命令 Git 全局设置: git config --global user.name "刘某人" git config --global user.email "邮箱号" 查看配置 git config --list git add 文件名 添加到暂冲区 git a…

SpringBoot--05--整合WebSocket,实现全双工通信

文章目录 为什么需要websocket项目中使用websocket导入maven坐标编写配置类server代码前端代码和http请求URL区别 为什么需要websocket 传统的HTTP协议是单向通信的&#xff0c;支持客户端向服务器发送请求&#xff0c;服务器接收请求。但是服务器有时也要向客户端发送请求。 …

万能视频播放器PotPlayer

软件介绍 PotPlayer播放器是一款全能 多媒体 影音播放器,堪称Windows平台最强大的本地 视频播放器. PotPlayer最新版拥有强劲播放引擎加速,支持DXVA,CUDA,QuickSync,多媒体播放器支持蓝光3D,其内置强大的编码器及滤镜/分离器。 支持自定义添加解码器,对字幕的支持非常优秀,能…

基于CST的特征模天线设计

前言&#xff1a; 特征模理论是在矩量法基础上发展而来的适用于各种电磁辐射和散射问题分析的理论&#xff0c;它有效综合了这两类方法的长处且克服了它们的不足&#xff0c;不仅可以通过明确的物理含义来直观深刻地揭示天线的工作原理&#xff0c;而且能求解任意辐射结构的复…

谷粒商城实战笔记-193~194-商城业务-多线程-线程池

文章目录 一&#xff0c;193-商城业务-异步-异步复习1. 继承Thread类2. 实现Runnable接口3. 实现Callable接口结合FutureTask4. 使用线程池 二&#xff0c;194-商城业务-异步-线程池详解1&#xff0c;线程池七大参数2&#xff0c;面试题3&#xff0c;Executors能创建的4中线程池…

个人经历分享如何用Python日入1K+,分享兼职网站和渠道!

大部分人主要通过接私活赚钱。我第一次接单是朋友介绍的&#xff0c;当时刚学Python&#xff0c;为一家公司爬数据&#xff0c;赚了一千多。从那之后逐渐熟练&#xff0c;常在假期接一些数据处理的单&#xff0c;很多时候兼职收入是主业收入的2~3倍。 附上我前两年的兼职接单记…

黑马头条vue2.0项目实战(十一)——功能优化(组件缓存、响应拦截器、路由跳转与权限管理)

1. 组件缓存 1.1 介绍 先来看一个问题&#xff1f; 从首页切换到我的&#xff0c;再从我的回到首页&#xff0c;我们发现首页重新渲染原来的状态没有了。 首先&#xff0c;这是正常的状态&#xff0c;并非问题&#xff0c;路由在切换的时候会销毁切出去的页面组件&#xff…

【vue讲解:vue3介绍、setup、ref、reactive、监听属性、生命周期、toRef、setup写法】

1 vue3介绍 # Vue3的变化-vue3完全兼容vue2---》但是vue3不建议用vue2的写法-拥抱TypeScript-之前咱们用的JavaScript---》ts完全兼容js- 组合式API和配置项APIvue2 是配置项apivue3 组合式api# vue4必须要用2 vue3项目创建和启动 # 创建vue3项目-vue-cli 官方不太建议用了…

【Java学习】方法的引用

所属专栏&#xff1a;Java学习 &#x1f341;1. 方法引用 方法的引用&#xff1a;把已经存在的方法拿来使用&#xff0c;当作函数式接口中抽象方法的方法体 " :: "是方法引用符 方法引用时需要注意&#xff1a; 1. 需要有函数式接口 2. 被引用的方法必须存在 3. …

4款专业高效的Win10 录屏工具大揭秘!

Win10 系统里面一般都有自带的录屏工具&#xff0c;用截图工具就可以实现录屏。但是呢&#xff0c;这个工具只适合录制一些简短的小片段。如果想要更多功能的录屏&#xff0c;还是需要使用到第三方的录屏工具&#xff0c;这次&#xff0c;我就跟大家分享4款专业的录屏软件。 1、…

拟南芥中基因家族序列的提取

1.拟南芥基因组数据的下载 phytozome 是一个收录植物基因组数据的网站&#xff0c;数据整理比较规范&#xff0c;已 经提供了去除可变剪切的 cds 和 protein 序列文件。只有 gff3 文件需要 过滤处理 2. 对拟南芥的注释文件gff3文件进行ID处理&#xff0c;最终得到以下4个文件 …

深度解析:.secret勒索病毒如何加密你的数据并勒索赎金

引言&#xff1a; 在当今这个数字化、信息化的时代&#xff0c;网络安全已成为一个不容忽视的重要议题。随着互联网的普及和技术的飞速发展&#xff0c;我们的生活、工作乃至整个社会的运转都越来越依赖于各种计算机系统和网络。然而&#xff0c;这种高度依赖也为我们带来了前…

硬件面试经典 100 题(51~70 题)

51、请列举您知道的覆铜板厂家。 生益、建滔。 52、示波器铭牌一般都会标识两个参数&#xff0c;比如泰克 TDS1002B 示波器标识的 60MHz 和 1GS/s&#xff0c;请解释这两个参数的含义。 60MHz 是指示波器的带宽&#xff0c;即正常可以测量 60MHz 频率以下的信号。 1GS/s 是指示…

鲲鹏920s 32核处理器linpack性能调优

1、BIOS参数调优 BIOS选项 设置值 Power Policy Performance Stream Write Mode Allocate share LLC CPU Prefetching Configuration Enabled Custom Refresh Rate 64ms Die Interleaving Disabled NUMA Enable SSBS Support Disabled 2、benchmark参数调优 主…

vue项目将px转成其他单位,如rem、cqw,postcss-pxtorem的使用

安装插件 新建配置文件.postcssrc.js // module.exports { // "plugins": { // "postcss-pxtorem": { // rootValue: 1,//必须和rem的初始值一致 // propList: [*], // // selectorBlackList: [ // // ant…