栈------中等

目录

71. 简化路径    中等

94. 二叉树的中序遍历    中等

103. 二叉树的锯齿形层次遍历    中等

144. 二叉树的前序遍历    中等

145. 二叉树的后序遍历    中等

173. 二叉搜索树迭代器    中等


71. 简化路径    中等

以 Unix 风格给出一个文件的绝对路径,你需要简化它。或者换句话说,将其转换为规范路径。

在 Unix 风格的文件系统中,一个点(.)表示当前目录本身;此外,两个点 (..) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。更多信息请参阅:Linux / Unix中的绝对路径 vs 相对路径

请注意,返回的规范路径必须始终以斜杠 / 开头,并且两个目录名之间必须只有一个斜杠 /。最后一个目录名(如果存在)不能/ 结尾。此外,规范路径必须是表示绝对路径的最短字符串。

示例 1:

输入:"/home/"
输出:"/home"
解释:注意,最后一个目录名后面没有斜杠。

示例 2:

输入:"/../"
输出:"/"
解释:从根目录向上一级是不可行的,因为根是你可以到达的最高级。

示例 3:

输入:"/home//foo/"
输出:"/home/foo"
解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。

示例 4:

输入:"/a/./b/../../c/"
输出:"/c"

示例 5:

输入:"/a/../../b/../c//.//"
输出:"/c"

示例 6:

输入:"/a//bc/d//././/.."
输出:"/a/b/c"
    public String simplifyPath(String path) {
        String strs[] = path.split("/");
        StringBuilder builder = new StringBuilder();
        Stack<String> stack = new Stack<>();
        for (String str : strs) {
            if (str.equals("..")) {
                if (!stack.isEmpty()) {
                    stack.pop();
                }
            } else if (!str.equals(".") && !str.equals("")) {
                stack.push(str);
            }
        }
        if (stack.isEmpty()) {
            return "/";
        }
        for (int i = 0; i < stack.size(); i++) {
            builder.append("/").append(stack.get(i));
        }
        return builder.toString();
    }

 解析:如果当前元素str为(..)时,且栈不为空,则弹栈

否则如果当前元素不为(.)且不为("")时,将str加入到栈中

最后对于栈中的元素,从前向后依次添加到返回字符串builder中

94. 二叉树的中序遍历    中等

给定一个二叉树,返回它的中序 遍历。

示例:

输入: [1,null,2,3]
   1
    \
     2
    /
   3

输出: [1,3,2]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        while (!stack.isEmpty() || root != null) {
            while (root != null) {
                stack.add(root);
                root = root.left;
            }
            root = stack.pop();
            ans.add(root.val);
            root = root.right;
        }
        return ans;
    }

103. 二叉树的锯齿形层次遍历    中等

给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

例如:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回锯齿形层次遍历如下:

[
  [3],
  [20,9],
  [15,7]
]
    Map<Integer, LinkedList<Integer>> map = new HashMap<>();
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        dfs(root, 0);
        List<List<Integer>> res = new ArrayList<>();
        for (Map.Entry<Integer, LinkedList<Integer>> entry : map.entrySet()) {
            res.add(entry.getValue());
        }
        return res;
    }

    private void dfs(TreeNode root, int k) {
        if (root == null) {
            return;
        }
        if (!map.containsKey(k)) {
            map.put(k, new LinkedList<>());
        }
        if (k % 2 == 0) {
            map.get(k).addLast(root.val);
        } else {
            map.get(k).addFirst(root.val);
        }
        dfs(root.left, k + 1);
        dfs(root.right, k + 1);
    }

解析:每次记录树的层级,如果奇数,则从左向右添加到当层集合中

如果是偶数,则从右向左添加到当层集合中

左右递归

144. 二叉树的前序遍历    中等

给定一个二叉树,返回它的 前序 遍历。

 示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [1,2,3]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        while (!stack.isEmpty() || root != null) {
            while (root != null) {
                ans.add(root.val);
                stack.add(root);
                root = root.left;
            }
            root = stack.pop();
            root = root.right;
        }
        return ans;
    }

145. 二叉树的后序遍历    中等

给定一个二叉树,返回它的 后序 遍历。

示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [3,2,1]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        Stack<TreeNode> stack1 = new Stack<TreeNode>();
        Stack<TreeNode> stack2 = new Stack<TreeNode>();
        while (!stack1.isEmpty() || root != null) {
            while (root != null) {
                stack1.add(root);
                stack2.add(root);
                root = root.right;
            }
            root = stack1.pop();
            root = root.left;
        }
        while (!stack2.isEmpty()) {
            ans.add(stack2.pop().val);
        }
        return ans;
    }

173. 二叉搜索树迭代器    中等

实现一个二叉搜索树迭代器。你将使用二叉搜索树的根节点初始化迭代器。

调用 next() 将返回二叉搜索树中的下一个最小的数。

示例:

BSTIterator iterator = new BSTIterator(root);
iterator.next();    // 返回 3
iterator.next();    // 返回 7
iterator.hasNext(); // 返回 true
iterator.next();    // 返回 9
iterator.hasNext(); // 返回 true
iterator.next();    // 返回 15
iterator.hasNext(); // 返回 true
iterator.next();    // 返回 20
iterator.hasNext(); // 返回 false

提示:

  • next() 和 hasNext() 操作的时间复杂度是 O(1),并使用 O(h) 内存,其中 是树的高度。
  • 你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 中至少存在一个下一个最小的数。
class BSTIterator {

    private List<Integer> list = new ArrayList<>();
    private int index;
    public BSTIterator(TreeNode root) {
        inOrder(root);
    }

    private void inOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        inOrder(root.left);
        list.add(root.val);
        inOrder(root.right);
    }
    
    public int next() {
        return list.get(index++);
    }
    
    public boolean hasNext() {
        return index < list.size();
    }
}

 解析:使用中序遍历,将结果存储在list集合中

每次直接从集合中获取和判断即可

316. 去除重复字母    中等

给你一个仅包含小写字母的字符串,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

示例 1:

输入: "bcabc"
输出: "abc"

示例 2:

输入: "cbacdcbc"
输出: "acdb"

注意:该题与 1081 https://leetcode-cn.com/problems/smallest-subsequence-of-distinct-characters 相同

    //递归
    public String removeDuplicateLetters(String s) {
        int[] cnt = new int[26];
        for (int i = 0; i < s.length(); i++) {
            cnt[s.charAt(i) - 'a']++;
        }
        int cur = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) < s.charAt(cur)) {
                cur = i;
            }
            if (--cnt[s.charAt(i) - 'a'] == 0) {
                break;
            }
        }
        return s.length() == 0 ? "" : s.charAt(cur) + removeDuplicateLetters(s.substring(cur + 1).replaceAll("" + s.charAt(cur), ""));
    }

解析:使用数组cnt将每个字符及其数量存储起来

如果第i个字符小于第cur个字符,则将第cur指针指向i

如果第i个字符为1的时候,直接break跳出

返回当前字符+递归获取下一个字符串

    //栈
    public String removeDuplicateLetters(String s) {
        //字符最后出现的位置
        Map<Character, Integer> map = new HashMap<>();
        //用于去重
        Set<Character> set = new HashSet<>();
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            map.put(s.charAt(i), i);
        }
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if (!set.contains(ch)) {
                while (!stack.isEmpty() && ch < stack.peek() && map.get(stack.peek()) > i) {
                    set.remove(stack.pop());
                }
                set.add(ch);
                stack.add(ch);
            }
        }
        StringBuilder builder = new StringBuilder();
        for (char ch : stack) {
            builder.append(ch);
        }
        return builder.toString();
    }

解析:重点理解,如果当前元素小于栈顶元素,则弹栈

331. 验证二叉树的前序序列化    中等

序列化二叉树的一种方法是使用前序遍历。当我们遇到一个非空节点时,我们可以记录下这个节点的值。如果它是一个空节点,我们可以使用一个标记值记录,例如 #

     _9_
    /   \
   3     2
  / \   / \
 4   1  #  6
/ \ / \   / \
# # # #   # #

例如,上面的二叉树可以被序列化为字符串 "9,3,4,#,#,1,#,#,2,#,6,#,#",其中 # 代表一个空节点。

给定一串以逗号分隔的序列,验证它是否是正确的二叉树的前序序列化。编写一个在不重构树的条件下的可行算法。

每个以逗号分隔的字符或为一个整数或为一个表示 null 指针的 '#'

你可以认为输入格式总是有效的,例如它永远不会包含两个连续的逗号,比如 "1,,3"

示例 1:

输入: "9,3,4,#,#,1,#,#,2,#,6,#,#"
输出: true

示例 2:

输入: "1,#"
输出: false

示例 3:

输入: "9,#,#,1"
输出: false
    //未使用栈,使用槽位
    public boolean isValidSerialization(String preorder) {
        int slot = 1;
        for (String s : preorder.split(",")) {
            slot--;
            if (slot < 0) {
                return false;
            }
            if (!s.equals("#")) {
                slot += 2;
            }
        }
        return slot == 0;
    }

解析:以将所有的槽位消耗完,那么这个前序序列化就是合法的。

开始时只有一个可用槽位。

空节点和非空节点都消耗一个槽位。

空节点不增加槽位,非空节点增加两个槽位。

    //未使用栈,使用槽位
    public boolean isValidSerialization(String preorder) {
        int plot = 1;
        int n = preorder.length();
        for (int i = 0; i < n; i++) {
            if (preorder.charAt(i) == ',') {
                plot--;
                if (plot < 0) {
                    return false;
                }
                if (preorder.charAt(i - 1) != '#') {
                    plot += 2;
                }
            }
        }
        if (preorder.charAt(n - 1) == '#') {
            plot--;
        } else {
            plot++;
        }
        return plot == 0;
    }

解析:相比于上个方法,少开辟空间

*394. 字符串解码    中等

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

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

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

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

示例 1:

输入:s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:

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

示例 3:

输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"

示例 4:

输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"
class Solution {
    Stack<String> stack = new Stack<>();
    int idx;

    public String decodeString(String s) {
        while (idx < s.length()) {
            char ch = s.charAt(idx);
            if (Character.isDigit(ch)) {
                String digits = getDigit(s);
                stack.add(digits);
            } else if (ch == '[' || Character.isLetter(ch)) {
                stack.add(String.valueOf(s.charAt(idx++)));
            } else {
                idx++;
                List<String> list = new LinkedList<>();
                while (!"[".equals(stack.peek())) {
                    list.add(stack.pop());
                }
                Collections.reverse(list);
                stack.pop();
                int num = Integer.parseInt(stack.pop());
                StringBuilder t = new StringBuilder();
                String temp = getString(list);
                for (int i = 0; i < num; i++) {
                    t.append(temp);
                }
                stack.add(t.toString());
            }
        }
        return getString(stack);
    }

    private String getDigit(String s) {
        StringBuilder builder = new StringBuilder();
        while (Character.isDigit(s.charAt(idx))) {
            builder.append(s.charAt(idx++));
        }
        return builder.toString();
    }

    private String getString(List<String> list) {
        StringBuilder builder = new StringBuilder();
        for (String s : list) {
            builder.append(s);
        }
        return builder.toString();
    }

    private String getString(Stack<String> stack) {
        StringBuilder builder = new StringBuilder();
        for (String s : stack) {
            builder.append(s);
        }
        return builder.toString();
    }
}

解析:暂时不熟悉

猜你喜欢

转载自blog.csdn.net/qq_43204550/article/details/108613631