堆排序及其应用

堆排序及其应用

1、大顶堆和小顶堆

堆是具有下列特征的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于都等于其左右孩子结点的值,称为小顶堆。
  结点之间的关系:大顶堆:a[n] >= a[2 * n] && a[n] >= a[2 * n + 1];小顶堆:a[n] <= a[2 * n] && a[n] <= a[2 * n + 1]

2、堆排序

由于堆中结点之间满足的关系,因此可以直接利用数组的下标对其进行排序,代码如下:

void AdjustHeap(vector<int>&a, int s, int len) {
    int i = s;
    int temp = a[i];
	int j = 2 * i;
	for (int j = 2 * i; j <= len; j *= 2) {
	    if (j < len && a[j] < a[j + 1]) {
		    ++j;
        }
		if (temp >= a[j]) {
		    break;
		}
		a[i] = a[j];
		i = j;
	}
	a[i] = temp;
}

void HeapSort(vector<int>&a) {
    int len = a.size() - 1;  // vector中第一个元素填入0
    for (int i = len / 2; i >= 1; --i) {  //建立大顶堆
	    AdjustHeap(a, i, len);	
	}
	
	for (int i = a.size() - 1; i > 1; --i) {
	    swap(a[1], a[i]);
		AdjustHeap(a, 1, i - 1);  // 堆剩下的i - 1个元素重新调整为大顶堆
	}
}

算法复杂度: O(nlogn)
 在构建堆的过程中,是从完全二叉树的最下层最右边的非叶子结点开始构建,将它与其孩子结点进行比较和交换,对于每个非叶子结点来说,最多进行两次比较和互换动作,因此整个构建堆的过程时间复杂度为O(n)。
 正式排序时,第i次取堆顶元素重建堆需要时间复杂度O(logi), (因为剩下i个元素无序),并且需要取n次堆顶元素,因此重建堆的过程时间复杂度为O(nlogn)。
  因此整体来说,时间复杂度为O(nlogn)。

3、堆排序的应用

  1. topK问题
    1)对于找出前K个最小元素:用大顶堆
     创建一个K维的数组,每次从N个数中读入一个,如果当前数组中的个数小于K,则直接放入,当数组中的个数等于K时只能替换,(如果读入的数比当前数组中最大的值都大则继续读入下一个,否则替换其中最大的一个)。因此这个过程需要确认K维数组中当前最大的数,用大顶堆。
     时间复杂度O(nlogk)
    2)找出前K个元素最大值:用小顶堆
  2. 海量数据问题
    对于海量数据的问题,考虑到内存占用,一般需要放到多个文件中进行处理,利用分治的思想。如果有重复的数据,需要利用hash表计算出每种数据出现的次数,然后在单个文件中利用堆进行排序。
    参考:在100G文件中找出出现次数最多的100个IP

在100G文件中找出出现次数最多的前100个IP
 要解决该问题需要找出一种分类方式,将重复出现的放到一个文件中,比如可以根据IP地址的前8位或者后8位进行分类(前8位比较稀疏,考虑用后8位),这样一共有2的8次幂中分类方式。这样将一个大文件拆成多个小文件,依次对每个小文件进行处理。
 对其中一个小文件,建立hash表,key是ip地址,value是其出现的次数。然后建立一个小顶堆,大小为100,逐个对出现的次数进行排序,挑选出前100个。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值