目录
一、解决TOPK问题
方式一
建立优先级队列,将所有数据放入其中,根据需求创建大根堆或小根堆,依次出队k个元素,放入结果集合中。
例如:找最小的k个数
因为优先级队列中默认建立的小根堆,不必修改比较方法。
代码如下:
import java.util.PriorityQueue;
public class TopK {
public static int [] TopK1(int[] array,int k){
//定义一个结果集
int [] result=new int[k];
//非空校验
if(array.length==0){
return result;
}
//创建优先级队列(小根堆)
PriorityQueue<Integer> queue=new PriorityQueue<>();
for (int i=0;i<array.length;i++){
//全部入队
queue.offer(array[i]);
}
//出队k个
for (int i=0;i<k;i++){
result[i]=queue.poll();
}
//加入结果集合中
return result;
}
}
方式二
建立大根堆:若找最小的k个数,建立大根堆,先将数组前k个数放入队中,之后加入的每个数都与队顶元素作比较,比队顶元素小的和队顶元素做交换,否则不做处理,最后留在优先级队列中的k个数就是最小的k个值。
注:因为用优先级队列建立大根堆,所以需要改变默认的比较方法,用到的是comparator比较器,需要重写比较规则
建立小根堆:若找最大的k个数,建立小根堆,先将数组的前k个数放入队中,之后加入的每个数都与队顶元素作比较,比队顶元素大的和队顶元素做交换,否则不做处理,最后留在优先级队列中的k个数就是最大的k个值。
例如:找最小的k个值,建立大根堆。
public class TopK{
public static int[] TopK(int[] array,int k) {
//创建结果集
int[] result = new int[k];
//非空校验
if (k <= 0) {
return result;
}
//这里需要建立大根堆,修改比较规则
//小根堆默认o1-o2,这里我们o2-o1实现大根堆
PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
for (int i=0;i<array.length;i++){
//前k个值不做处理直接入队
if(i<k){
queue.offer(array[i]);
}else{
//比队顶元素大做交换后入队
if(array[i]<queue.peek()){
queue.poll();
queue.offer(array[i]);
}
}
}
//将最后留在堆中的k个值放入结果集合中
for (int i=0;i<k;i++){
result[i]=queue.poll();
}
//放回结果集合
return result;
}
}
二、堆排序
与建堆的区别
堆排序是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建小堆,排降序建大堆。最后输出的是有序数列。
建堆:大根堆中,只针对根节点比左右子树大(包括每一个子树都满足);小根堆中,只针对根节点比左右子树小(包括每一个子树都满足)。最后输出的不是有序数列。
堆排序代码
public class HeapSort {
//建堆
public static void createHeap(int[] array){
for(int parent=(array.length-2)/2;parent>=0;parent--){
shiftDown(array,parent,array.length);
}
}
//向下调整
private static void shiftDown(int[] array, int parent, int length) {
// 找到左孩子节点
int child = 2 * parent + 1;
while (child < length) {
// 判断有没有右孩子节点
if (child + 1 < length) {
// 如果有右孩子,取左右孩子节点中最大值的下标
if (array[child + 1] > array[child]) {
//child始终在值最大的节点上
child++;
}
}
// 孩子节点中最大的值与父节点的值比较
if (array[child] <= array[parent]) {
//小于等于父节点不做交换
break;
}
// 大于父节点时,交换父子节点
swap(array, parent, child);
// 重置父子节点的下标
parent = child;
child = 2 * parent + 1;
}
}
//交换父节点与子节点
private static void swap(int[] array, int parent, int child) {
int temp = array[parent];
array[parent] = array[child];
array[child] = temp;
}
//排序
public static void sort (int [] array) {
// 1.首先建堆
createHeap(array);
// 2. 确定终止下标
//传进无序堆时,先把堆顶元素和最后一个元素做了交换,所以end应该在倒数第二的位置
int end = array.length - 1;
while (end >= 0) {
// 3. 首尾交换
swap(array, 0, end);
// 4. 向下调整
shiftDown(array, 0, end);
// 5. 修正 end
end--;
}
}
public static void main(String[] args) {
int [] array = { 27,15,19,18,28,34,65,49,25,37 };
sort(array);
System.out.println(Arrays.toString(array));
}
}