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是一个检测用的符号,它前面为空,告诉后续检测到这里为止。对于每个字符
- 是[,入栈一个空的实例,加ph作为标识
- 是],把ph前放入list,把ph移除,把list放进那个空实例
- 是数字,while循环摘出数字,放进栈
- 是, 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]
的输入。
解法一、栈
- 如果c是字母,更新res
- 如果c是数字,更新multi
- 如果c是[,入栈,然后重置res和multi
- 如果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,然后了解了抽象的接口。
- 今天的对我来说还是有点太复杂了,需要很多前置知识,对类和接口这种基础都一窍不通····花了差不多快三四个小时
- 感觉能用栈的也可以用递归做。这类的标识:嵌套,有对立的括号型标识