https://blog.youkuaiyun.com/juzihongle1/article/details/70212243
堆(Heap)、堆排序和TopK
https://www.cnblogs.com/eudiwffe/p/6202111.html
《排序算法》——堆排序(大顶堆,小顶堆,Java)
https://blog.youkuaiyun.com/Gamer_gyt/article/details/47143987
大顶堆和小顶堆–Java版
https://blog.youkuaiyun.com/silentwolfyh/article/details/77099258
PriorityQueue
https://www.cnblogs.com/gnivor/p/4841191.html
堆排序和topk不完全一样,topk在建堆之后是不排序的,大顶堆里是数组里最小的k个数,小顶堆里是数组里最大的k个数,只有arr[0]是topk,其余的都是无序的,堆排序需要在建堆之后进行排序,整个数组才会有序
这个方法的时间复杂度是多少呢?
1.构建堆的时间复杂度是 O(k)
2.遍历剩余数组的时间复杂度是O(n-k)
3.每次调整堆的时间复杂度是 O(logk)
其中2和3是嵌套关系,1和2,3是并列关系,所以总的最坏时间复杂度是O((n-k)logk + k)。当k远小于n的情况下,也可以近似地认为是O(nlogk)。
这个方法的空间复杂度是多少呢?
刚才我们在详细步骤中把二叉堆单独拿出来演示,是为了便于理解。但如果允许改变原数组的话,我们可以把数组的前k个元素“原地交换”来构建成二叉堆,这样就免去了开辟额外的存储空间。
因此,方法的空间复杂度是O(1)。
topk
public static void heapSort(int[] array,int k) {
if (array == null || array.length == 0)
return;
buildHeap(array,k); // 第一次排序,构建最大堆,只保证了堆顶元素是数组里最大的
for (int i = k; i < array.length; i++) {
if (array[i]<array[0]) {
swap(array, i, 0);
buildHeap(array,k);
}
}
}
public static void buildHeap(int[] array,int k) {
if (array == null || array.length == 1)
return;
// 堆的公式就是 int root = 2*i, int left = 2*i+1, int right = 2*i+2;
int cursor = k / 2-1;
for (int i = cursor; i >= 0; i--) { // 这样for循环下,就可以第一次排序完成
maxHeap(array, k, i);
// minHeap(array, k, i);
}
}
堆排序
public static void heapSort(int[] array) {
if (array == null || array.length == 1)
return;
buildHeap(array); // 第一次排序,构建最大堆,只保证了堆顶元素是数组里最大的
for (int i = array.length - 1; i >= 0; i--) {
// 这个是什么意思呢?,经过上面的一些列操作,目前array[0]是当前数组里最大的元素,需要和末尾的元素交换
// 然后,拿出最大的元素
swap(array, 0, i);
// 交换完后,下次遍历的时候,就应该跳过最后一个元素,也就是最大的那个值,然后开始重新构建最大堆
// 堆的大小就减去1,然后从0的位置开始最大堆
maxHeap(array, i, 0);
// minHeap(array, i, 0);
}
}
// 构建堆
public static void buildHeap(int[] array) {
if (array == null || array.length == 1)
return;
// 堆的公式就是 int root = 2*i, int left = 2*i+1, int right = 2*i+2;
int cursor = array.length / 2-1;
for (int i = cursor; i >= 0; i--) { // 这样for循环下,就可以第一次排序完成
maxHeap(array, array.length, i);
// minHeap(array, array.length, i);
}
}
大顶堆
public static void maxHeap(int[] array, int heapSieze, int index) {
int left = index * 2 + 1; // 左子节点
int right = index * 2 + 2; // 右子节点
int maxValue = index; // 暂时定在Index的位置就是最大值
// 如果左子节点的值,比当前最大的值大,就把最大值的位置换成左子节点的位置
if (left < heapSieze && array[left] > array[maxValue]) {
maxValue = left;
}
// 如果右子节点的值,比当前最大的值大,就把最大值的位置换成右子节点的位置
if (right < heapSieze && array[right] > array[maxValue]) {
maxValue = right;
}
// 如果不相等,说明啊,这个子节点的值有比自己大的,位置发生了交换了位置
if (maxValue != index) {
swap(array, index, maxValue); // 就要交换位置元素
// 交换完位置后还需要判断子节点是否打破了最大堆的性质。最大堆性质:两个子节点都比父节点小。
maxHeap(array, heapSieze, maxValue);
}
}
小顶堆
public static void minHeap(int[] array, int heapSieze, int index) {
int left = index * 2 + 1; // 左子节点
int right = index * 2 + 2; // 右子节点
int maxValue = index; // 暂时定在Index的位置就是最小值
// 如果左子节点的值,比当前最小的值小,就把最小值的位置换成左子节点的位置
if (left < heapSieze && array[left] < array[maxValue]) {
maxValue = left;
}
// 如果右子节点的值,比当前最小的值小,就把最小值的位置换成左子节点的位置
if (right < heapSieze && array[right] < array[maxValue]) {
maxValue = right;
}
// 如果不相等,说明啊,这个子节点的值有比自己小的,位置发生了交换了位置
if (maxValue != index) {
swap(array, index, maxValue); // 就要交换位置元素
// 交换完位置后还需要判断子节点是否打破了最小堆的性质。最小性质:两个子节点都比父节点大。
minHeap(array, heapSieze, maxValue);
}
}
交换函数:必须要把arr传入才会改变arr,否则进去的是值,不是引用
public static void swap(int[] array, int index1, int index2) {
int temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
K Closest Points to Origin
We have a list of points on the plane. Find the K closest points to the origin (0, 0).
(Here, the distance between two points on a plane is the Euclidean distance.)
You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in.)
Example 1:
Input: points = [[1,3],[-2,2]], K = 1
Output: [[-2,2]]
Explanation:
The distance between (1, 3) and the origin is sqrt(10).
The distance between (-2, 2) and the origin is sqrt(8).
Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin.
We only want the closest K = 1 points from the origin, so the answer is just [[-2,2]].
Example 2:
Input: points = [[3,3],[5,-1],[-2,4]], K = 2
Output: [[3,3],[-2,4]]
(The answer [[-2,4],[3,3]] would also be accepted.)
public int[][] kClosest(int[][] points, int K) {
// 优先队列就是堆,传入的是比较器,由于是和原点比较,所以用的是x1^2+y1^2-(x2^2+y2^2)
PriorityQueue<int[]> pq = new PriorityQueue<int[]>((p1, p2) ->p2[0] * p2[0] + p2[1] * p2[1] - p1[0] * p1[0] - p1[1] * p1[1]);
for (int[] p : points) {
pq.offer(p);
if (pq.size() > K) {
pq.poll();
}
}
int[][] res = new int[K][2];
while (K > 0) {
res[--K] = pq.poll();
}
return res;
}