Java 普通队列 和 优先级队列 的使用

  不同的问题使用不同的数据结构,往往可达奇效。

普通队列 与 Tree的层次遍历

  LeetCode 107. Binary Tree Level Order Traversal II

Given a binary tree, return the bottom-up level order traversal of its nodes’ values. (ie, from left to right, level by level from leaf to root).

For example:
Given binary tree [3,9,20,null,null,15,7],
这里写图片描述
return its bottom-up level order traversal as:
这里写图片描述

  这里可以采取两种方法:
  1. 使用 先序/后序遍历+深度dep跟踪
  Java代码如下:

public class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        lists = new ArrayList<>();
        if(root == null)
            return lists;
        dep = depth(root);//计算得到树的层数
        for(int i = 0; i<dep; i++){
            List<Integer> t = new ArrayList<>();
            lists.add(t);
        }
        postorder(root, 0);//后序遍历
        return lists;
    }
    private List<List<Integer>> lists;
    private int dep;
    private void postorder(TreeNode r, int d){//后序遍历+深度dep跟踪
        if(r == null)
            return;
        postorder(r.left, d+1);
        postorder(r.right, d+1);
        List<Integer> al = lists.get(dep-1-d);
        al.add(r.val);
        lists.set(dep-1-d,al);
    }
    private int depth(TreeNode r){
        if(r == null)
            return 0;
        return 1 + Math.max(depth(r.left), depth(r.right));
    }
}

  2. 使用层次遍历。
  如果是用数组存的完全二叉树,那么层次遍历就直接遍历一遍数组即可。但是这是一颗普通二叉树。这里我们使用普通队列进行层次遍历。
  初始化:将root入队;
  循环进行层次遍历:每次循环,队列中都只有Tree的一层元素。读取当前队列中各个元素的子节点入队,同时将原先各个元素出队。
  Java代码如下:

public class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        List<List<Integer>> wrapList = new LinkedList<List<Integer>>();

        if(root == null) return wrapList;

        queue.offer(root);//初始化
        while(!queue.isEmpty()){
            int levelNum = queue.size();
            List<Integer> subList = new LinkedList<Integer>();//存储这一层的节点元素
            for(int i=0; i<levelNum; i++) {
                //读取当前队列中各个元素的子节点入队
                if(queue.peek().left != null) queue.offer(queue.peek().left);
                if(queue.peek().right != null) queue.offer(queue.peek().right);
                //同时将原先各个元素出队
                subList.add(queue.poll().val);
            }
            wrapList.add(0, subList);//利用LinkedList的单链表特性,使得最后输出结果为从最下一层往最上层遍历的假象
        }
        return wrapList;
    }
}

优先级队列

  LeetCode 621. Task Scheduler

Given a char array representing tasks CPU need to do. It contains capital letters A to Z where different letters represent different tasks.Tasks could be done without original order. Each task could be done in one interval. For each interval, CPU could finish one task or just be idle.

However, there is a non-negative cooling interval n that means between two same tasks, there must be at least n intervals that CPU are doing different tasks or just be idle.

You need to return the least number of intervals the CPU will take to finish all the given tasks.

Example 1:

Input: tasks = [‘A’,’A’,’A’,’B’,’B’,’B’], n = 2
Output: 8
Explanation: A -> B -> idle -> A -> B -> idle -> A -> B.

Note:
The number of tasks is in the range [1, 10000].
The integer n is in the range [0, 100].

  优先级队列用的最多的是在二叉堆的构造上。
  当需要使用按照一定规则排序队列时,推荐使用优先级队列。本题的一般解法即是如此。
  一种普通思路是:每一个interval内,每一种任务最多执行一次。为了使用最少的interval,可以将任务按所需执行次数进行排序。然后在每个interval内,优先依次执行优先级高的任务种类。每进行一次interval,不同种类任务的优先级可能会发生变化。
  Java代码如下:

public int leastInterval(char[] tasks, int n) {
    Map<Character, Integer> map = new HashMap<>();//key=任务种类,value=执行次数
    for (int i = 0; i < tasks.length; i++) {
        map.put(tasks[i], map.getOrDefault(tasks[i], 0) + 1); 
    }
    PriorityQueue<Map.Entry<Character, Integer>> q = new PriorityQueue<>( //优先级队列在定义时最好指定比较策略,或传入比较器。此处一级比较用value(降序),二级比较用key(升序)
            (a,b) -> a.getValue() != b.getValue() ? b.getValue() - a.getValue() : a.getKey() - b.getKey());

    q.addAll(map.entrySet());//初始化

    int count = 0; //CPU总消耗时间
    while (!q.isEmpty()) {//队列为空,表示任务全部执行完毕,退出循环
        int k = n + 1; //表示interval间隔
        List<Map.Entry> tempList = new ArrayList<>(); //存储此次interval执行的任务序列
        while (k > 0 && !q.isEmpty()) { //当此次interval未结束,或全部任务未执行完毕
            Map.Entry<Character, Integer> top = q.poll(); // 优先级最高(当前所需执行次数最多)的任务
            top.setValue(top.getValue() - 1); // 表示执行过一次。该任务还需执行次数-1
            tempList.add(top); 
            k--; //执行一次任务,用掉一格时间
            count++; //成功执行任务的次数+1,消耗CPU时间格+1
        }

        for (Map.Entry<Character, Integer> e : tempList) {
            if (e.getValue() > 0) q.add(e); // 对于还需执行次数大于1的任务,重归优先级队列,留待下次interval执行
        }

        if (q.isEmpty()) break;
        count = count + k; // 由于退出内层循环有两种可能:1.此次interval结束;2.需要执行的任务在此次interval内都执行过一次,但是interval还未结束。因此,如果是第二种情况,CPU就要进入idle,需要把剩下的idle次数算在CPU总消耗时间内
    }
    return count;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值