目录
题目一:用栈实现队列
题目描述:请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push
、pop
、peek
、empty
)
思路分析:(来自笨笨在努力)
- 相同的插入顺序,栈与队列的删除顺序正好相反
- 使用两个栈s1,s2, 其中s1是目标栈,要使s1中元素出栈顺序与队列出队顺序一样,s2是辅助栈,借助s2使s1出栈顺序与队列顺序一样;
- 添加元素n时,如果s1是空的,直接入s1
- 否则将s1中的全部元素按出栈顺序插入s2中,直到s1为空,这时,再将n添加进s1,并将s2中的元素依次再添加进s1。
- 上述过程,借助s2,将s1中元素顺序正好颠倒,使颠倒后的出栈顺序与队列顺序一样
代码:
class MyQueue {
// 定义两个栈,s1是目标栈,s2是辅助栈
Stack<Integer> s1;
Stack<Integer> s2;
// 构造
public MyQueue() {
s1 = new Stack<Integer>();
s2 = new Stack<Integer>();
}
// 入队
public void push(int x) {
if (s1.isEmpty()) { // 如果s1为空直接入栈
s1.push(x);
} else { // 否则将s1中元素全部放入s2中
while (!s1.isEmpty()) {
s2.push(s1.pop());
}
s1.push(x);
while (!s2.isEmpty()) { // x入s1后,再将s2中的元素全入s1
s1.push(s2.pop());
}
}
}
// 出队
public int pop() {
return s1.pop();
}
// 返回队首
public int peek() {
return s1.peek();
}
// 判空
public boolean empty() {
return s1.isEmpty();
}
}
题目二:用队列实现栈
题目描述:请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push
、top
、pop
和 empty
)
思路分析:(来自代码随想录)
- 双队列:用两个队列q1和q2实现队列的功能,q2其实完全就是一个备份的作用,把q1最后面的元素以外的元素都备份到q2,然后弹出最后面的元素,再把其他元素从q2导回1
- 单队列:一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时在去弹出元素就是栈的顺序了
解法一:双队列
// 两个队列实现
class MyStack {
// 定义两个队列q1、q2
Queue<Integer> q1;
Queue<Integer> q2;
// 构造
public MyStack() {
q1 = new LinkedList<>();
q2 = new LinkedList<>();
}
// 入栈
public void push(int x) {
q2.offer(x); // 将元素入辅助队列
while (!q1.isEmpty()) {
q2.offer(q1.poll());
}
Queue<Integer> temp = q1;
q1 = q2;
q2 =temp; // 交换元素,入q2的元素进入q1
}
// 出栈
public int pop() {
return q1.poll();
}
// 返回栈顶
public int top() {
return q1.peek();
}
// 判空
public boolean empty() {
return q1.isEmpty();
}
}
解法二:单队列
// 一个队列实现
class MyStack {
// 定义队列q
Queue<Integer> q;
// 构造
public MyStack() {
q = new LinkedList<>();
}
// 入栈
public void push(int x) {
q.offer(x); // 将元素入队列
int n = q.size();
for (int i = 0; i < n - 1; i++) { // 弹出队首元素,并重复加入到队尾
q.offer(q.poll());
}
}
// 出栈
public int pop() {
return q.poll();
}
// 返回栈顶
public int top() {
return q.peek();
}
// 判空
public boolean empty() {
return q.isEmpty();
}
}
题目三:有效的括号
题目描述:给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
思路分析:(来自代码随想录)
第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false
第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false
第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false
那么什么时候说明左括号和右括号全都匹配了呢,就是字符串遍历完之后,栈是空的,就说明全都匹配了。
代码:
class Solution {
public boolean isValid(String s) {
Deque<Character> deque = new LinkedList<>();
char ch;
for (int i = 0; i < s.length(); i++) {
ch = s.charAt(i);
// 碰到左括号就把相对应的右括号入栈,避免出栈还要进行判断
if (ch == '(') {
deque.push(')');
} else if (ch == '{') {
deque.push('}');
} else if (ch == '[') {
deque.push(']');
// 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号多了
// 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符,说明对应的括号错了
} else if (deque.isEmpty() || deque.peek() != ch) {
return false;
} else {
deque.pop(); // 碰到右括号和栈顶元素匹配时,弹出栈顶元素
}
}
// 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明左括号多了
return deque.isEmpty();
}
}
题目四: 删除字符串中的所有相邻重复项
题目描述:给出由小写字母组成的字符串 S
,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
思路分析:
- 栈:利用栈的特性,遍历字符串,先存入字母,当栈顶元素与字符串下一个存入的字母相同是弹出栈顶元素
- 双指针:用fast指针覆盖slow指针的值,遇到相同值,slow指针退一步,fast指针每次循环都前进一步,最后(0,slow)区间内的字母即为最终结果
解法一:栈
class Solution {
public String removeDuplicates(String s) {
ArrayDeque<Character> deque = new ArrayDeque<>();
char ch;
for (int i = 0; i < s.length(); i++) {
ch = s.charAt(i);
if (deque.isEmpty() || deque.peek() != ch) {
deque.push(ch); // 栈顶元素重复时会被弹出
} else {
deque.pop(); // 栈顶元素不重复时加入栈
}
}
String str = "";
while (!deque.isEmpty()) {
str = deque.pop() + str; // 将剩余元素弹出
}
return str;
}
}
解法二:双指针
class Solution {
public String removeDuplicates(String s) {
char[] ch = s.toCharArray();
int fast = 0;
int slow = 0;
while (fast < s.length()) {
// 直接用fast指针覆盖slow指针的值
ch[slow] = ch[fast];
// 遇到前后相同值的,就跳过,即slow指针后退一步,下次循环就可以直接被覆盖掉了
if(slow > 0 && ch[slow] == ch[slow - 1]){
slow--;
} else {
slow++;
}
fast++;
}
return new String(ch, 0, slow);
}
}