栈和队列
文章目录
一、栈
1、简单的栈运用
20. 有效的括号
给定一个字符串,只包含(,[,{,),],}, 判定字符串中的括号匹配是否合法。
- 如"()”,“()[{"是合法的
- 如“(]","([)]" 是非法的
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for(int i = 0;i < s.length();i++) {
char t = s.charAt(i);
if(t == '[' || t == '{' || t == '(' ) {
stack.push(t);
}
else {
if(stack.isEmpty()) return false;
if(t == ')') {
if(stack.isEmpty()) return false;
if(stack.peek() == '(') stack.pop();
else return false;
}
if(t == '}') {
if(stack.peek() == '{') stack.pop();
else return false;
}
if(t == ']') {
if(stack.peek() == '[') stack.pop();
else return false;
}
}
}
if(!stack.isEmpty()) return false;
return true;
}
}
拓展:
Evaluate Reverse Polish Notation
Simplify Path
2、栈和递归的紧密关系
2.1、递归算法
二叉树中的算法
前序、中序和后序遍历
- Binary Tree Preorder Traversal
-
Binary Tree Inorder Traversal
-
Binary Tree Postorder Traversal
2.2、使用栈模拟系统栈,写出非递归程序
LeetCode 144. 二叉树前序遍历
public class Solution144 {
// Definition for a binary tree node.
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
private class Command{
String s; // go, print
TreeNode node;
Command(String s, TreeNode node){
this.s = s;
this.node = node;
}
};
public List<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<Integer>();
if(root == null)
return res;
Stack<Command> stack = new Stack<Command>();
stack.push(new Command("go", root));
while(!stack.empty()){
Command command = stack.pop();
if(command.s.equals("print"))
res.add(command.node.val);
else{
assert command.s.equals("go");
if(command.node.right != null)
stack.push(new Command("go",command.node.right));
if(command.node.left != null)
stack.push(new Command("go",command.node.left));
stack.push(new Command("print", command.node));
}
}
return res;
}
}
public class Solution1 {
public List<Integer> preorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<Integer>();
if(root == null)
return res;
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
while(!stack.empty()){
TreeNode curNode = stack.pop();
res.add(curNode.val);
if(curNode.right != null)
stack.push(curNode.right);
if(curNode.left != null)
stack.push(curNode.left);
}
return res;
}
}
LeetCode 94.二叉树中序遍历
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/// 94. Binary Tree Inorder Traversal
/// https://leetcode.com/problems/binary-tree-inorder-traversal/solution/
/// 非递归二叉树的中序遍历
/// 时间复杂度: O(n), n为树的节点个数
/// 空间复杂度: O(h), h为树的高度
public class Solution094 {
// Definition for a binary tree node.
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
private class Command{
String s; // go, print
TreeNode node;
Command(String s, TreeNode node){
this.s = s;
this.node = node;
}
};
public List<Integer> inorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<Integer>();
if(root == null)
return res;
Stack<Command> stack = new Stack<Command>();
stack.push(new Command("go", root));
while(!stack.empty()){
Command command = stack.pop();
if(command.s.equals("print"))
res.add(command.node.val);
else{
assert command.s.equals("go");
if(command.node.right != null)
stack.push(new Command("go",command.node.right));
stack.push(new Command("print", command.node));
if(command.node.left != null)
stack.push(new Command("go",command.node.left));
}
}
return res;
}
}
/// Source : https://leetcode.com/problems/binary-tree-inorder-traversal/solution/
/// Author : liuyubobobo
/// Time : 2018-05-30
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
// Classic Non-Recursive algorithm for inorder traversal
// Time Complexity: O(n), n is the node number in the tree
// Space Complexity: O(h), h is the height of the tree
public class Solution1 {
public List<Integer> inorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<Integer>();
if(root == null)
return res;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.empty()){
while(cur != null){
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
res.add(cur.val);
cur = cur.right;
}
return res;
}
}
LeetCode 145.二叉树后序遍历
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/// 145. Binary Tree Postorder Traversal
/// https://leetcode.com/problems/binary-tree-postorder-traversal/description/
/// 非递归的二叉树的后序遍历
/// 时间复杂度: O(n), n为树的节点个数
/// 空间复杂度: O(h), h为树的高度
public class Solution145 {
// Definition for a binary tree node.
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
private class Command{
String s; // go, print
TreeNode node;
Command(String s, TreeNode node){
this.s = s;
this.node = node;
}
};
public List<Integer> postorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<Integer>();
if(root == null)
return res;
Stack<Command> stack = new Stack<Command>();
stack.push(new Command("go", root));
while(!stack.empty()){
Command command = stack.pop();
if(command.s.equals("print"))
res.add(command.node.val);
else{
assert command.s.equals("go");
stack.push(new Command("print", command.node));
if(command.node.right != null)
stack.push(new Command("go",command.node.right));
if(command.node.left != null)
stack.push(new Command("go",command.node.left));
}
}
return res;
}
}
/// Source : https://leetcode.com/problems/binary-tree-postorder-traversal/description/
/// Author : liuyubobobo
/// Time : 2018-05-30
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
// Non-Recursive
// Using a tag to record whether the node has been visited
//
// Time Complexity: O(n), n is the node number in the tree
// Space Complexity: O(h), h is the height of the tree
public class Solution1 {
private class TagNode{
TreeNode node;
boolean isFirst;
TagNode(TreeNode node){
this.node = node;
this.isFirst = false;
}
};
public List<Integer> postorderTraversal(TreeNode root) {
ArrayList<Integer> res = new ArrayList<Integer>();
if(root == null)
return res;
Stack<TagNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.empty()){
while(cur != null){
stack.push(new TagNode(cur));
cur = cur.left;
}
TagNode tagNode = stack.pop();
cur = tagNode.node;
if(tagNode.isFirst == false){
tagNode.isFirst = true;
stack.push(tagNode);
cur = cur.right;
}
else{
res.add(cur.val);
cur = null;
}
}
return res;
}
}
- Flatten Nested List Iterator
二、队列
队列的基本应用
- 广度优先遍历
- 树;层序遍历
- 图;无权图的最短路径
1、广度优先遍历
LeetCode 102. 二叉树层序遍历
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>(){{add(root);}};
ArrayList<List<Integer>> res = new ArrayList<>();
if(root == null) return res;
while(!q.isEmpty()){
ArrayList<Integer> tmp = new ArrayList<>();
for(int i= q.size();i>0;i--){// 打印这一层
TreeNode node = q.poll();
tmp.add(node.val);
if(node.left != null) q.add(node.left);
if(node.right != null) q.add(node.right);
}
res.add(tmp);
}
return res;
}
}
- Binary Tree Right Side View
2、BFS和图的最短路径
LeetCode 279. 完全平方数
class Solution {
public int numSquares(int n) {
Queue<Pair<Integer,Integer>> q = new LinkedList<>();
q.add(new Pair(n,0));
boolean[] visited = new boolean[n + 1];
visited[n] = true;
while(!q.isEmpty()) {
Pair<Integer,Integer> front = q.poll();
int num = front.getKey();
int step = front.getValue();
if(num == 0) return step;
for(int i = 0;;i++){
int a = num - i * i;
if(a < 0 ) break;
if(!visited[a]) {
q.add(new Pair(a, step + 1));
visited[a] = true;
}
}
}
return 0;
}
}
[源码](https://github.com/liuyubobobo/Play-with-Algorithm-Interview/tree/master/06-Stack-and-Queue/Course Code (Java)/05-Perfect-Squares/src)
拓展
Word Ladder
Word Ladder II
3、优先队列
底层实现——堆
对于堆的底层实现,白板编程
Queue<Integer> queue = new PriorityQueue<>((v1, v2) -> v2 - v1);// 大顶堆
Queue<Integer> queue = new PriorityQueue<>()// 小顶堆
LeetCode 347. 前 K 个高频元素
最简单的思路:扫描一-遍统计频率;排序找到前k个出现频率最高的元素。O(nlogn)
维护一个含有k个元素的优先队列。如果遍历到的元素比队列中的最小频率元素的频率高,则取出队列中最小频率的元素,将新元素入队。最终,队列中剩下的,就是前k个出现频率最高的元素。
class Solution {
public int[] topKFrequent(int[] nums, int k) {
HashMap<Integer,Integer> map = new HashMap<>();
for(int num:nums) {
if(!map.containsKey(num)) map.put(num,1);
else map.put(num,map.get(num) + 1);
}
// 优先队列的定义
PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return map.get(a) - map.get(b);
}
});// 关键比较器
for(Integer key : map.keySet()) {
if(pq.size() == k) {
if(map.get(pq.peek()) < map.get(key)) {
pq.poll();
pq.add(key);
}
}
else pq.add(key);
}
int[] res = new int[k];
int i = 0;
for(Integer item:pq) {
res[i++] = item;
}
return res;
}
}
思路3:维护优先队列,时间复杂度: O(nlog(n-k))
LeetCode 23. Merge k Sorted Lists
4、单调队列
单调队列,顾名思义,就是一个元素单调的队列,那么就能保证队首的元素是最小(最大)的,从而满足动态规划的最优性问题的需求。
单调队列,又名双端队列。双端队列,就是说它不同于一般的队列只能在队首删除、队尾插入,它能够在队首、队尾同时进行删除。
剑指 Offer 59 - I. 滑动窗口的最大值
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length == 0 || k == 0) return new int[0];
int[] res = new int[nums.length - k + 1];
LinkedList<Integer> help = new LinkedList<>();
// 形成窗口之前
for(int i = 0;i < k;i++) {
while(!help.isEmpty() && nums[i] > help.getLast()) help.removeLast();
help.addLast(nums[i]);
}
res[0] = help.getFirst();
for(int i = k;i < nums.length;i++) {
if(nums[i - k] == help.getFirst()) help.removeFirst();
while(!help.isEmpty() && nums[i] > help.getLast()) help.removeLast();
help.addLast(nums[i]);
res[i - k + 1] = help.getFirst();
}
return res;
}
}
剑指 Offer 59 - II. 队列的最大值
class MaxQueue {
Queue<Integer> q;
LinkedList<Integer> help;
public MaxQueue() {
q = new LinkedList<>();
help = new LinkedList<>();
}
public int max_value() {
return q.size()==0?-1:help.getFirst();
}
public void push_back(int value) {
q.offer(value);
while(!help.isEmpty() && help.getLast() < value) help.removeLast();
help.add(value);
}
public int pop_front() {
if(q.size() == 0) return -1;
if((int)q.peek() == help.getFirst()) help.removeFirst();
return q.poll();
}
}
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue obj = new MaxQueue();
* int param_1 = obj.max_value();
* obj.push_back(value);
* int param_3 = obj.pop_front();
*/