堆排序和topK问题
完全二叉树
完全二叉树除了树的最后一层节点不需要是满的,其它的每一层从左到右都是满的。它有以下三个特点:
- 左孩子index=父节点index * 2 + 1
- 右孩子index=父节点index * 2 + 2
- 最后一个非叶子节点编号为index/2
利用最大堆做排序O(nlgn)
初始化最大堆
从最后一个非叶子节点往前遍历,进行堆调整,这里拿{ 49,38,65,97,76,13,27,49,55,04 }举例:
堆顶元素移除到末尾
堆顶元素是当前list中的最大值,放在末尾即可;
对其余元素进行堆调整,如此循环到第0号元素
#include <iostream>
// 调整最大堆
int adjust_head(int* list, int index, int len)
{
int l = index * 2 + 1;
int r = index * 2 + 2;
int max_index = index;
if (l < len && list[l] > list[max_index])
{
max_index = l;
}
if (r < len && list[r] > list[max_index])
{
max_index = r;
}
if (max_index != index)
{
std::swap(list[index], list[max_index]);
adjust_head(list, max_index, len); // 此时max_index指向的node是index下放下来的,值比较小,需要对此node往下调整
}
return 0;
}
int head_sort(int* list, int len)
{
// 建立堆
for (int i = len / 2; i >= 0; i--)
{
adjust_head(list, i, len);
}
// 从堆顶不停取出元素,放在数组后面
for (int i = len - 1; i >= 0; i--)
{
std::swap(list[0], list[i]);
// 堆调整只对其余i个元素进行
adjust_head(list, 0, i);
}
return 0;
}
int main(int argc, char* argv[])
{
int list[] = { 49,38,65,97,76,13,27,49,55,04 };
head_sort(list, 10);
for (int i = 0; i < 10; i++)
{
printf("%d ", list[i]);
}
printf("\n");
return 0;
}
利用最小堆求解topk问题
优点:
- 时间复杂度O(nlgk)
- 可以处理超大规模一次无法全部加载入内存的数据
#include <iostream>
#include <vector>
// 调整最小堆
int adjust_head(int* list, int index, int len)
{
int l = index * 2 + 1;
int r = index * 2 + 2;
int min_index = index;
if (l < len && list[l] < list[min_index])
{
min_index = l;
}
if (r < len && list[r] < list[min_index])
{
min_index = r;
}
if (min_index != index)
{
std::swap(list[index], list[min_index]);
adjust_head(list, min_index, len); // 此时max_index指向的node是index下放下来的,值比较小,需要对此node往下调整
}
return 0;
}
// topK question(最小堆)
int main(int argc, char* argv[])
{
int len = 10;
int k = 5;
int list[] = { 49,38,65,97,76,13,27,49,55,4 };
// 创建堆
int* myheap = new int[k];
for (int i = 0; i < k; i++)
{
myheap[i] = list[i];
}
// 初始化最小
for (int i = k / 2; i >= 0; i--)
{
adjust_head(myheap, i, k);
}
// 不断更新堆heap,完成topk的查找
for (int i = k; i < len; i++)
{
if (list[i] <= myheap[0])
{
continue;
}
// 如果下一个元素比堆顶大,可以纳入topk,再调整最小堆
myheap[0] = list[i];
adjust_head(myheap, 0, k);
}
for (int i = 0; i < k; i++)
{
printf("%d ", myheap[i]);
}
delete myheap;
printf("\n");
return 0;
}