题目描述
在一个无序的整数数组中选取重复频率最高的前k个数,例如:整数数组[3,4,5,6,4,3,5,2,3],k=3,则结果集为[3,5,4]。
题目解析
第一种方法暴力解法:对所有数的频率进行统计,然后用排序算法进行排序找出前k个频率最高的数。
如果我们按相对来说最优的排序算法快速排序来进行查找,快速排序代码如下:
public static void quickSort(int[] a,int left, int right){
if (left >= right) {
return;
}
int v = a[left];
int l = left + 1;
int r = right;
while (l < r) {
if (a[l] <= v) {
l++;
continue;
}
if (a[r] >= v) {
r--;
continue;
}
int temp = a[l];
a[l] = a[r];
a[r] = temp;
l++;
r--;
}
if (a[left] > a[l]) {
a[left] = a[l];
a[l] = v;
}else {
a[left] = a[l - 1];
a[l - 1] = v;
l--;
}
quickSort(a,left,l-1);
quickSort(a,l+1,right);
}
快速排序的时间复杂度不低于nlogn,统计的时间复杂度为n,
所以这种方法的最终时间复杂度为O(nlogn),空间复杂度为O(n)。
第二种方法是利用堆排序:将统计频率用最小堆进行排序,设置堆的最大值,超过最大值时将小的频率替换,最后堆剩下的值就是频率最高的数值。
这里统计频率用map存储。代码如下:
//用map对数的频率进行统计
public static void conut(int[] a){
int count = a.length;
for (int i = 0; i < count; i++) {
if (countMap.containsKey(a[i])) {
countMap.put(a[i], countMap.get(a[i])+1);
}else {
countMap.put(a[i], 1);
}
}
}
//利用堆排序
public static int[] dumpSort(Map<Integer,Integer> map,int k){
conut(a);
PriorityQueue<Integer> priorityQueue = new PriorityQueue(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return map.get(o1) - map.get(o2);
}
});
for (Integer i : map.keySet()) {
if (priorityQueue.size() < k) {
priorityQueue.add(i);
} else if (map.get(priorityQueue.peek()) < map.get(i)) {
priorityQueue.remove();
priorityQueue.add(i);
}
}
int[] res = new int[k];
for (int i = 0; i < k; i++) {
res[i] = priorityQueue.remove();
}
return res;
}
这里维护堆的数目是k,操作的数量是n,堆是二叉树的形式,所以每次维护堆的时间复杂度为logk,又因为总量是n,所以排序的时间复杂度为O(nlogk)。统计频率的时间复杂度为O(n),所以总的时间复杂度为O(nlogk)。空间复杂度中因为维护频率的map需要n,堆的数量需要k,一共需要的空间复杂度为
n+k,记为O(n)。
第三种是最优的算法利用桶排序:将统计后频率相同的数值放进同一个桶里,最后倒着从大的桶里开始取值,取出的量达到k位置。这里用list数组来表示不同的桶,list[i]用所以i来表示频率,list[i]其中的数值表示其中该频率的数值有哪些。代码如下:
public static void conut(int[] a){
int count = a.length;
for (int i = 0; i < count; i++) {
if (countMap.containsKey(a[i])) {
countMap.put(a[i], countMap.get(a[i])+1);
}else {
countMap.put(a[i], 1);
}
}
}
public static List<Integer> bucketSort(int k){
conut(a);
List<Integer> res = new ArrayList<>();
List<Integer>[] lists = new List[countMap.size()];
for (Integer key : countMap.keySet()) {
int i = countMap.get(key);
if (lists[i] == null) {
lists[i] = new ArrayList<>();
}
lists[i].add(key);
}
int length = lists.length;
for (int i = length - 1; i >= 0 && res.size() < k; i--) {
if (lists[i] == null) {
continue;
}
res.addAll(lists[i]);
}
return res;
}
这里只需要遍历一次即可,所以时间复杂度为n,统计频率的时间复杂度为n,所以最终时间复杂度表示为O(n)。每个数值只重复储存了一次,所以空间复杂度为O(n)。