本篇主要介绍LeetCode上第1086题,本篇素材主要来源于平常刷题习惯整理记录,做为数据结构算法开篇,但愿更多的展示刷题思路,学习数据结构与算法的方法技巧。本篇会有个小“彩蛋”,“彩蛋”在笔者这里的定义是能够提升算法执行效率的小技巧,后续关于数据结构与算法的系列推文中,笔者也会尽量多的埋一些“彩蛋”。千里之行,始于足下,共勉~算法
Given a list of scores of different students, return the average score of each student's top five scores in the order of each student's id.数组
Each entry items[i] has items[i][0] the student's id, and items[i][1] the student's score. The average score is calculated using integer division.bash
Example 1:微信
Input: [[1,91],[1,92],[2,93],[2,97],[1,60],[2,77],[1,65],[1,87],[1,100],[2,100],[2,76]] Output: [[1,87],[2,88]] Explanation: The average of the student with id = 1 is 87. The average of the student with id = 2 is 88.6. But with integer division their average converts to 88.数据结构
Note:学习
1 <= items.length <= 1000 items[i].length == 2 The IDs of the students is between 1 to 1000 The score of the students is between 1 to 100 For each student, there are at least 5 scoresui
给出一个不一样学生的分数二维数组,根据每一个学生的id,将每一个学生的排在前五的平均分数返回。编码
items[i]中items[i][0]表明学生的id,items[i][1]表明学生的得分。平均分数是用整数除法计算spa
例1:翻译
输入:[1,91],[1,92],[2,93],[2,97],[1,60],[2,77],[1,65],[1,87],[1,87][1100],[2100],[2,76]] 输出:[[1,87],[2,88]] 解释: id为1的学生的平均数是87。 在id=2的学生中,平均为88.6。可是随着整除,平均值变为88。
备注:
1 <= items.length <= 1000 Items[i].length == 2 学生的id在1到1000之间 这些学生的分数在1到100之间 每一个学生至少有5个得分
结合LeetCode的官方分类能够猜想出本题解题思路偏向Hash思想,故至少有一种思路是Hash方向,具体实践时能够尝试不一样的解题思路。因为是从不少分数中选出分数最高的前5个计算平均分,所以天然能联想到最大堆的基本数据结构,而Java标准库提供了堆相关的优先队列PriorityQueue,所以第二种思路考虑使用堆思想。
考虑题目描述id限制在1~1000之间,天然能够联想到Hash思想里面比较经典桶结构,故考虑使用1~1001的桶结构来存储不一样id,数组下标即id。每一个id对应一个排序数组存储前5排名的分数,具体实现思路以下:
一、二维数组int n[1001][5]存储id,排前5名分数数组,分数数组降序排列。
二、遍历items数组:
i.若n[i]为null说明为新id,建立n[i]=int[5]的排序数组,并将当前分数加入数组第1位;
ii.不然,若score得分低于排序数组中最后一位n[i][4]最小值,则继续循环;
iii.不然,for循环遍历排序数组,交换排序插入新分数;
三、遍历数组n,若n[i]数组为null,说明当前桶中无数据即无对应学生id,循环继续;
不然,取出n[i]处排序数组,循环累加总分计算平均分,id和平均分存入数组。
复制代码
堆实现思路相对简单,使用大小为5的最小堆队列,每次插入新值,移除队头的最小值,保证队列是最大的5个数,这样队列中始终是排名前5的分数。遍历完成后再计算每一个id对应堆队列的平均分。为何使用最小堆是由于Java标准库中的堆队列PriorityQueue的设定就是最小堆(标准库:->_-> 怪我咯),堆实现相对简单所以不做详细思路拆分,直接上代码。
public int[][] highFive(int[][] items) {
int n[][] = new int[1001][];
for (int i = 0; i < items.length; ++i) {
int id = items[i][0];
int score = items[i][1];
if (n[id] == null) {
n[id] = new int[5];
n[id][0] = score;
continue;
}
if (score <= n[id][4]) {
continue;
}
for (int j = 0; j < 5; j++) {
if (score > n[id][j]) {
int temp = n[id][j];
n[id][j] = score;
score = temp;
}
}
}
List<int[]> result = new ArrayList<>();
for (int i = 0; i < 1001; ++i) {
if (n[i] == null) {
continue;
}
int total = 0;
for (int score : n[i]) {
total += score;
}
result.add(new int[] { i, total / 5 });
}
return result.toArray(new int[0][0]);
}
复制代码
public int[][] highFive(int[][] items) {
List<Integer> ids = new ArrayList<Integer>();
HashMap<Integer, PriorityQueue<Integer>> map = new HashMap<Integer, PriorityQueue<Integer>>();
for (int i = 0; i < items.length; ++i) {
PriorityQueue<Integer> queue = map.get(items[i][0]);
if (queue == null) {
queue = new PriorityQueue<Integer>(5);
map.put(items[i][0], queue);
ids.add(items[i][0]);
}
queue.add(items[i][1]);
if (queue.size() > 5) {
queue.poll();
}
}
int result[][] = new int[ids.size()][2];
for (int i = 0; i < ids.size(); ++i) {
PriorityQueue<Integer> queue = map.get(ids.get(i));
int sum = 0;
while (!queue.isEmpty()) {
sum += queue.poll();
}
result[i][0] = ids.get(i);
result[i][1] = sum / 5;
}
return result;
}
复制代码
仔细观察上述两种思路的实现代码会发现,有几处for循环的时候使用的都是++i,而不是i++,这即是本篇当中的彩蛋。关于彩蛋后续不按期会有讲解(废话...彩蛋不就是下次才能看明白的么->_->)