LeetCode - 5. 栈与队列
刷题顺序来自:代码随想录
目录
用栈实现队列
232. 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty)。
实现 MyQueue 类:
void push(int x)将元素 x 推到队列的末尾int pop()从队列的开头移除并返回元素int peek()返回队列开头的元素boolean empty()如果队列为空,返回true;否则,返回 `false
class MyQueue {
Stack<Integer> pushStack = new Stack<>();
Stack<Integer> popStack = new Stack<>();
public MyQueue() {
}
public void push(int x) {
pushStack.push(x);
}
public int pop() {
if(popStack.empty()) {
move(pushStack, popStack);
}
return popStack.pop();
}
public int peek() {
if(popStack.empty()) {
move(pushStack, popStack);
}
return popStack.peek();
}
public boolean empty() {
return pushStack.empty() && popStack.empty();
}
// 将栈1的元素都移动到栈2
private void move(Stack<Integer> s1, Stack<Integer> s2) {
while(!s1.empty()) {
int element = s1.pop();
s2.push(element);
}
}
}
225. 用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
可以仅用一个队列实现。
class MyStack {
// Queue是接口,需要用其实现类实例化
Queue<Integer> queue = new LinkedList<Integer>();
public MyStack() {
}
public void push(int x) {
queue.offer(x);
}
public int pop() {
move();
return queue.poll();
}
public int top() {
move();
int top = queue.peek();
queue.offer(queue.poll());
return top;
}
public boolean empty() {
return queue.size() == 0;
}
// 取出队列的前n-1个元素,放入队尾
// 此时队列头元素为原来的最后一个元素
private void move() {
for(int i = 0; i < queue.size() - 1; i++) {
queue.offer(queue.poll());
}
}
}
有效的括号
20. 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
示例 1:
输入:s = "{[]}"
输出:true
示例 2:
输入:s = "([)]"
输出:false
使用栈匹配。
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for(int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (ch == '(' || ch == '[' || ch == '{') { // 左括号入栈
stack.push(ch);
}
else { // 右括号出栈,并查看弹出的左括号是否与当前右括号匹配
if (stack.empty()) {
return false;
}
char pop = stack.pop();
if((ch == ')' && pop != '(') || (ch == ']' && pop != '[') || (ch == '}' && pop != '{')) {
return false;
}
}
}
return stack.empty();
}
删除重复相邻项
1047. 删除字符串中的所有相邻重复项
给出由小写字母组成的字符串 s,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
示例 1:
输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
当前字符和栈顶字符不相同,当前字符入栈,否则栈顶字符出栈。
public String removeDuplicates(String s) {
LinkedList<Character> list = new LinkedList<>();
for(int i = 0; i < s.length(); i++) {
char curr = s.charAt(i);
if(list.size() == 0 || list.peekLast() != curr) {
list.offerLast(curr);
}
else {
list.pollLast();
}
}
StringBuilder res = new StringBuilder();
while(list.size() != 0) {
res.append(list.pollFirst());
}
return res.toString();
}
逆波兰表达式求值
150. 逆波兰表达式求值
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
- 整数除法只保留整数部分。
- 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入:s = "{[]}"
输出:true
示例 2:
输入:s = "{[]}"
输出:true
示例 3:
输入:s = "{[]}"
输出:true
public int evalRPN(String[] tokens) {
Stack<Integer> nums = new Stack<>();
for(String s: tokens) {
if (s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) {
int num1 = nums.pop();
int num2 = nums.pop();
nums.push(calculate(num2, num1, s));
}
else {
nums.push(Integer.parseInt(s));
}
}
return nums.pop();
}
// int a b 的计算值
public int calculate(int a, int b, String op) {
if (op.equals("+")) {
return a + b;
}
else if (op.equals("-")) {
return a - b;
}
else if (op.equals("*")) {
return a * b;
}
else if (op.equals("/")) {
return a / b;
}
return 0;
}
滑动窗口最大值
239. 滑动窗口最大值
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1
输出:[1]
示例 3
输入:nums = [1,-1], k = 1
输出:[1,-1]
维护一个有序的Deque,队列中元素值递减,每次滑动窗口后,移除旧元素,加入新元素,队列第一个元素就是最大值。然而实际上不需要维护窗口中的所有元素。
在每次将新元素添加到队列尾部时,需要移除队列尾部值比新元素更小的元素。这是为了保证每次添加的新元素是队列中的最小值,我们可以永久移除队列中比新元素值小的老元素的原因是:
- 新元素的索引是队列中最大的,队列中有元素比新元素的值还要小时,它永远都不可能成为最大值,因为它会被新元素更早被滑出窗口。
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length <= 1 || k <= 1) return nums;
int[] res = new int[nums.length - k + 1];
LinkedList<Integer> list = new LinkedList<>();
for(int i = 0; i < k; i++) {
// 添加新元素到队列尾部
// 需要保证队列所有元素都是不小于新元素的
while(list.size() != 0 && list.getLast() < nums[i]) {
list.removeLast();
}
list.add(nums[i]);
}
res[0] = list.getFirst();
for(int i = k; i < nums.length; i++) {
// 滑动窗口右移,元素nums[i-k]被移出窗口,需要判断其是否是最大值
if(list.getFirst() == nums[i-k]){
list.removeFirst();
}
while(list.size() != 0 && list.getLast() < nums[i]) {
list.removeLast();
}
list.add(nums[i]);
res[i-k+1] = list.getFirst();
}
return res;
}
前k个高频元素
347. 前 K 个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
public int[] topKFrequent(int[] nums, int k) {
// 通过HashMap统计每个整数出现的频率
HashMap<Integer, Integer> map = new HashMap<>();
for(int i: nums) {
map.put(i, map.getOrDefault(i, 0) + 1);
}
// 将HashMap中的Entry存储到list数组中
Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
ArrayList<Map.Entry<Integer, Integer>> list = new ArrayList<>();
for(Map.Entry<Integer, Integer> entry: entries) {
list.add(entry);
}
// 根据索引进行排序
list.sort(new Comparator<Map.Entry<Integer, Integer>>() {
public int compare(Map.Entry<Integer, Integer> e1, Map.Entry<Integer, Integer> e2) {
return e1.getValue().compareTo(e2.getValue());
}
}
);
// 取出现频率前k高的整数
int[] res = new int[k];
for(int i = 0; i < k; i++) {
res[i] = list.get(list.size() - i - 1).getKey();
}
return res;
}

浙公网安备 33010602011771号