栈的基础使用
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 注意空字符串可被认为是有效字符串。
输入: "()" --- 输出: true
输入: "()[]{}"---输出: true
输入: "(]"---输出: false
利用栈,遇到左括号加入栈,遇到右括号就看是否和栈顶元素匹配,如果匹配将栈顶元素出栈,如果不匹配返回false
import java.util.Stack;
class Solution {
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
char[] str = s.toCharArray();
for (int i = 0; i < str.length; i++) {
if (str[i] == '(' || str[i] == '{' || str[i] == '[') {
stack.push(str[i]);
} else {
if (stack.size() == 0) {
return false;
} else {
char c = stack.peek();
char match = ' ';
if (str[i] == ')') {
match = ')';
} else if (str[i] == ']') {
match = ']';
} else if (str[i] == '}') {
match = '}';
}
if (c != match) {
return false;
}
}
}
}
if (stack.size() != 0) {
return false;
} else {
return true;
}
}
}
相关问题150,71
class Solution {
// 当遇到运算符"+"、"-"、"*"、"/"时,从栈中pop出两个数字计算,否则将数字入栈;
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack();
for (String s : tokens) {
if (s.equals("+")) {
stack.push(stack.pop() + stack.pop());
} else if (s.equals("-")) {
stack.push(-stack.pop() + stack.pop());
} else if (s.equals("*")) {
stack.push(stack.pop() * stack.pop());
} else if (s.equals("/")) {
int num1 = stack.pop();
stack.push(stack.pop() / num1);
} else {
stack.push(Integer.valueOf(s));
}
}
return stack.pop();
}
}
栈与递归的联系
比如遍历二叉树
当递归调用方法时,此时要向栈里推入一些内容,如上一个函数具体执行到了哪里,函数返回的时候取出栈顶元素就知道之后要执行的
- 下面使用非递归方式
/**
* Definition for a binary tree node. public class TreeNode { int val; TreeNode
* left; TreeNode right; TreeNode(int x) { val = x; } }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
LinkedList<TreeNode> stack = new LinkedList<>();
LinkedList<Integer> output = new LinkedList<>();
if (root == null) {
return output;
}
stack.add(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pollLast();
output.add(node.val);
if (node.right != null) {
stack.add(node.right);
}
if (node.left != null) {
stack.add(node.left);
}
}
return output;
}
}
相关问题341
public class NestedIterator implements Iterator<Integer> {
Queue<Integer> queue = new LinkedList<>();
public NestedIterator(List<NestedInteger> nestedList) {
getInteger(nestedList);
}
private void getInteger(List<NestedInteger> nestedList) {
for(NestedInteger nest: nestedList){
if(nest.isInteger()){
queue.offer(nest.getInteger());
}else{
getInteger(nest.getList());
}
}
}
@Override
public Integer next() {
return queue.poll();
}
@Override
public boolean hasNext() {
return !queue.isEmpty();
}
}
队列
队列的基本应用-广度优先遍历
- 树;层序遍历
- 图;无权图的最短路径
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> levels = new ArrayList<List<Integer>>();
if (root == null) return levels;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
int level = 0;
while ( !queue.isEmpty() ) {
// start the current level
levels.add(new ArrayList<Integer>());
// number of elements in the current level
int level_length = queue.size();
for(int i = 0; i < level_length; ++i) {
TreeNode node = queue.remove();
// fulfill the current level
levels.get(level).add(node.val);
// add child nodes of the current level
// in the queue for the next level
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
// go to next level
level++;
}
return levels;
}
}
相关问题,107,103,199
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
DFS(root, 0, res);
return res;
}
private void DFS(TreeNode root, int level, List<List<Integer>> res) {
if (root == null) {
return;
}
if (res.size() <= level) {
res.add(0, new ArrayList<>());
}
res.get(res.size() - 1 - level).add(root.val);
DFS(root.left, level + 1, res);
DFS(root.right, level + 1, res);
}
}
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
DFS(root, 0, res);
return res;
}
// 使用dfs,对应层判断一下奇偶,决定在表头还是表尾添加元素
private void DFS(TreeNode root, int depth, List<List<Integer>> res) {
if (root == null) {
return;
}
if (res.size() == depth) {
res.add(new ArrayList<Integer>());
}
if (depth % 2 == 0) {
res.get(depth).add(root.val);
} else {
res.get(depth).add(0, root.val);
}
DFS(root.left, depth+1, res);
DFS(root.right, depth + 1, res);
}
}
class Solution {
public List<Integer> rightSideView(TreeNode root) {
if (root == null) {
return Collections.emptyList();
}
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
List<Integer> list = new ArrayList<>();
TreeNode node;
while (queue.size() > 0) {
int size = queue.size();
while (size-- > 0) {
node = queue.remove();
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
if (size == 0) {
list.add(node.val);
}
}
}
return list;
}
}
BFS和图的最短路径
给定正整数 n,找到若干个完全平方数(完全平方数就是一个整数是平方,比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
对于任何一个正整数,他一定能表示为n个完全平方数的和(因为1是一个完全平方数,最少可以表示为n个1相加)
- 贪心是想法:看能先填入一个最大完全平方数,之后再看剩下的数字还能不能填下其他的完全平方数
但是这个问题贪心算法不成立,比如12=9+1+1+1,但是最少的组合是12=4+4+4 -
对问题建模:
整个问题转化为一个图论问题,从n到0,每个数字表示一个节点,如果两个数字x到y相差一个完全平方数,则连接一条边,得到了一个无权图,问题转化成求这个无权图中从n到0的最短路径
class Solution {
static class Node {
int val;//表示数字是多少
int step;//已经走了几步
public Node(int val, int step) {
this.val = val;
this.step = step;
}
}
// 该算法在往队列里面添加节点的时候会 add 很多重复的节点,导致超时
/*public int numSquares(int n) {
Queue<Node> queue = new LinkedList<>();
queue.add(new Node(n, 0));
//广度优先遍历
while (!queue.isEmpty()) {
int num = queue.peek().val;//取出队首元素
int step = queue.peek().step;//已经走了几步
queue.remove();//出队
if(num==0){
return step;
}
for(int i=1;num-i*i>=0;i++){
queue.add(new Node(num-i*i, step + 1));
}
}
return -1;
}*/
// 优化办法是,加入 visited 数组,检查要 add 的数据是否已经出现过了,防止数据重复出现,从而影响图的遍历
// 同时优化:num - i * i 表达式,只让他计算一次
// 同时在循环体里面判断退出或返回的条件,而不是在循环体外
public int numSquares(int n) {
Queue<Node> queue = new LinkedList<>();
queue.add(new Node(n, 0));
// 其实一个真正的图的 BSF 是一定会加上 visited 数组来过滤元素的
boolean[] visited = new boolean[n+1];
while (!queue.isEmpty()) {
int num = queue.peek().val;
int step = queue.peek().step;
queue.remove();
for (int i = 1; ; i++) {
int a = num - i * i;
if (a < 0) {
break;
}
// 若 a 已经计算到 0 了,就不必再往下执行了
if (a == 0) {
return step + 1;
}
if (!visited[a]) {//还没有访问过
queue.add(new Node(a, step + 1));
visited[a] = true;
}
}
}
return -1;
}
}
相关问题127,126
优先队列
底层实现,堆
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
- 最简单的思路:扫描一遍,统计频率,排序找到前k个出现频率最高的元素。O(nlogn)
- 维护一个含有k个元素的优先队列,如果遍历到的元素比队列中的最小频率元素的频率高,则取出队列中最小频率的元素,将新元素入队,最终队列剩下的就是前k个出现频率最高的元素,O(nlogk)
class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
//统计每个元素出现的频率
HashMap<Integer, Integer> count = new HashMap();
for (int n: nums) {
count.put(n, count.getOrDefault(n, 0) + 1);
}
//遍历map,用最小堆维护当前出现频率最高的k个元素
PriorityQueue<Integer> heap =
new PriorityQueue<Integer>((n1, n2) -> count.get(n1) - count.get(n2));
for (int n: count.keySet()) {
heap.add(n);
if (heap.size() > k)
heap.poll();
}
List<Integer> top_k = new LinkedList();
while (!heap.isEmpty())
top_k.add(heap.poll());
Collections.reverse(top_k);
return top_k;
}
}
相关问题23,