225、难度简单:
方法一:两个队列:时空复杂度:O(n)
要求:实现每种操作的均摊时间复杂度为 O(1) 的栈(换句话说,执行 n 个操作的总时间复杂度 O(n) ,尽管其中某个操作可能需要比其他操作更长的时间。可以使用两个以上的队列)
class MyStack {
Queue<Integer> queue1; // 用于存储栈内的元素
Queue<Integer> queue2; // 作为入栈操作的辅助队列
/** Initialize your data structure here. */
public MyStack() {
queue1 = new LinkedList<Integer>();
queue2 = new LinkedList<Integer>();
}
// 要解决的点是栈从最顶部取元素,而队列从最底部开始取。所以需要让队列逆序数据
// 首先将元素入队到queue2,然后将queue1的全部元素(原先排好栈顺序的)依次出队并入队到queue2
// (由于队列的特点,一队列把自己的全值给另一队列,二者数据顺序相同)
// 此时queue2的前端的元素即为新入栈的元素,再将queue1和queue2互换,
// 则queue1的元素即为栈内的元素,queue1的前端和后端分别对应栈顶和栈底
public void push(int x) {
queue2.offer(x);
while (!queue1.isEmpty()) {
queue2.offer(queue1.poll());
}
Queue<Integer> temp = queue1;
queue1 = queue2;
queue2 = temp;
}
// 队列queue1对元素的入出队列顺序已经和栈的方式相同了,直接弹出来就行
public int pop() {
return queue1.poll();
}
/** Get the top element. */
public int top() {
return queue1.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue1.isEmpty();
}
}
方法二:一个队列:时空复杂度:O(n)
class MyStack {
Queue<Integer> queue;
/** Initialize your data structure here. */
public MyStack() {
queue = new LinkedList<Integer>();
}
// 首先获得入栈前的元素个数 n,然后将元素入队到队列,
// 再将队列中的前 n 个元素(即除了新入栈的元素之外的全部元素)依次出队并入队到队列,
// 此时队列的前端的元素即为新入栈的元素,且队列的前端和后端分别对应栈顶和栈底
public void push(int x) {
int n = queue.size();
queue.offer(x);
for (int i = 0; i < n; i++) {
queue.offer(queue.poll());
}
}
/** Removes the element on top of the stack and returns that element. */
public int pop() {
return queue.poll();
}
/** Get the top element. */
public int top() {
return queue.peek();
}
/** Returns whether the stack is empty. */
public boolean empty() {
return queue.isEmpty();
}
}
394、难度中等:
本题和"20、有效的括号"类似,都需要用栈来解决形如"3[ a 2[ c ] ]、{ [ ] }"的嵌套问题
方法一:栈操作:
原理:本题中可能出现括号嵌套的情况,比如 2[a2[bc]]
,这种情况下我们可以先转化成 2[abcbc]
,在转化成 abcbcabcbc
遍历题传字符串:
1、如果当前的字符为数位,解析出一个数字(可能是连续的多个数位,比如36、100)并进栈
2、如果当前的字符为字母或者左括号,直接进栈
3、如果当前的字符为右括号,开始出栈,一直到左括号出栈,出栈序列反转后拼接成一个字符串,此时取出栈顶的数字(此时栈顶一定是数字,想想为什么?),就是这个字符串应该出现的次数,我们根据这个次数和字符串构造出新的字符串并进栈
class Solution {
int ptr;
public String decodeString(String s) {
LinkedList<String> stk = new LinkedList<String>(); // 作为返回的结果
ptr = 0; // 作为题给字符串的下标
while (ptr < s.length()) {
char cur = s.charAt(ptr);
// 入结果栈数字
if (Character.isDigit(cur)) {
// 获取一个数字并进栈,由于其后可能还存在数字凑成两位、三位数等,所以调用方法getDigits
String digits = getDigits(s);
stk.addLast(digits);
// 入结果栈字母
} else if (Character.isLetter(cur) || cur == '[') {
// 获取一个字母并进栈
stk.addLast(String.valueOf(s.charAt(ptr++)));
// 数字*字符串:既不是数字也不是英文字母那就是"["或"]"
} else {
++ptr;
// 创建新栈(每次被执行到都创建一个新的),用来保存一个[纯英文字母]里的纯英文字母集合
LinkedList<String> sub = new LinkedList<String>();
// 持续弹出栈stk里的元素,一直到遇见"["为止,此时sub栈里有一个完整的[...]里的...
while (!"[".equals(stk.peekLast())) {
sub.addLast(stk.removeLast());
}
// 逆序
Collections.reverse(sub);
// 左括号出栈
stk.removeLast();
// 此时栈顶为当前 sub 对应的字符串应该出现的次数
int repTime = Integer.parseInt(stk.removeLast());
StringBuffer t = new StringBuffer();
// 将[...]里的...字符串化
String o = getString(sub);
// 构造字符串:根据[...]前的数字将...乘倍
while (repTime-- > 0) {
t.append(o);
}
// 将构造好的字符串入结果栈
stk.addLast(t.toString());
}
}
// 调用我们自己写的getString方法,将栈里的所有元素字符串化
return getString(stk);
}
// 返回数字用:
// 遍历题给字符串 s 得到一个字符数字,但该数字是一位的,有可能后面还存在一位(比如"36"、"100")
// 所以需要获取完整的整数
public String getDigits(String s) {
StringBuffer ret = new StringBuffer();
while (Character.isDigit(s.charAt(ptr))) {
ret.append(s.charAt(ptr++));
}
return ret.toString();
}
public String getString(LinkedList<String> v) {
StringBuffer ret = new StringBuffer();
for (String s : v) {
ret.append(s);
}
return ret.toString();
}
}
在看完以上代码后可能会异或代码是如何解决 2[a2[bc]]
格式的:
首先第一轮入栈 stk 循环会一直持续到元素 c 后的 ] 为止,然后我们开始将字符 c、b依次存入另一个栈 sub 中,再逆序为 b、c,乘2后存入到 stk,此时 stk 里的内容为 “2”、"["、“a”、“bcbc”。
进入下一轮循环,遇到 ] 所以执行while (!"[".equals(stk.peekLast())) {,得到的是 bcbc、a 再逆序为 a、bcbc,乘2存入stk中。代码结束返回abcbcabcbc
方法二:递归:涉及到《编译原理》相关内容,详细看下方链接
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/decode-string/solution/zi-fu-chuan-jie-ma-by-leetcode-solution/
class Solution {
String src;
int ptr;
public String decodeString(String s) {
src = s;
ptr = 0;
return getString();
}
public String getString() {
if (ptr == src.length() || src.charAt(ptr) == ']') {
// String -> EPS
return "";
}
char cur = src.charAt(ptr);
int repTime = 1;
String ret = "";
if (Character.isDigit(cur)) {
// String -> Digits [ String ] String
// 解析 Digits
repTime = getDigits();
// 过滤左括号
++ptr;
// 解析 String
String str = getString();
// 过滤右括号
++ptr;
// 构造字符串
while (repTime-- > 0) {
ret += str;
}
} else if (Character.isLetter(cur)) {
// String -> Char String
// 解析 Char
ret = String.valueOf(src.charAt(ptr++));
}
return ret + getString();
}
public int getDigits() {
int ret = 0;
while (ptr < src.length() && Character.isDigit(src.charAt(ptr))) {
ret = ret * 10 + src.charAt(ptr++) - '0';
}
return ret;
}
}