目录
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) 内存,其中 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();
}
}
解析:暂时不熟悉