下面先对该经典题目做一下思路的分析,方便对LeetCode的题目做进一步的理解。
思路一、使用最小堆
使用优先队列来维护当前看到的的前M个元素。对N个元素从头到尾扫描遍历一次,再将当前的前M个元素放入优先队列中,每次看到一个新的元素如果该元素比优先队列中最小的元素还要大时候,接着就把优先队列中最小的元素给抛出,取而代之的换成该新的元素。直到N-M个新的元素扫描完为止。
因为我们定义的优先队列需要不断地去寻找最小值,所以此队列,就需要利用最小堆来实现。
思路二、使用最大堆
由于没有明确地定义值最大地那个元素的优先级最高,所以可以自己定义一个值最小优先级最高的队列。
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
说明:
你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
思路:先求出频次,再进行前K高的元素的判断取出操作,给出的数组是一个有重复元素的数组。利用Java提供的TreeMap来统计元素的频次。使用我定义的最大堆来实现的优先队列来解决此问题。
class Solution {
//频次类
private class Freq implements Comparable{
public int e, freq;
public Freq(int e, int freq){
this.e = e;
this.freq = freq;
}
@Override
public int compareTo(Freq another){
//当前freq与其他freq做比较
if(this.freq < another.freq){
//当前元素的频次低,表示优先级高,因为优先队列底层是用最大堆来实现的
//最大的元素频次自然是最低的
return 1;
}else if(this.freq > another.freq){
return -1;
}else{
return 0;
}
}
}
//给出一个方法,传入一个数组nums,和频次K高的元素返回到List中
public List topKFrequent(int[] nums, int k) {
//利用TreeMap对数组元素的频次进行统计
TreeMap map = new TreeMap<>();
//遍历数组
for(int num: nums){
//如果元素存在,则对该频次进行+1
if(map.containsKey(num)) {
map.put(num, map.get(num) + 1);
}else{
//否则赋为1
map.put(num, 1);
}
}
//定义一个优先队列,存放频次高的元素
PriorityQueue pq = new PriorityQueue<>();
//对映射所有的键进进行遍历
for(int key: map.keySet()){
//如果队列存放的元素小于我们要求的k
if(pq.getSize() < k){
//入队新元素对象e,频次为map.get(key)
pq.enqueue(new Freq(key, map.get(key)));
}else if(map.get(key) > pq.getFront().freq){ //遍历到的元素大于队列中优先级最高的元素,即最大的元素,频次是最低的
//对频次最低的元素进行出队
pq.dequeue();
//进队一个新的元素
pq.enqueue(new Freq(key, map.get(key)));
}
}
//定义数组链表存放队列中的元素
LinkedList res = new LinkedList<>();
while(!pq.isEmpty())
res.add(pq.dequeue().e);
return res;
}
}
此时不能直接提交该代码,因为里面的PriorityQueue是我们自己定义的,所以需要将PriorityQueue的底层所有实现代码做成一个内部类来提交,则不会出现错误。
Java中的PriorityQueue内部实现是一个最小堆,与我们实现的最大堆不一样,上面的解答是利用最大堆来实现的。
1.使用我们自己定义的优先队列中使用的比较器来实现我们自己的比较逻辑来实现。
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.TreeMap;
public class Solution {
private class Freq implements Comparable{
public int e, freq;
public Freq(int e, int freq){
this.e = e;
this.freq = freq;
}
public int compareTo(Freq another){
if(this.freq < another.freq) {
return -1;
}else if(this.freq > another.freq){
return 1;
}else{
return 0;
}
}
}
public List topKFrequent(int[] nums, int k) {
TreeMap map = new TreeMap<>();
for(int num: nums){
if(map.containsKey(num))
map.put(num, map.get(num) + 1);
}else{
map.put(num, 1);
}
}
PriorityQueue pq = new PriorityQueue<>();
for(int key: map.keySet()){
if(pq.size() < k) {
pq.add(new Freq(key, map.get(key)));
}else if(map.get(key) > pq.peek().freq){
pq.remove();
pq.add(new Freq(key, map.get(key)));
}
}
LinkedList res = new LinkedList<>();
while(!pq.isEmpty()){
res.add(pq.remove().e);
}
return res;
}
}
2.使用Java类库中的比较器逻辑实现
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.TreeMap;
import java.util.Comparator;
public class Solution {
private class Freq{
public int e, freq;
public Freq(int e, int freq){
this.e = e;
this.freq = freq;
}
}
private class FreqComparator implements Comparator{
@Override
public int compare(Freq a, Freq b){
//让频率小的靠前
return a.freq - b.freq;
}
}
public List topKFrequent(int[] nums, int k) {
TreeMap map = new TreeMap<>();
for(int num: nums){
if(map.containsKey(num)) {
map.put(num, map.get(num) + 1);
}else{
map.put(num, 1);
}
}
PriorityQueue pq = new PriorityQueue<>(new FreqComparator());
for(int key: map.keySet()){
if(pq.size() < k){
pq.add(new Freq(key, map.get(key)));
}else if(map.get(key) > pq.peek().freq){
pq.remove();
pq.add(new Freq(key, map.get(key)));
}
}
LinkedList res = new LinkedList<>();
while(!pq.isEmpty()){
res.add(pq.remove().e);
}
return res;
}
}
上面的比较器实现,可用于当比较器是我们自己实现的逻辑,如果要传的字符串,则可以对我们自己的比较器进行修改。
所以把比较器放在外部。
3.进一步改进,使用匿名类来使用比较器。
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.TreeMap;
import java.util.Comparator;
public class Solution {
private class Freq{
public int e, freq;
public Freq(int e, int freq){
this.e = e;
this.freq = freq;
}
}
private class FreqComparator implements Comparator{
@Override
public int compare(Freq a, Freq b){
//让频率小的靠前
return a.freq - b.freq;
}
}
public List topKFrequent(int[] nums, int k) {
TreeMap map = new TreeMap<>();
for(int num: nums){
if(map.containsKey(num)) {
map.put(num, map.get(num) + 1);
}else{
map.put(num, 1);
}
}
PriorityQueue pq = new PriorityQueue<>(new Comparator() {
@Override
public int compare(Freq a, Freq b) {
return a.freq - b.freq;
}
});
for(int key: map.keySet()){
if(pq.size() < k){
pq.add(new Freq(key, map.get(key)));
}else if(map.get(key) > pq.peek().freq){
pq.remove();
pq.add(new Freq(key, map.get(key)));
}
}
LinkedList res = new LinkedList<>();
while(!pq.isEmpty()){
res.add(pq.remove().e);
}
return res;
}
}
4.此时匿名类在我们定义的算法函数作用域中。按照频率进行比较。
public class Solution {
public List topKFrequent(int[] nums, int k) {
TreeMap map = new TreeMap<>();
for(int num: nums){
if(map.containsKey(num)) {
map.put(num, map.get(num) + 1);
}else{
map.put(num, 1);
}
}
PriorityQueue pq = new PriorityQueue<>(new Comparator() {
@Override
public int compare(Integer a, Integer b) {
return map.get(a) - map.get(b);
}
});
for(int key: map.keySet()){
if(pq.size() < k){
pq.add(key);
}else if(map.get(key) > map.get(pq.peek())){
pq.remove();
pq.add(key);
}
}
LinkedList res = new LinkedList<>();
while(!pq.isEmpty()){
res.add(pq.remove());
}
return res;
}
}
5.使用lambda表达式
public class Solution {
public List topKFrequent(int[] nums, int k) {
TreeMap map = new TreeMap<>();
for(int num: nums){
if(map.containsKey(num)){
map.put(num, map.get(num) + 1);
}else{
map.put(num, 1);
}
}
PriorityQueue pq = new PriorityQueue<>(
(a, b) -> map.get(a) - map.get(b)
);
for(int key: map.keySet()){
if(pq.size() < k){
pq.add(key);
}else if(map.get(key) > map.get(pq.peek())){
pq.remove();
pq.add(key);
}
}
LinkedList res = new LinkedList<>();
while(!pq.isEmpty()){
res.add(pq.remove());
}
return res;
}
}