栈
1 Linux路径处理
原题:71. 简化路径 - 力扣(LeetCode)
思路:
1.使用栈来存储路径
2.遍历路径,遇到**…则弹出栈顶元素,遇到.**或者空字符串则跳过,其他情况则入栈
3.最后将栈中元素用**/*连接起来
public String simplifyPath(String path) {
//将path用"\"切割
String[] sp = path.split("/");
Deque<String> stack = new ArrayDeque<>();
for(String str : sp){
if (str.equals("..")){ //当前字符串为".." 若栈不为空则栈顶出栈一个元素
if(!stack.isEmpty())stack.pop();
}else if(str.equals(".") || str.equals("")){ //若当前字符为"." 或是""空字符串(切割可能产生空字符串) 则跳过
continue;
}else{ //入栈
stack.push(str);
}
}
//用栈底开始出栈,拼接到sb中
StringBuilder sb = new StringBuilder();
while(!stack.isEmpty()){
sb.append("/").append(stack.pollLast());
}
return sb.length() == 0 ? "/" : sb.toString();
}
2 小行星碰撞
735. 小行星碰撞 - 力扣(LeetCode)
public int[] asteroidCollision(int[] asteroids) {
ArrayDeque<Integer> stack = new ArrayDeque<>();
int n = asteroids.length;
for(int i = 0; i < n; i ++){
int cur = asteroids[i];
boolean alive = true; //表示cur是否存在,默认为true
//栈不为空,且cur与栈顶元素方向相反 发生碰撞
while (alive && !stack.isEmpty() && (cur < 0 && stack.peek() > 0)){
int absCur = Math.abs(cur);
int absPeek = Math.abs(stack.peek());
if (absCur < absPeek){//若cur的绝对值< peek,则碰撞后cur不存在, peek仍然存在
alive = false;
}
else if(absCur > absPeek){ //若cur的绝对值 > peek 则碰撞后cur存在,peek不存在
alive = true;
stack.pop();
}else{ //若cur绝对值 == peek 则两者都不存在
alive = false;
stack.pop();
}
}
if (alive){
stack.push(cur);
}
}
int[] ans = new int[stack.size()];
int j = 0;
while (!stack.isEmpty()){
ans[j] = stack.pollLast();
j ++;
}
return ans;
}
3 括号内字符串翻转
1190. 反转每对括号间的子串 - 力扣(LeetCode)
思路:
左括号,则将之前准备好的sb入栈,并重置sb
右括号 将当前sb翻转,并出栈一个字符串插到翻转后的sb最前面
public String reverseParentheses(String s) {
int n = s.length();
StringBuilder sb = new StringBuilder();
Deque<String> stack = new ArrayDeque<>();
for (int i = 0; i < n; i ++){
char c = s.charAt(i);
if (c == '('){ //入栈'('之前的sb,然后重置sb
stack.push(sb.toString());
sb = new StringBuilder();
}else if (c == ')'){//翻转')'之前的sb,并将栈顶元素出栈插到翻转后sb最前面
sb.reverse();
sb.insert(0, stack.pop());
}else{ //普通字符 则加入sb
sb.append(c);
}
}
return sb.toString();
}
4 有效的括号
20. 有效的括号 - 力扣(LeetCode)
思路:遇到左括号则入栈,遇到右括号则出栈(出栈时需进行括号匹配)
public boolean isValid(String s) {
int n = s.length();
Deque<Character> stack = new ArrayDeque<>();
for (int i = 0; i < n; i++){
char c = s.charAt(i);
//当c为左括号 入栈
if (c == '(' || c == '{' || c =='['){
stack.push(c);
}else{//当c为右括号,进行括号匹配
//如果此刻栈为空,则没有左括号与该右括号匹配 返回false
if (stack.isEmpty()) return false;
//先将栈顶元素拿到(不是出栈)
char peek = stack.peek();
//括号匹配方法1,左右括号是同一类型时,匹配才成功
if (peek == '(' && c != ')') return false;
if (peek == '[' && c != ']') return false;
if (peek == '{' && c != '}') return false;
//到这就是匹配成功 出栈
stack.pop();
//括号匹配方法2,左右括号是同一类型时,匹配才成功
if (peek == '(' && c == ')'){
//匹配成功,栈顶元素出栈
stack.pop();
}else if (peek =='{' && c == '}'){
stack.pop();
}else if (peek == '[' && c == ']'){
stack.pop();
}else {
return false;
}
}
}
//最后栈是空 才表明括号匹配全部成功,否则表示有括号匹配失败 返回false
return stack.isEmpty() ? true : false;
}
单调栈
1 移除k位数字,求最小数 (遇到换题)
402. 移掉 K 位数字 - 力扣(LeetCode)
思路:通过单调栈,保证每次新加入的字符都比前面的字符小,遇到大的字符则移除,这样可以形成最小的数。代码通过遍历和判断来移除尽量大的数字,并在必要时移除队列尾部的元素。
public String removeKdigits(String num, int k) {
if (num.length() <= k) return "0";
//使用双端队列来保存可能的最小数字
LinkedList<Character> deque = new LinkedList<>();
int n = num.length();
//遍历num中的每个字符
for (int i = 0; i < n; i++) {
char c = num.charAt(i);
//当队列不空且 k > 0 且队尾字符大于当前字符时,移除队尾字符
while (!deque.isEmpty() && k >0 && deque.peekLast() > c){
deque.pollLast(); //移除队尾元素
k --; //移除的字符数减少一个
}
//将当前字符添加到队列的末尾
deque.offerLast(c);
}
//如果还没够删够k位数字,继续删除队列尾部的字符
for (int i = 0; i < k; i++) {
deque.pollLast();
}
//构建最终结果
StringBuilder sb = new StringBuilder();
boolean leadingZero = true; //是否遇到前导零
while (!deque.isEmpty()){
char digit = deque.pollFirst(); //取出队列中的第一个字符
//如果遇到前导零,则跳过
if (leadingZero && digit == '0'){
continue;
}
leadingZero = false; //一旦遇到非零字符,就不再是前导零
sb.append(digit); //将非前导零字符添加到结果中
}
//如果结果为空,则返回0
return sb.length() == 0 ? "0" : sb.toString();
}