High Five Lintcode
There are two properties in the node student id
and scores
, to ensure that each student will have at least 5 points, find the average of 5 highest scores for each person.
Example
Given results = [[1,91],[1,92],[2,93],[2,99],[2,98],[2,97],[1,60],[1,58],[2,100],[1,61]]
Return
/** * Definition for a Record * class Record { * public int id, score; * public Record(int id, int score){ * this.id = id; * this.score = score; * } * } */ public class Solution { /** * @param results a list of <student_id, score> * @return find the average of 5 highest scores for each person * Map<Integer, Double> (student_id, average_score) */ public Map<Integer, Double> highFive(Record[] results) { PriorityQueue<Record> hq = new PriorityQueue<>(10, new Comparator<Record>(){ public int compare(Record a, Record b) { if (a.id != b.id) { return a.id - b.id; } return b.score - a.score; } }); Map<Integer, Double> result = new HashMap<>(); for (int i = 0; i < results.length; i++) { hq.add(results[i]); } while (!hq.isEmpty()) { Record r = hq.peek(); int id = r.id; int sum = 0; for (int i = 0; i < 5; i++) { r = hq.poll(); if (r.id == id) { sum += r.score; } } result.put(id, sum / 5.0); while (!hq.isEmpty() && hq.peek().id == id) { hq.poll(); } } return result; } }
这道题一开始用heap解的,发现小数据可以,大数据的话会超时。大概比较的次数太多了。
所以改进了一下,heap里面只存五个数,是min heap。如果多于5个数,且要加的数比最小的大,就把最小的poll出来,再把这个数放进去。这样可以减小heap里面比较的次数。这下不会超时了。
public class Solution { /** * @param results a list of <student_id, score> * @return find the average of 5 highest scores for each person * Map<Integer, Double> (student_id, average_score) */ class RecordComparator implements Comparator<Record> { public int compare(Record a, Record b) { return a.score - b.score; } } public Map<Integer, Double> highFive(Record[] results) { Map<Integer, Double> result = new HashMap<>(); Map<Integer, PriorityQueue<Record>> hm = new HashMap<>(); for (int i = 0; i < results.length; i++) { if (hm.containsKey(results[i].id)) { if (hm.get(results[i].id).size() < 5) { hm.get(results[i].id).add(results[i]); } else { PriorityQueue<Record> pq = hm.get(results[i].id); if (pq.peek().score < results[i].score) { pq.poll(); pq.add(results[i]); } } } else { hm.put(results[i].id, new PriorityQueue<Record>(5, new RecordComparator())); hm.get(results[i].id).add(results[i]); } } for (Integer i: hm.keySet()) { int sum = 0; PriorityQueue<Record> q = hm.get(i); for (int j = 0; j < 5; j++) { Record r = q.poll(); sum += r.score; } double average = sum / 5.0; result.put(i, average); } return result; } }
看了下答案,hashmap里面value用arraylist,可能会快一点点,这个要根据数据集来看决定采用哪种结构。因为用arraylist的话,每次都是O(n)时间来查找最小值来替换,用heap的话是O(log(n)),在前五个添加的时候慢一点,但是后面替换快很多。
附上arraylist写法,懒得自己写了。。。有时间可以再写写锻炼一下。。。
/** * Definition for a Record * class Record { * public int id, score; * public Record(int id, int score){ * this.id = id; * this.score = score; * } * } */ public class Solution { /** * @param results a list of <student_id, score> * @return find the average of 5 highest scores for each person * Map<Integer, Double> (student_id, average_score) */ public Map<Integer, Double> highFive(Record[] results) { Map<Integer, Double> answer = new HashMap<Integer, Double>(); Map<Integer, List<Integer>> hash = new HashMap<Integer, List<Integer>>(); for (Record r : results) { if (!hash.containsKey(r.id)){ hash.put(r.id, new ArrayList<Integer>()); } if (hash.get(r.id).size() < 5) { hash.get(r.id).add(r.score); } else { int index = 0; for (int i = 1; i < 5; ++i) if (hash.get(r.id).get(i) < hash.get(r.id).get(index)) index = i; if (hash.get(r.id).get(index) < r.score) hash.get(r.id).set(index, r.score); } } for (Map.Entry<Integer, List<Integer>> entry : hash.entrySet()) { int id = entry.getKey(); List<Integer> scores = entry.getValue(); double average = 0; for (int i = 0; i < 5; ++i) average += scores.get(i); average /= 5.0; answer.put(id, average); } return answer; } }