LeetCode刷题——栈与递归篇

94 阅读9分钟

用栈访问最后若干元素

682.棒球比赛(简单)

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

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

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

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

输入:ops = ["5","2","C","D","+"]
输出:30
解释:
"5" - 记录加 5 ,记录现在是 [5]
"2" - 记录加 2 ,记录现在是 [5, 2]
"C" - 使前一次得分的记录无效并将其移除,记录现在是 [5].
"D" - 记录加 2 * 5 = 10 ,记录现在是 [5, 10].
"+" - 记录加 5 + 10 = 15 ,记录现在是 [5, 10, 15].
所有得分的总和 5 + 10 + 15 = 30

使用链表模拟栈

class Solution {
    public int calPoints(String[] operations) {
        List<Integer> list = new ArrayList<>(); 
        int index = 0;
        for(String ops : operations){
            if("C".equals(ops)){
                index--;
            }else if("D".equals(ops)){
                list.add(index, list.get(index-1) * 2);
                index++;
            }else if("+".equals(ops)){
                list.add(index, list.get(index-1) + list.get(index-2));
                index++;
            }else{
                list.add(index, Integer.parseInt(ops));
                index++;
            }
        }
        int sum = 0;
        for(int i = 0; i < index; i++){
            sum += list.get(i);
        }
        return sum;
    }
}

使用栈

class Solution {
    public int calPoints(String[] operations) {
        Stack<Integer> stack = new Stack<>();
        int sum = 0;
        for(String str : operations){
            switch(str){
                case "+":
                    int a = stack.pop();
                    int b = stack.peek();
                    int temp = a + b;
                    sum += temp;
                    stack.push(a);
                    stack.push(temp);
                    break;
                case "C":
                    int last = stack.pop();
                    sum -= last;
                    break;
                case "D":
                    int newnum = stack.peek() * 2;
                    stack.push(newnum);
                    sum += newnum;
                    break;
                default:
                    sum += stack.push(Integer.parseInt(str));
                    break;
            }
        }
        return sum;
    }
}

71.简化路径(中等)★

给你一个字符串 path ,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 '/' 开头),请你将其转化为更加简洁的规范路径。

在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,'//')都被视为单个斜杠 '/' 。 对于此问题,任何其他格式的点(例如,'...')均被视为文件/目录名称。

请注意,返回的 规范路径 必须遵循下述格式:

始终以斜杠 '/' 开头。
两个目录名之间必须只有一个斜杠 '/' 。
最后一个目录名(如果存在)不能 以 '/' 结尾。
此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含 '.''..')。

输入: path = "/a/./b/../../c/"
输出: "/c"
class Solution {
    public String simplifyPath(String path) {
        //使用split时,分割符在头尾会产生空格
        String[] str  = path.split("/");  
        //使用双端队列比较方便 
        Deque<String> deque = new ArrayDeque<>();      
        
        for(String s : str){
            if("..".equals(s)){
                if(!deque.isEmpty()){
                    deque.pollLast();
                }
            } else if(s.length() > 0 && !".".equals(s)){ //空格或.不需要处理
                deque.offerLast(s);
            }
        }

        StringBuffer sb = new StringBuffer();
        if(deque.isEmpty()){
            sb.append("/");
        }else{
             while(!deque.isEmpty()){
                String s = deque.pollFirst();
                sb.append("/" + s);
            }
        }
        return sb.toString();
    }
}

388.文件的最长绝对路径(中等)★

图片.png /n为换行符,分割两个文件夹,/t为制表符,表示当前元素在文件系统树中的深度

class Solution {
    //首先,文件系统为一棵树,\t表示当前文件夹或文件的深度
    //我们将遍历到的文件夹和文件加入到栈中
    //若当前文件或文件夹的深度小于栈顶的元素,则栈必须回退至符合条件
    //由于只求长度,故在栈中需要记录文件的长度即可,然后不断更新最大值
    //注意含有.的才是文件
    public int lengthLongestPath(String input) {
        int len = input.length();
        int index = 0;
        int ans = 0;
        Deque<Integer> deque = new ArrayDeque<>();
        while(index < len){
            //先求出当前元素的深度
            int depth = 1;
            while(index < len && input.charAt(index) == '\t'){
                depth++;
                index++;
            }
            //记录当前元素长度,判断是否为文件
            boolean isFile = false;
            int length = 0;
            while(index < len && input.charAt(index) != '\n'){
                if(input.charAt(index) == '.'){
                    isFile = true;
                }
                index++;
                length++;
            }
            index++;    //跳过当前换行符

            
            while(deque.size() >= depth){   //若当前栈的深度大于等于当前元素的深度,则必须出栈直到小于
                deque.pop();
            }
            if(!deque.isEmpty()){
                length += deque.peek()+1;   //当前栈顶元素的长度加文件本身长度加1
            }
            if(isFile){
                ans = Math.max(ans, length);    //是文件时比较大小
            }else{
                deque.push(length);
            }   
        }
        return ans;
    }
}
class Solution {
    //使用hashMap存储最新的文件夹路径,等遍历到文件时长度为map[n-1]+len
    static int[] hashMap = new int[10010];
    public int lengthLongestPath(String input) {
        Arrays.fill(hashMap, -1);
        int ans = 0;
        int n = input.length();

        for(int i = 0; i < n; ){        //相当于while循环
            int depth = 0;
            //计算当前文件/文件夹深度
            while(i < n && input.charAt(i) == '\t'){
                depth++;
                i++;
            }
            //计算当前元素长度
            int len = 0;
            boolean isDir = true;
            while(i < n && input.charAt(i) != '\n'){
                if(input.charAt(i) == '.'){
                    isDir  = false;
                }
                i++;
                len++;
            }
            int prev = depth - 1 >= 0 ?  hashMap[depth - 1] : -1;  //当前文件夹或文件上一级文件夹的长度
            int path = prev + len + 1; //当前文件夹或文件长度
            if(isDir){
                hashMap[depth] = path;  //将hashmap的值修改为当前的路径长度
            }else if(path > ans){
                ans = path;
            }
            i++;
        }
        return ans; 
    }
}

150.逆波兰表达式求值(中等)

输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
class Solution {
    //逆波兰表达式即后缀表达式,使用一个数字栈,遇到数字时进栈,遇到运算符时出栈两个元素,最后剩下的元素即为结果,使用数组模拟栈即可
    public int evalRPN(String[] tokens) {
        int[] stack = new int[tokens.length/2 + 1];//表达式中运算数的数量为运算符数量加一
        int index = 0;
        for(String s : tokens){
            switch(s){
                case "+":
                    stack[index - 2] += stack[index - 1];
                    index--;
                    break;
                case "-":
                    stack[index - 2] -= stack[index - 1];
                    index--;
                    break;
                case "*":
                    stack[index - 2] *= stack[index - 1];
                    index--;
                    break;
                case "/":
                    stack[index - 2] /= stack[index - 1];
                    index--;
                    break;
                default:
                    stack[index] = Integer.parseInt(s);
                    index++;
                    break;
            }
        }
        return stack[0];
    }
}

227.基本计算器II(中等)★

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。 整数除法仅保留整数部分。 你可以假设给定的表达式总是有效的。所有中间结果将在 [-2^31, 2^31 - 1] 的范围内。 注意操作符前后有空格

输入:s = "3+2*2"
输出:7
//由于乘除法的优先级高于加减法,故先对乘除法进行运算,遍历字符串,遇到数字时求出完整的数字再入栈
//遇到加法时后续数字直接入栈,遇到减法时,后续数字取相反数后入栈
//遇到乘除法时后续数字与当前栈顶元素进行操作,结果入栈,最后将栈内数字全部相加即可
class Solution {
    public int calculate(String s) {
        Deque<Integer> deque = new ArrayDeque<>();
        char perSign = '+'; //默认符号为加号
        int len = s.length();
        int num = 0;
        
        for(int  i = 0; i < len; i++){
            if(Character.isDigit(s.charAt(i))){
                num = num * 10 + s.charAt(i) - '0';
            }
            if(!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' ' || i == len-1){ //注意当i=len-1,当前为数字,需要将前一个存储起来的运算符用掉
                switch(perSign){
                    case '+':
                        deque.push(num);
                        break;
                    case '-':
                        deque.push(-num);
                        break;
                    case '*':
                        deque.push(num * deque.pop());
                        break;
                    default:
                        deque.push(deque.pop() / num);
                        break;
                }
                perSign = s.charAt(i);
                num = 0;
            }
        }
        int ans = 0;
        while(!deque.isEmpty()){
            ans += deque.pop();
        }
        return ans;
    }
}

使用双栈实现一个完全计算器

class Solution {
    //预处理运算符优先级
    Map<Character, Integer> map = new HashMap<>(){{
        put('-', 1);
        put('+', 1);
        put('*', 2);
        put('/', 2);
        put('%', 2);
        put('^', 3);
    }};
    public int calculate(String s) {
        s = s.replaceAll(" ", "");  // 去除所有空格
        char[] cs = s.toCharArray();
        int n = s.length();
        Deque<Integer> nums = new ArrayDeque<>();   //数字栈
        nums.addLast(0);    //防止第一个数为负数,先往nums中添加0
        Deque<Character> ops = new ArrayDeque<>();  //操作符栈
        for (int i = 0; i < n; i++) {
            char c = cs[i];
            if (c == '(') {
                ops.addLast(c);
            } else if (c == ')') {
                // 计算到最近一个左括号为止
                while (!ops.isEmpty()) {
                    if (ops.peekLast() != '(') {
                        calc(nums, ops);
                    } else {
                        ops.pollLast();
                        break;
                    }
                }
            } else {
                if (isNumber(c)) {
                    int u = 0;
                    int j = i;
                    // 将从 i 位置开始后面的连续数字整体取出,加入 nums
                    while (j < n && isNumber(cs[j])) u = u * 10 + (cs[j++] - '0');
                    nums.addLast(u);
                    i = j - 1;      //i指向数字的最后一位,方便继续遍历
                } else {
                    if (i>0 && (cs[i - 1] == '(' || cs[i - 1] == '+' || cs[i - 1] == '-')) {
                        nums.addLast(0); //上个位置为左括号或加号或减号时,加入0避免边界条件判断
                    }
                    while (!ops.isEmpty() && ops.peekLast() != '(') {
                        char prev = ops.peekLast();
                        if (map.get(prev)>=map.get(c)) {//栈内优先级大于等于当前运算符的都计算
                            calc(nums, ops);
                        } else {
                            break;
                        }
                    }
                    ops.addLast(c);     //将当前运算符入栈
                }
            }
        }
        // 将剩余的计算完
        while (!ops.isEmpty()) calc(nums, ops);
        return nums.peekLast();
    }
    void calc(Deque<Integer> nums, Deque<Character> ops) {
        if (nums.isEmpty() || nums.size() < 2) return;
        if (ops.isEmpty()) return;
        int b = nums.pollLast(), a = nums.pollLast();
        char op = ops.pollLast();
        int ans = 0;
        if (op == '+') ans = a + b;
        else if (op == '-') ans = a - b;
        else if (op == '*') ans = a * b;
        else if (op == '/')  ans = a / b;
        else if (op == '^') ans = (int)Math.pow(a, b);
        else if (op == '%') ans = a % b;
        nums.addLast(ans);
    }
    boolean isNumber(char c) {
        return Character.isDigit(c);
    }
}

224.基本计算器(困难)

解法同上

20.有效的括号(简单)

给定一个括号序列字符串,要求判断括号是否全部能够匹配。

输入: s = "()[]{}"
输出: true
//使用栈
class Solution {
    public boolean isValid(String s) {
        Deque<Character> deque = new ArrayDeque<>();
        char[] ch = s.toCharArray();
        for(char c : ch){
            if(c =='(' || c == '[' || c == '{'){
                deque.addLast(c);
            }else{
                if(!deque.isEmpty()){
                    if(c == ')'){
                        if(deque.pollLast() != '(') return false;
                    }else if(c == ']'){
                        if(deque.pollLast() != '[') return false;
                    }else{
                        if(deque.pollLast() != '{') return false;
                    }
                }else{
                    return false;
                }
            }
        }
        return deque.isEmpty();
    }
}

636.函数的独占时间(中等)

有一个 单线程 CPU 正在运行一个含有 n 道函数的程序。每道函数都有一个位于 0 和 n-1 之间的唯一标识符。

函数调用 存储在一个 调用栈 上 :当一个函数调用开始时,它的标识符将会推入栈中。而当一个函数调用结束时,它的标识符将会从栈中弹出。标识符位于栈顶的函数是 当前正在执行的函数 。每当一个函数开始或者结束时,将会记录一条日志,包括函数标识符、是开始还是结束、以及相应的时间戳。

给你一个由日志组成的列表 logs ,其中 logs[i] 表示第 i 条日志消息,该消息是一个按 "{function_id}:{"start" | "end"}:{timestamp}" 进行格式化的字符串。例如,"0:start:3" 意味着标识符为 0 的函数调用在时间戳 3 的 起始开始执行 ;而 "1:end:2" 意味着标识符为 1 的函数调用在时间戳 2 的 末尾结束执行。注意,函数可以 调用多次,可能存在递归调用 。

函数的 独占时间 定义是在这个函数在程序所有函数调用中执行时间的总和,调用其他函数花费的时间不算该函数的独占时间。例如,如果一个函数被调用两次,一次调用执行 2 单位时间,另一次调用执行 1 单位时间,那么该函数的 独占时间 为 2 + 1 = 3 。

以数组形式返回每个函数的 独占时间 ,其中第 i 个下标对应的值表示标识符 i 的函数的独占时间。

输入:n = 2, logs = ["0:start:0","0:start:2","0:end:5","1:start:6","1:end:6","0:end:7"]
输出:[7,1]
class Solution {
    //栈中存储标志位start的项,存储其编号和时间戳,表示当前在运行或等待运行的函数
    //注意栈顶元素的时间戳需要随着标志为end的项的到来而变化,表示当前的开始时间
    //当有新函数入栈时,要计算前一个函数运行了多长时间并记录到数组中
    public int[] exclusiveTime(int n, List<String> logs) {
        Deque<int[]> deque = new ArrayDeque<>();  //存储每个函数的编号和时间戳
        int[] ans = new int[n];
        for(String log : logs){
            int index = Integer.parseInt(log.substring(0, log.indexOf(":")));
            String type = log.substring(log.indexOf(":")+1, log.lastIndexOf(":"));
            int t = Integer.parseInt(log.substring(log.lastIndexOf(":") + 1));

            if("start".equals(type)){
                if(!deque.isEmpty()){
                    ans[deque.peek()[0]] += (t - deque.peek()[1]);
                    deque.peek()[1] = t;
                }
                deque.push(new int[]{index, t});
            }else{
                ans[index] += (t - deque.pop()[1] + 1);
                if(!deque.isEmpty()){
                    deque.peek()[1] = t + 1;
                }
            }
        }
        return ans;
    }
}

32.最长有效括号(困难)★

给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

输入: s = ")()())"
输出: 4
解释: 最长有效括号子串是 "()()"

方法1.动态规划

class Solution {
    //使用动态规划,dp[i]表示以s.charAt(i)结尾的字符的有效括号长度
    //若当前字符为'(',则以其结尾的字符串肯定不是有效括号,故为长度为0
    //若当前字符为')',分两种情况
    //1.前一个字符为'(',即"...()",则dp[i] = dp[i-2]+2
    //2.前一个字符为')',即"...))",前一个')'能组成的有效括号长度为dp[i-1]
    //若最后一个')'要有效,必须在s[i-dp[i-1]-1]的位置为'(',即"(...))",再加上'('前面的有效长度
    //dp[i] = dp[i-dp[i-1]-2] + dp[i-1] + 2
    public int longestValidParentheses(String s) {
        int ans = 0;
        int n = s.length();
        int[] dp = new int[n];
        for(int i = 1; i < n; i++){
            if(s.charAt(i) == ')'){
                if(s.charAt(i-1) == '('){
                    dp[i] = (i >= 2 ? dp[i-2] : 0) + 2;
                }else if(i-dp[i-1] > 0 && s.charAt(i-dp[i-1]-1) == '('){
                    dp[i] = (i-dp[i-1]-2 >= 0 ? dp[i-dp[i-1]-2] : 0) + dp[i-1] + 2;
                }
                ans = Math.max(ans, dp[i]);
            }
        }
        return ans;
    }
}

方法2.栈

class Solution {
    //使用栈,栈中存储的是每个左括号的下标,当遍历到右括号时,弹出当前栈顶左括号
    //并用当前右括号下标减去当前栈顶左括号下标计算出有效括号长度
    //需要注意的是,我们需要在栈底存储一个最后未被匹配的右括号的下标
    //当我们遇到下一个右括号且栈中没有左括号与其匹配(即栈底的右括号成为栈顶)
    //则本段匹配结束,当前右括号成为新的栈底
    //为了防止第一个括号为左括号直接入栈的情况,我们预先在栈中加入元素-1
    public int longestValidParentheses(String s) {
        Deque<Integer> deque = new ArrayDeque<>();
        deque.push(-1);
        char[] ch = s.toCharArray();
        int ans = 0;
        for(int i = 0; i < ch.length; i++){
            if(ch[i] == '('){
                deque.push(i);
            }else{
                deque.pop();
                if(deque.isEmpty()){
                    deque.push(i);
                }else{
                    ans = Math.max(ans, i - deque.peek()); 
                }
            }
        }
        return ans;
    }
}

左右双指针法

//从左到右计算左右括号的数量,当左右括号数量相等时,计算得出长度
//当右括号的数量大于左括号的数量时,左右括号数量都归零
//但这种计算无法应对"((()"的情况,故需要从右到左再遍历一遍,条件相反
class Solution {
    public int longestValidParentheses(String s) {
        int left = 0, right = 0, maxlength = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxlength = Math.max(maxlength, 2 * right);
            } else if (right > left) {
                left = right = 0;
            }
        }
        left = right = 0;
        for (int i = s.length() - 1; i >= 0; i--) {
            if (s.charAt(i) == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxlength = Math.max(maxlength, 2 * left);
            } else if (left > right) {
                left = right = 0;
            }
        }
        return maxlength;
    }
}

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

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

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

输入: s = "324"
输出: 324
输入: s = "[123,[456,[789]]]"
输出: [123,[456,[789]]]

使用递归,深度优先搜索

class Solution {
    int i = 0;      //设置全局索引,保证按序处理s
    public NestedInteger deserialize(String s) {
        if(s.charAt(i) == '['){     //若遇到[,开启一个新的列表
            i++;
            NestedInteger ni = new NestedInteger();
            while(s.charAt(i) != ']'){      //当前列表未结束,递归调用deserialize解析
                ni.add(deserialize(s));
                if(s.charAt(i) == ',') i++;
            }
            i++;        //继续为下一次递归做准备,或者最后一次递归直接越界
            return ni;  //返回当前递归层次的ni
        }else{
            boolean negative = false;
            if(s.charAt(i) == '-'){
                i++;
                negative = true;
            }
            int num = 0;
            while(i < s.length() && Character.isDigit(s.charAt(i))){
                num = num * 10 + s.charAt(i) - '0';
                i++;
            }
            if(negative) num *= -1;
            return new NestedInteger(num);  //注意需要返回NestedInteger类型
        }
    }
}

使用栈模拟递归过程

class Solution {
    //使用栈模拟递归
    public NestedInteger deserialize(String s) {
        if(s.charAt(0) != '[') return new NestedInteger(Integer.parseInt(s));//只有数字的情况
        Deque<NestedInteger> deque = new ArrayDeque<>();
        int num = 0;
        boolean negative = false;
        for(int i = 0; i < s.length(); i++){
            char c = s.charAt(i);
            if(c == '-'){
                negative = true;
            }else if(c == '['){
                deque.push(new NestedInteger());    //入栈一个新的NestedInteger
            }else if(Character.isDigit(c)){
                num = num * 10 + c - '0';
            }else if(c == ',' || c == ']'){
                if(Character.isDigit(s.charAt(i-1))){
                    if(negative) num *= -1;
                    deque.peek().add(new NestedInteger(num)); //将数字加入栈顶的NestedInteger
                }
                num = 0;
                negative = false;
                if(c == ']' && deque.size() > 1){
                    NestedInteger ni = deque.pop(); //当前NestedInteger结束,将其加入下一个NestedInteger
                    deque.peek().add(ni);
                }
            }
        }
        return deque.pop();  
    }
}

42.接雨水(困难)★

按行遍历 时间复杂度较高,无法通过特定的测试用例

class Solution {
    //按行更新雨水量num
    //例如对于第一行,从遇到第一个大于等于1的数字开始,若当前位置数字小于1
    //则temp_num+1,遇到下一个大于等于1的数时,说明前面累计的数量有效
    //故num+temp_num,temp_num置0,继续累加
    public int trap(int[] height) {
        int max = getMax(height);
        int ans = 0;
        for(int i = 1; i <= max; i++){
            int temp = 0;
            boolean flag = false;
            for(int h : height){
                if(flag && h < i) temp++;
                if(h >= i){
                    ans += temp;
                    temp = 0;
                    flag = true;
                }
            }
        }
        return ans;
    }
    
    public int getMax(int[] arr){
        if(arr.length == 0) return 0;
        int max = arr[0];
        for(int num : arr){
            if(num > max) max = num;
        }
        return max;
    }
}

按列遍历,动态规划维护左右最大值数组 时间复杂度降为O(n)

class Solution {
    //按列遍历,对于每一列,找出它左右最大的数,并得他们的最小值
    //最小值就是当前水面高度,若当前数比水面高度小,则有水
    //在求左右两边的最大值时可以使用动态规划,left_max[i]表示i左边最大的数,不包括i
    public int trap(int[] height) {
        int ans = 0;
        int n = height.length;
        int[] left_max = new int[n];
        int[] right_max = new int[n];
        for(int i = 1; i < n - 1; i++){
            left_max[i] = Math.max(height[i-1], left_max[i-1]);
        }
        for(int i = n-2; i >= 0; i--){
             right_max[i] = Math.max(height[i+1], right_max[i+1]);
        }
        for(int i = 1; i < n-1; i++){
            int min = Math.min(left_max[i], right_max[i]);
            if(height[i] < min) ans += min - height[i];
        }
        return ans;
    }
}
//对于i,left_max数组只使用了前一个数,故可以将其转化为一个变量,从左到右遍历即可
//因此对于right_max需要从右到左遍历
//双指针遍历法可以将空间复杂度降低为O(1)

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

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

实现扁平迭代器类 NestedIterator :

NestedIterator(List<NestedInteger> nestedList) 用嵌套列表 nestedList 初始化迭代器。
int next() 返回嵌套列表的下一个整数。
boolean hasNext()如果仍然存在待迭代的整数,返回 true;否则返回 false 。
输入:nestedList = [[1,1],2,[1,1]]
输出:[1,1,2,1,1]
解释:通过重复调用 next 直到 hasNext 返回 falsenext 返回的元素的顺序应该是: [1,1,2,1,1]。

在构造函数中提前扁平化整个嵌套列表(递归)

public class NestedIterator implements Iterator<Integer> {

    private LinkedList<Integer> ans; //用链表作为容器

    public NestedIterator(List<NestedInteger> nestedList) {
        ans = new LinkedList<>();
        DFS(nestedList);
    }

    @Override
    public Integer next() {
        return ans.removeFirst();
    }

    @Override
    public boolean hasNext() {
        return !ans.isEmpty();
    }

    public void DFS(List<NestedInteger> nestedList){
        for(NestedInteger n : nestedList){
            if(n.isInteger()){
                ans.addLast(n.getInteger());
            }else{
                DFS(n.getList());
            }
        }
        
    }
}

调用next或hasNext时扁平化当前的嵌套子列表(非递归)

public class NestedIterator implements Iterator<Integer> {

    //在next和hasNext函数中扁平化对应的子列表,先把所有元素逆序放入栈中
    //调用hasNext方法时,访问栈顶元素,若为数字则在next中弹出,若为列表则在hasNext中逆序入栈
    
    private Deque<NestedInteger> deque;     //使用队列作为容器,注意这里的泛型是NestedInteger
    public NestedIterator(List<NestedInteger> nestedList) {
        deque = new ArrayDeque<>();
        for(int i = nestedList.size()-1; i >= 0; i--){
            deque.addLast(nestedList.get(i));       //先把所有元素逆序入栈
        }
    }

    @Override
    public Integer next() {
        NestedInteger cur = deque.removeLast(); //弹出栈顶元素
        return cur.getInteger();
    }

    @Override
    public boolean hasNext() {
        while(!deque.isEmpty()){
            NestedInteger top = deque.peekLast();  //访问栈顶元素
            if(top.isInteger()){
                return true;
            }
            deque.removeLast();  //栈顶元素时列表
            for(int i = top.getList().size()-1; i >= 0; i--){   //对列表元素进行遍历
                deque.addLast(top.getList().get(i));
            }
        }
        return false;
    }
}

394.字符串解码(中等)★

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

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

输入: s = "3[a2[c]]"
输出: "accaccacc"

非递归,使用栈

class Solution {
    //非递归方式,使用数字栈和字符串栈实现
    //遇到左括号时,将当前字符串和数字入栈,并初始化字符串和数字
    //遇到右括号时,将当前字符串重复n次(从数字栈中取出),并与栈顶元素拼接
    public String decodeString(String s) {
        Deque<Integer> deque_num = new ArrayDeque<>();
        Deque<String> deque_str = new ArrayDeque<>();
        int num = 0;
        StringBuilder res = new StringBuilder();
        char[] ch = s.toCharArray();

        for(char c : ch){
            if(Character.isDigit(c)){
                num = num * 10 + c - '0';
            }else if(c == '['){
                deque_num.addLast(num);
                deque_str.addLast(res.toString());
                num = 0;
                res = new StringBuilder();
            }else if(c == ']'){
                StringBuilder temp = new StringBuilder();
                int multi = deque_num.removeLast();
                for(int i = multi; i > 0; i--){
                    temp.append(res);
                }
                res = new StringBuilder(deque_str.removeLast() + temp);  //注意此时需要更新当前字符串
            }else{
                res.append(c);
            }
        }
        return res.toString();
    }
}

递归

class Solution {
    public String decodeString(String s) {
        return dfs(s, 0)[0];    //调用dfs,结果存储在数组第一个值中
    }
    private String[] dfs(String s, int i) {
        StringBuilder res = new StringBuilder();
        int multi = 0;
        while(i < s.length()) {
            if(Character.isDigit(s.charAt(i))){
                multi = multi * 10 + s.charAt(i) - '0';
            }else if(s.charAt(i) == '[') {      //遇到左括号时更新指针位置
                String[] tmp = dfs(s, i + 1);   //并递归调用得到当前括号内的值(到右括号时返回)
                i = Integer.parseInt(tmp[0]);   //更新指针(移动到当前左括号对应的右括号)
                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() };     //将结果放在字符串数组首位返回
    } 
}