Skip to content

Latest commit

 

History

History
1257 lines (1067 loc) · 31.4 KB

3_栈和队列.md

File metadata and controls

1257 lines (1067 loc) · 31.4 KB

栈和队列

一、栈的典型应用

*1、有效的括号(20)

20. 有效的括号

//思路:
//(1)是左方向的括号,就直接入栈
//(2)右方向的括号,就拿出栈顶元素与之对比,匹配就弹出
public boolean isValid(String s) {
  //注意空字符串可被认为是有效字符串。
  if(s==null || s.length()==0){
    return true;
  }

  Stack<Character> stack = new Stack<>();

  //将 s 转换为字符数组,方便后面对字符的操作
  char[] ss = s.toCharArray();

  for(Character c : ss){
    //如果当前的字符串是左方向的字符串则直接入栈
    if(c == '(' || c =='[' || c=='{'){
      stack.push(c);
    }else{
      //没有左方向的括号且s不为null,显然不匹配的
      if(stack.isEmpty()){
        return false;
      }
      // topc 记录的是栈顶的元素
      char topc = stack.peek();
      if((topc=='(' && c==')') ||
         (topc=='[' && c==']')||
         (topc=='{' && c=='}')) {
        stack.pop();
      }else{
        return false;
      }
    }
  }
  // stack 如果是空的,则完全匹配,返回true
  return stack.isEmpty();
}

@Test
public void test(){
  //String s="()";
  //String s="()[]{}";
  //String s="(]";
  String s="([)]";
  //String s="{[]}";
  System.out.println(isValid(s));
}

*2、逆波兰表达式求值(150)

150. 逆波兰表达式求值

public int evalRPN(String[] tokens) {
  Stack<Integer> stack = new Stack<>();

  for(String token:tokens){
    switch (token){
      case "+": {
        int num1 = stack.pop();
        int num2 = stack.pop();
        stack.push(num2+num1);
        break;
      }
      case "-":{
        int num1 = stack.pop();
        int num2 = stack.pop();
        stack.push(num2-num1);
        break;
      }
      case "*":{
        int num1 = stack.pop();
        int num2 = stack.pop();
        stack.push(num2*num1);
        break;
      }
      case "/":{
        int num1 = stack.pop();
        int num2 = stack.pop();
        stack.push(num2/num1);
        break;
      }
      default:{
        int num = Integer.parseInt(token);
        stack.push(num);
        break;
      }
    }
  }

  return stack.pop();
}

@Test
public void test(){
  //String[] tokens = {"2", "1", "+", "3", "*"};
  //String[] tokens = {"4", "13", "5", "/", "+"};
  String[] tokens = {"10", "6", "9", "3", "+","-11", "*","/", "*","17", "+", "5", "+"};
  System.out.println(evalRPN(tokens));
}

3、简化路径(71)

71. 简化路径

/**
     * 思路:
     * 1、会出现几中字符:
     *  / 
     *  . 
     *  .. 
     * 这三种字符;
     * 2、字符串处理,由于".."是返回上级目录(如果是根目录则不处理),
     * 因此可以考虑用栈记录路径名,以便于处理。
     *
     * 需要注意几个细节:
     * 1、何时出栈?
     * "../ "代表回上一级目录,此时栈顶元素出栈,表示返回上一级
     *
     * 2、何时
     * 如果遇到不是 ".",也不是"",不是"..",那么进栈
     * "" 针对的是 // 这种情况。
     */
public String simplifyPath(String path) {
  if(path==null){
    return "/";
  }

  //根据 / 来切分目录
  String[] dirs = path.split("/");

  Stack<String> stack = new Stack<>();

  for(String dir : dirs){
    if("..".equals(dir)){
      if(!stack.isEmpty()){
        stack.pop();
      }
    }
    if(!".".equals(dir) && !"".equals(dir) && !"..".equals(dir)){
      stack.push(dir);
    }
  }

  return "/"+String.join("/",stack);
}

@Test
public void test(){
  //String path = "/home/";
  //String path = "/../";
  //String path="/home//foo/";
  String path ="/a/../../b/../c//.//";
  System.out.println(simplifyPath(path));
}

4、 行星碰撞(735)

735. 行星碰撞

public int[] asteroidCollision(int[] asteroids) {
    if(asteroids==null || asteroids.length==0){
        return new int[]{};
    }
    if(asteroids.length==1){
        return new int[]{asteroids[0]};
    }

    Stack<Integer> stack = new Stack<>();

    for(int asteroid : asteroids){
        if(stack.isEmpty() || asteroid>0){
            stack.push(asteroid);
            continue; //直接进入下一次循环
        }
        //处理 asteroid < 0 的情况
        while (true){
            int top = stack.peek();
            if(top<0){ //相同方向,则直接入栈
                stack.push(asteroid);
                break;
            }
            // top > 0
            if(top == -asteroid){ //相撞后,栈顶的是要消失的
                stack.pop();
                break;
            }else if(top > -asteroid){ // top 质量更大,则 asteroid 不能入栈
                break;
            }else{
                assert top < -asteroid;
                // 需要出栈
                stack.pop();
                if(stack.isEmpty()){ //栈内的都被碰撞完了,则剩下该行星了
                    stack.push(asteroid);
                    break;
                }
            }
        }
    }

    int[] res = new int[stack.size()];
    //注意:stack 出栈的顺序和数组原来的顺序是相反的
    int index=stack.size()-1;
    while(!stack.isEmpty()){
        res[index--] = stack.pop();
    }
    return res;
}

@Test
public void test(){
    //int[] asteroids = {5, 10, -5};
    //int[] asteroids = {8, -8};
    //int[] asteroids = {10, 2, -5};
    //int[] asteroids = {-2, -1, 1, 2};
    int[] asteroids ={-2,-2,1,-2};
    int[] res = asteroidCollision(asteroids);
    for(int num : res){
        System.out.println(num);
    }
}

二、运用栈模拟递归

1、二叉树的前序遍历(144)

144. 二叉树的前序遍历

//采用枚举类型,GO 表示访问节点,PRINT 表示要打印该节点
private enum Command{Go,Print};

private class StackNode{
    TreeNode node;
    Command command;
    StackNode(TreeNode node,Command command){
        this.node = node;
        this.command = command;
    }
}

public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    Stack<StackNode> stack = new Stack<>();
    stack.push(new StackNode(root,Command.Go));

    while(!stack.isEmpty()){
        StackNode node = stack.pop();
        TreeNode treeNode = node.node;
        Command command = node.command;
        if(command.equals(Command.Print)){
            res.add(treeNode.val);
        }else{
            if(treeNode.right!=null){
                stack.push(new StackNode(treeNode.right,Command.Go));
            }
            if(treeNode.left!=null){
                stack.push(new StackNode(treeNode.left,Command.Go));
            }
            stack.push(new StackNode(treeNode,Command.Print));
        }
    }
    return res;
}

@Test
public void test(){
    int[] pre={4,9,5,1,0};
    int[] in={5,9,1,4,0};
    TreeNode root = TreeNodeUtils.ConstructBinaryTree(pre,in);
    System.out.println(preorderTraversal(root));
}

2、二叉树的中序遍历(94)

private enum Command{Go,Print};

private class StackNode{
    TreeNode node;
    Command command;
    StackNode(TreeNode node,Command command){
        this.node = node;
        this.command = command;
    }
}

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    Stack<StackNode> stack=new Stack<>();
    stack.push(new StackNode(root,Command.Go));

    while(!stack.isEmpty()){
        StackNode node = stack.pop();
        TreeNode treeNode = node.node;
        Command command = node.command;
        if(command.equals(Command.Print)){ //只有遇到打印命令时,才可打印
            res.add(treeNode.val); 
        }else{  
            if(treeNode.right!=null){
                stack.push(new StackNode(treeNode.right,Command.Go));
            }
            stack.push(new StackNode(treeNode,Command.Print));
            if(treeNode.left!=null){
                stack.push(new StackNode(treeNode.left,Command.Go));
            }
        }
    }
    return res;
}

@Test
public void test(){
    int[] pre={4,9,5,1,0};
    int[] in={5,9,1,4,0};
    TreeNode root = TreeNodeUtils.ConstructBinaryTree(pre,in);
    System.out.println(inorderTraversal(root));
}

3、二叉树的后序遍历(145)

private enum Command{Go,Print};

private class StackNode{
    TreeNode node;
    Command command;
    StackNode(TreeNode node,Command command){
        this.node = node;
        this.command = command;
    }
}

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if(root==null){
        return res;
    }

    Stack<StackNode> stack = new Stack<>();
    stack.push(new StackNode(root,Command.Go));

    while(!stack.isEmpty()){
        StackNode node = stack.pop();
        TreeNode treeNode = node.node;
        Command command = node.command;
        if(command.equals(Command.Print)){
            res.add(treeNode.val);
        }else{
            stack.push(new StackNode(treeNode,Command.Print));
            if(treeNode.right!=null){
                stack.push(new StackNode(treeNode.right,Command.Go));
            }
            if(treeNode.left!=null){
                stack.push(new StackNode(treeNode.left,Command.Go));
            }
        }
    }
    return res;
}

@Test
public void test(){
    int[] pre={4,9,5,1,0};
    int[] in={5,9,1,4,0};
    TreeNode root = TreeNodeUtils.ConstructBinaryTree(pre,in);
    System.out.println(postorderTraversal(root));
}

三、队列的典型应用

*1、二叉树的层次遍历(102)

102. 二叉树的层次遍历

import javafx.util.Pair;
//思路:
//1、队列一般用来进行二叉树的层次遍历
//2、这里要注意创建新的 ArrayList 的时机
public List<List<Integer>> levelOrder(TreeNode root) {
  List<List<Integer>> res = new ArrayList<>();
  if(root ==null){
    return res;
  }

  // queue 存储是树的节点及该节点对应的层数
  Queue<Pair<TreeNode,Integer>> queue = new LinkedList<>();

  // root 根节点从 0 层开始,方便后面的操作,因为 List 下标也是从 0 开始的
  queue.add(new Pair<>(root,0));
  res.add(new ArrayList<>(root.val));

  while(!queue.isEmpty()){
    Pair<TreeNode,Integer> pair = queue.poll();
    TreeNode node = pair.getKey();
    Integer level = pair.getValue();

    if(level == res.size()){ // 从第 0 层开始遍历
      //TODO:此时创建新的 ArrayList
      res.add(new ArrayList<>());
    }

    //node.val 是 level 层数据
    res.get(level).add(node.val);

    //对 level+1 层的数据进行处理
    if(node.left != null){
      queue.add(new Pair<>(node.left,level+1));
    }
    if(node.right!= null){
      queue.add(new Pair<>(node.right,level+1));
    }
  }
  return res;
}

@Test
public void test(){
  int[] pre ={3,9,20,15,7};
  int[] in ={9,3,15,20,7};
  List<List<Integer>> res= levelOrder(
    TreeNodeUtils.ConstructBinaryTree(pre,in));
  for(List<Integer> list : res){
    System.out.println(list);
  }
}

思路二:队列存储的每层次的结点

//思路二:
public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    if(root ==null){
        return res;
    }

    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    int level=0;

    while(!queue.isEmpty()){
        int num = queue.size();
        res.add(new ArrayList<>());
        while(num>0){
            TreeNode node = queue.poll();
            if(node.left!=null){
                queue.add(node.left);
            }
            if(node.right!=null){
                queue.add(node.right);
            }
            res.get(level).add(node.val);
            num--;
        }
        level++;
    }
    return res;
}

@Test
public void test(){
  int[] pre ={3,9,20,15,7};
  int[] in ={9,3,15,20,7};
  List<List<Integer>> res= levelOrder(
    TreeNodeUtils.ConstructBinaryTree(pre,in));
  for(List<Integer> list : res){
    System.out.println(list);
  }
}

2、二叉树的层次遍历 II(107)

107. 二叉树的层次遍历 II

//思路:与 102 题一样
//注意:使用 Collections.revers(res) --> 反向输出 res 结果即可
public List<List<Integer>> levelOrderBottom(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    if(root==null){
        return res;
    }

    // queue 存储是树的节点及该节点对应的层数
    Queue<Pair<TreeNode,Integer>> queue = new LinkedList<>();
    queue.add(new Pair<>(root,0));

    while (!queue.isEmpty()){
        Pair<TreeNode,Integer> pair = queue.poll();
        TreeNode node = pair.getKey();
        int level = pair.getValue();
        if(level==res.size()){
            res.add(new ArrayList<>());
        }
        res.get(level).add(node.val);
        if(node.left!=null){
            queue.add(new Pair<>(node.left,level+1));
        }
        if(node.right!=null){
            queue.add(new Pair<>(node.right,level+1));
        }
    }

    Collections.reverse(res);
    return res;
}

@Test
public void test(){
    int[] pre={3,9,20,15,7};
    int[] in={9,3,15,20,7};
    TreeNode root = TreeNodeUtils.ConstructBinaryTree(pre, in);
    List<List<Integer>> res = levelOrderBottom(root);
    System.out.println(res);
}
//思路二:
//队列只存储每层的节点
public List<List<Integer>> levelOrderBottom(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    if(root==null){
        return res;
    }

    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    int level=0;

    while(!queue.isEmpty()){
        int num = queue.size();
        res.add(new ArrayList<>());
        while(num>0){
            TreeNode node = queue.poll();
            if(node.left!=null){
                queue.add(node.left);
            }
            if(node.right!=null){
                queue.add(node.right);
            }
            res.get(level).add(node.val);
            num--;
        }
        level++;
    }

    Collections.reverse(res);
    return res;
}

@Test
public void test(){
    int[] pre={3,9,20,15,7};
    int[] in={9,3,15,20,7};
    TreeNode root = TreeNodeUtils.ConstructBinaryTree(pre, in);
    List<List<Integer>> res = levelOrderBottom(root);
    System.out.println(res);
}

3、二叉树的锯齿形层次遍历(103)

103. 二叉树的锯齿形层次遍历

public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    if(root==null){
        return res;
    }

    //注意:是每个顶点对应一个层次
    Queue<Pair<TreeNode,Integer>> queue = new LinkedList<>();
    queue.add(new Pair<>(root,0));

    while(!queue.isEmpty()){
        Pair<TreeNode,Integer> pair = queue.poll();
        TreeNode node = pair.getKey();
        int level = pair.getValue();
        if(level == res.size()){
            res.add(new ArrayList<>());
        }
        res.get(level).add(node.val);
        if(node.left!=null){
            queue.add(new Pair<>(node.left,level+1));
        }
        if(node.right!=null){
            queue.add(new Pair<>(node.right,level+1));
        }
    }

    int level = 1;
    for(List<Integer> list : res){
        if(level%2==0){ //偶数层就反转
            Collections.reverse(list);
        }
        level++;
    }

    return res;
}

@Test
public void test(){
    int[] pre={3,9,20,15,7};
    int[] in={9,3,15,20,7};
    TreeNode root = TreeNodeUtils.ConstructBinaryTree(pre, in);
    List<List<Integer>> res = zigzagLevelOrder(root);
    System.out.println(res);
}
//思路二:
//队列只存储每层的节点
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    if(root==null){
        return res;
    }

    //使用 queue 存储的是每层的节点
    Queue<TreeNode> queue=new LinkedList<>();
    queue.add(root);
    int level = 0; //当前层数,为了方便list添加,这里从 0 开始

    while (!queue.isEmpty()){
        int num = queue.size(); //该层的节点数
        res.add(new ArrayList<>());
        while(num>0){
            TreeNode node = queue.poll();
            if(node.left!=null){
                queue.add(node.left);
            }
            if(node.right!=null){
                queue.add(node.right);
            }
            res.get(level).add(node.val);
            num--;
        }
        if(level%2==1){
            Collections.reverse(res.get(level));
        }
        level++;
    }

    return res;
}

@Test
public void test(){
    int[] pre={3,9,20,15,7};
    int[] in={9,3,15,20,7};
    TreeNode root = TreeNodeUtils.ConstructBinaryTree(pre, in);
    List<List<Integer>> res = zigzagLevelOrder(root);
    System.out.println(res);
}

4、二叉树的右视图(199)

199. 二叉树的右视图

//思路一:暴力解法
public List<Integer> rightSideView(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    //存储该二叉树进行层次遍历的结果
    List<List<Integer>> levelOrder = new ArrayList<>();

    Queue<Pair<TreeNode,Integer>> queue = new LinkedList<>();
    queue.add(new Pair<>(root,0));

    while(!queue.isEmpty()){
        Pair<TreeNode,Integer> pair = queue.poll();
        TreeNode node = pair.getKey();
        int level = pair.getValue();
        if(level == levelOrder.size()){
            levelOrder.add(new ArrayList<>());
        }
        levelOrder.get(level).add(node.val);
        if(node.left!=null){
            queue.add(new Pair<>(node.left,level+1));
        }
        if(node.right!=null){
            queue.add(new Pair<>(node.right,level+1));
        }
    }

    System.out.println(levelOrder);
    for(List<Integer> list : levelOrder){
        int lastIndex = list.size()-1;
        res.add(list.get(lastIndex));
    }
    return res;
}

@Test
public void test(){
    int[] pre={1,2,5,3,4};
    int[] in={2,5,1,3,4};
    TreeNode root = TreeNodeUtils.ConstructBinaryTree(pre,in);
    System.out.println(rightSideView(root));
}
//思路二:
//队列只存储每层的节点
public List<Integer> rightSideView(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if(root==null){
        return res;
    }

    //使用 queue 存储某一层的节点
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(root); //第一层只有一个 root 结点

    while(!queue.isEmpty()){
        int num = queue.size();//该层的节点数
        while(num>0){ //遍历节点
            TreeNode node = queue.poll();
            if(node.left!=null){
                queue.add(node.left);
            }
            if(node.right!=null){
                queue.add(node.right);
            }
            num--;
            if(num==0){ //遍历到最后一个元素,其实就是右边的节点
                res.add(node.val);
            }
        }
        //经过循环后,queue 此时该层的所有的节点出队,存储的是下一层的节点
    }
    return res;
}

@Test
public void test(){
    int[] pre={1,2,5,3,4};
    int[] in={2,5,1,3,4};
    TreeNode root = TreeNodeUtils.ConstructBinaryTree(pre,in);
    System.out.println(rightSideView(root));
}

四、BFS 和队列

*1、完全平方数(279)

279. 完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。

示例 1:

输入: n = 12
输出: 3 
解释: 12 = 4 + 4 + 4.

示例 2:

输入: n = 13
输出: 2
解释: 13 = 4 + 9.

// 思路:
// 1、贪心算法?
// 使用贪心策略:每次取最大的完全平方数,则12 = 9+1+1+1,但是12 = 4+4+4是最终解。
// 2、对问题建模:将整个问题转化为图论问题。
// 从 n 到 0,每个数字表示一个节点;
// 如果两个数字 x 到 y 相差一个完全平方数,则连接一条边。我们就得到了整个无权图。
// 那么,原来的问题就转化成,求这个无权图中从n到0的最短路径。

  • n = 4 时,对应的无权图。

- n = 5 时,对应的无权图。

  • n = 6 时,对应的无权图。

// 由无权图的性质,最短路径通过 BFS 可以得到。
public int numSquares(int n) {
	Queue<Vertex> queue = new LinkedList<>();
	queue.add(new Vertex(n,0));

	//优化效率: 已经访问过的顶点不能再访问了
	boolean[] visited = new boolean[n+1];
	visited[n] = true;

	while(!queue.isEmpty()){
		Vertex vertex = queue.poll();
		int num = vertex.num;
		int steps = vertex.steps;
		if(num == 0){ //已经到达 0 位置了
			return steps;
		}
		for(int i=1;;i++){
			int tmp = num - i*i;
			if(tmp<0){
				break;
			}
			if(!visited[tmp]){
				queue.add(new Vertex(tmp,steps+1));
				visited[tmp] = true;
			}
		}
	}
	return 0;
}

private class Vertex { //表示该无权图的顶点
	int num;
	int steps;  //记录到 num 的步数。num = n 时,steps=0
	public Vertex(int num,int steps){
		this.num = num;
		this.steps = steps;
	}
}

2、单词接龙(127)

127. 单词接龙

给定两个单词(beginWordendWord)和一个字典,找到从 beginWordendWord 的最短转换序列的长度。转换需遵循如下规则:

  1. 每次转换只能改变一个字母。
  2. 转换过程中的中间单词必须是字典中的单词。

说明:

  • 如果不存在这样的转换序列,返回 0。
  • 所有单词具有相同的长度。
  • 所有单词只由小写字母组成。
  • 字典中不存在重复的单词。
  • 你可以假设 beginWordendWord 是非空的,且二者不相同。

示例 1:

输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

输出: 5

解释: 一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog",
     返回它的长度 5。

示例 2:

输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

输出: 0

解释: endWord "cog" 不在字典中,所以无法进行转换。  

//思路:将单词看成图的顶点
// word1-->word2 的路径就是word1 和 word2 "相似"(word1和wordd2根据题目规则可以相互转化)
// 最后从 beginWord 走到 endWord。示例 1 可表示成如下无权图: 


public int ladderLength(String beginWord, String endWord, List<String> wordList) {
	//优化:提高效率
	Set<String> wordSet = new HashSet<>();
	for(String word : wordList){
		wordSet.add(word);
	}

	//存储已经被访问的 word
	Set<String> visited = new HashSet<>();

	Queue<Vertex> queue = new LinkedList<>();
	queue.add(new Vertex(beginWord,1));

	while(!queue.isEmpty()){
		Vertex vertex = queue.poll();
		String curWord = vertex.word;
		int steps = vertex.steps;
		visited.clear();
		if(endWord.equals(curWord)){
			return steps;
		}
		for(String word : wordSet){
			if(isSimilar(word,curWord)){
				queue.add(new Vertex(word,steps+1));
				visited.add(word);
			}
		}

		for(String w : visited){
			wordSet.remove(w);
		}
	}
	return 0;
}

//判断 word1 和 word2 是否相似,即 word1 和 word2 是否可以相互转化
private boolean isSimilar(String word1,String word2){
	if(word1.length()!=word2.length() || word1.equals(word2)){
		return false;
	}
	int diff=0;
	for(int i=0;i<word1.length();i++){
		if(word1.charAt(i)!=word2.charAt(i)){
			diff++;
		}
		if(diff>1){ //要求 word1 和 word2 中只有一个字符是不同的
			return false;
		}
	}
	return true;
}

private class Vertex{
	String word;
	int steps; //记录到 word 的步数
	public Vertex(String word,int steps){
		this.word = word;
		this.steps = steps;
	}
}

五、优先队列

*1、前 K 个高频元素(347)

347. 前 K 个高频元素

public List<Integer> topKFrequent(int[] nums, int k) {
    List<Integer> res = new ArrayList<>();
    if(k<0 || k>nums.length){
        return res;
    }

    //统计数字出现次数
    Map<Integer,Integer> records = new HashMap<>();
    for(int num : nums){
        int freq = records.getOrDefault(num,0);
        records.put(num,++freq);
    }

    NumFrequent[] numFrequents = new NumFrequent[records.size()];
    int i=0;
    for(int num : records.keySet()){
        int freq = records.get(num);
        numFrequents[i] = new NumFrequent(num,freq);
        i++;
    }

    //默认是小根堆
    PriorityQueue<NumFrequent> pq = new PriorityQueue<>(new Comparator<NumFrequent>() {
        @Override
        public int compare(NumFrequent o1, NumFrequent o2) {
            int num = o1.freq - o2.freq; //先按照频率,进行升序排列
            int num2 = (num==0)? o1.num - o2.num:num;
            return num2;
        }
    });

    for(NumFrequent numFrequent : numFrequents){
        pq.add(numFrequent);
        if(pq.size()>k){
            pq.poll();
        }
    }

    while(!pq.isEmpty()){
        res.add(pq.poll().num);
    }
    return res;
}

private class NumFrequent{
    int num;
    int freq;
    NumFrequent(int num,int freq){
        this.num = num;
        this.freq = freq;
    }
}

2、前K个高频单词(692)

692. 前K个高频单词

public List<String> topKFrequent(String[] words, int k) {
    List<String> res = new ArrayList<>();
    if(k<0 || k>words.length){
        return res;
    }

    //统计数字出现次数
    Map<String,Integer> records = new HashMap<>();
    for(String word : words){
        int freq = records.getOrDefault(word,0);
        records.put(word,++freq);
    }

    WordFrequent[] wordFrequents = new WordFrequent[records.size()];
    int i=0;
    for(String word : records.keySet()){
        int freq = records.get(word);
        wordFrequents[i] = new WordFrequent(word,freq);
        i++;
    }

    //默认是小根堆
    PriorityQueue<WordFrequent> pq = new PriorityQueue<>(new Comparator<WordFrequent>() {
        @Override
        public int compare(WordFrequent o1, WordFrequent o2) {
            int num = o1.freq - o2.freq;
            int num2 = (num==0)?o2.word.compareTo(o1.word):num; //注意这里要逆序,这样后面输出才是正序的
            return num2;
        }
    });

    for(WordFrequent numFrequent : wordFrequents){
        pq.add(numFrequent);
        if(pq.size()>k){
            pq.poll();
        }
    }

    Stack<String> stack = new Stack<>();
    while(!pq.isEmpty()){
        stack.push(pq.poll().word);
    }
    while(!stack.isEmpty()){
        res.add(stack.pop());
    }
    return res;
}

private class WordFrequent{
    String word;
    int freq;
    WordFrequent(String word,int freq){
        this.word = word;
        this.freq = freq;
    }
}

@Test
public void test(){
    //String[] words={"i", "love", "leetcode", "i", "love", "coding"};
    //int k = 2;
    String[] words={"the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"};
    int k = 4;
    System.out.println(topKFrequent(words,k));
}

*六、栈和队列关系

1、用栈实现队列(232)

232. 用栈实现队列

//思路:
//使用两个栈解决
class MyQueue {
    private Stack<Integer> stack1;
    private Stack<Integer> stack2;
    //存储队列头元素
    private int front; //方便后面的 peek() 操作

    /** Initialize your data structure here. */
    public MyQueue() {
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }

    /** Push element x to the back of queue. */
    public void push(int x) {
        if(stack1.isEmpty()){ //此时 x 是第一个元素,方便后面的 peek()操作
            front = x;
        }
        //放入元素,优先放入 stack1 中
        stack1.push(x);
    }

    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
        if(empty()){
           throw new IllegalArgumentException("stack is empty");
        }
        if(stack2.isEmpty()){ 
            //经过若干次 pop 后,stack2 有可能为空,所以需要判空处理
            //stack2 中没有任何元素
            //取出 stack1 中所有元素,都放入 stack2 中
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        return stack2.pop();
    }

    /** Get the front element. */
    public int peek() {
        if(!stack2.isEmpty()){
            return stack2.peek();
        }
        // front 是为了防止 push 后,stack2 中没有元素的情况
        return front;
    }

    /** Returns whether the queue is empty. */
    public boolean empty() { //注意两个栈都为空,才为空 -->
        // 比如不断 push,则 stack1 中有元素,但 stack2 中没有元素
        return (stack1.isEmpty() && stack2.isEmpty());
    }
}

@Test
public void test(){
    MyQueue queue = new MyQueue();

    queue.push(1);
    queue.push(2);
    System.out.println(queue.peek());  // 返回 1
    System.out.println(queue.pop());   // 返回 1
    queue.empty(); // 返回 false
}

2、用队列实现栈(225)

225. 用队列实现栈

class MyStack {
    private Queue<Integer> queue1;
    private Queue<Integer> queue2;

    /** Initialize your data structure here. */
    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }

    /** Push element x onto stack. */
    public void push(int x) {
        queue1.offer(x);
    }

    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
        if(empty()){
            throw new IllegalArgumentException("stack is empty");
        }
        while (queue1.size()>1){
            queue2.offer(queue1.poll());
        }
        int num = queue1.poll();
        //即使出栈后,交换两个队列,始终保持 queue1 队列中存在元素
        Queue<Integer> tmp = queue1;
        queue1 = queue2;
        queue2 = tmp;
        return num;
    }

    /** Get the top element. */
    public int top() {
        while (queue1.size()>1){
            queue2.offer(queue1.poll());
        }
        return queue1.peek();
    }

    /** Returns whether the stack is empty. */
    public boolean empty() {
        return (queue1.isEmpty()) && (queue2.isEmpty());
    }
}

@Test
public void test(){
    MyStack myStack = new MyStack();
    //System.out.println(myStack.empty());
    myStack.push(1);
    myStack.push(2);
    System.out.println(myStack.top());
    System.out.println(myStack.top());
    System.out.println(myStack.top());
    //System.out.println(myStack.empty());
}