LeetCode进阶1086-Hash思想

概要

本篇主要介绍LeetCode上第1086题,本篇素材主要来源于平常刷题习惯整理记录,做为数据结构算法开篇,但愿更多的展示刷题思路,学习数据结构与算法的方法技巧。本篇会有个小“彩蛋”,“彩蛋”在笔者这里的定义是能够提升算法执行效率的小技巧,后续关于数据结构与算法的系列推文中,笔者也会尽量多的埋一些“彩蛋”。千里之行,始于足下,共勉~算法

题意分析

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 Table分类下

分析

结合LeetCode的官方分类能够猜想出本题解题思路偏向Hash思想,故至少有一种思路是Hash方向,具体实践时能够尝试不一样的解题思路。因为是从不少分数中选出分数最高的前5个计算平均分,所以天然能联想到最大堆的基本数据结构,而Java标准库提供了堆相关的优先队列PriorityQueue,所以第二种思路考虑使用堆思想。

思路设计

方法一:Hash 思想

考虑题目描述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的设定就是最小堆(标准库:->_-> 怪我咯),堆实现相对简单所以不做详细思路拆分,直接上代码。

编码实践

Hash实现

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++,这即是本篇当中的彩蛋。关于彩蛋后续不按期会有讲解(废话...彩蛋不就是下次才能看明白的么->_->)

Alt

扫一扫 关注个人微信订阅号
相关文章
相关标签/搜索