栈与递归——385、341、394

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,然后了解了抽象的接口。
  • 今天的对我来说还是有点太复杂了,需要很多前置知识,对类和接口这种基础都一窍不通····花了差不多快三四个小时
  • 感觉能用栈的也可以用递归做。这类的标识:嵌套,有对立的括号型标识
  • 18
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值