Heapsort 堆排序算法详解(Java实现)
【给力追-女视频】【抠:⒈01б.x.⒐⒌⒉⒍】 Heapsort (堆排序)是最经典的排序算法之一,在google或者百度中搜一下可以搜到很多非常详细的解析。同样好的排序算法还有quicksort(快速排序)和merge sort(归并排序),选择对这个算法进行分析主要是因为它用到了一个非常有意思的算法技巧:数据结构 - 堆。而且堆排其实是一个看起来复杂其实并不复杂的排序算法,个人认为heapsort在机器学习中也有重要作用。这里重新详解下关于Heapsort的方方面面,也是为了自己巩固一下这方面知识,有可能和其他的文章有不同的入手点,如有错误,还请指出。文中引用的referecne会再结尾标注。
p.s. 个人认为所谓详解是你在看相关wiki或者算法书看不懂的时候看通俗易懂的解释,不过最佳方案还是去看教授们的讲解,推荐reference[1]中的heapsort章节。
以上是废话,可以不看 Section 1 - 简介
Heapsort是一个comparison-based的排序算法(快排,归并,插入都是;counting sort不是),也是一种选择排序算法(selection sort),一个选择算法(selection algorithm)的定义是找到一个序列的k-th order statistic(统计学中的术语),直白的说就是找到一个list中第k-th小的元素。以上都可以大不用懂,heapsort都理解了回来看一下是这回事就是了。同样,插值排序也是一种选择排序算法。
Heapsort的时间复杂度在worst-case是O(nlgn)O(nlgn),
average-case是O(nlgn)O(nlgn);空间复杂度在worst-case是O(1)O(1),也就是说heapsort可以in-place实现;heapsort不稳定。
以下顺便附上几种排序算法的时间复杂度比较(Θ?notationΘ?notation比O?notationO?notation更准确的定义了渐进分析(asymptotic
analysis)的上下界限,详细了解可以自行google): Table 1 - 四种排序算法的running time比较 Algorithm Worst-case
Average-case-expected
Insertion sort(插值排序) Θ(n2)Θ(n2) Θ(n2)Θ(n2)
Merge sort(归并排序) Θ(nlgn)Θ(nlgn) Θ(nlgn)Θ(nlgn) Heapsort(堆排序) O(nlgn)O(nlgn) O(nlgn)O(nlgn) Quicksort(快速排序) Θ(n2)Θ(n2)
Θ(n2)Θ(n2) (expected)
*Additional Part - KNN
heapsort在实践中的表现经常不如quicksort(尽管quicksort最差表现为 Θ(n2)Θ(n2),但quicksort
99%情况下的runtime complexity为 Θ(nlgn)Θ(nlgn)),但heapsort的O(nlgn)O(nlgn)的上限以及固定的空间使用经常被运作在嵌入式系统。在搜索或机器学习中经常也有重要的作用,它可以只返回k个排序需要的值而不管其他元素的值。例如KNN(K-nearest-neighbour)中只需返回K个最小值即可满足需求而并不用对全局进行排序。当然,也可以使用divide-and-conquer的思想找最大-小的K个值,这是一个题外话,以后有机会做一个专题比较下。
以下程序为一个简单的在python中调用heapq进行heapsort取得k个最小值,可以大概体现上面所述的特性:
Created On 15-09-2014 @author: Jetpie import heapq, time
import scipy.spatial.distance as spd import numpy as np pool_size = 100000
#generate an 3-d random array of size 10,000
# data = np.array([[2,3,2],[3,2,1],[2,1,3],[2,3,2]]) data = np.random.random_sample((pool_size,3))
#generate a random input
input = np.random.random_sample() #calculate the distance list
dist_list = [spd.euclidean(input,datum) for datum in data] #find k nearest neighbours #use heapsort start = time.time()
heap_sorted = heapq.nsmallest(k, range(len(dist_list)), key = lambda x: dist_list[x])
print('Elasped
time
for
heapsort
to
return
%s
smallest: %s'%(k,(time.time() - start)))
#find k nearest neighbours #use heapsort start = time.time()
heap_sorted = heapq.nsmallest(k, range(len(dist_list)), key = lambda x: dist_list[x])
print('Elasped
time
for
heapsort
to
return
%s
smallest: %s'%(k,(time.time() - start)))
get_k_smallest 运行结果为:
Elasped time for heapsort to return 10 smallest: 0.0350000858307 Elasped time for heapsort to return 10000 smallest:
0.0899999141693
Section 2 - 算法过程理解 2.1 二叉堆
在“堆排序”中的“堆”通常指“二叉堆(binary heap)”,许多不正规的说法说“二叉堆”其实就是一个完全二叉树(complete binary tree),这个说法正确但不准确。但在这基础上理解“二叉堆”就非常的容易了,二叉堆主要满足以下两项属性(properties):
#1 - Shape Property: 它是一个完全二叉树。
#2 - Heap Property: 主要分为max-heap property和min-heap property(这就是我以前说过的术语,很重要)
|--max-heap property :对于所有除了根节点(root)的节点 i,A[Parent]≥A[i]A[Parent]≥A[i]
|--min-heap property :对于所有除了根节点(root)的节点 i,A[Parent]≤A[i]A[Parent]≤A[i]
上图中的两个二叉树结构均是完全二叉树,但右边的才是满足max-heap property的二叉堆。
在以下的描述中,为了方便,我们还是用堆来说heapsort中用到的二叉堆。
2.2 一个初步的构想
有了这样一个看似简单的结构,我们可以产生以下初步构想来对数组A做排序:
堆 排 序 算 法



