首先,看一下堆的定义;
n个元素的序列{k1,k2,…,kn}当且仅当满足下列关系之一时,称之为堆。
情形1:ki <= k2i 且ki <= k2i+1 (最小化堆或小顶堆)
情形2:ki >= k2i 且ki >= k2i+1 (最大化堆或大顶堆)
其中i=1,2,…,n/2向下取整;
该排序的思想是;首先建立二叉堆,这个阶段花费O(N)时间,然后我们执行N次deleteMin操作。按照顺序,最小的元素先离开堆,通过将这些元素记录到第二数组,最后在将数组拷贝回来,得到N个元素的顺序,由于每个deleteMIn花费时间O(logN), 因此,总的运行时间是O(NlogN). 优先队列,可以用于O(NlogN)时间排序, 基于该思想的算法叫做堆排序。
该算法的主要问题是使用一个附加的数组,一次存储需求增加一倍,为了回避使用第二个数组的,可以在每次deleteMin之后,堆缩减1,因此,位于堆中左后的单元,可以存储刚刚存放删除的元素。使用这种策略,该数组将以递减的的顺序包含这些元素.
java代码的例子:
public class HeapSort { public static void main(String[] args) throws Exception{ Integer [] a = new Integer[]{32,12,345,5,66,23}; heapSort(a); Arrays.stream(a).forEach(System.out::println); } /** * Internal method for heapSort that is used in deleteMax and buiildHeap * @param i the position from which to percolate down * @param n the binary size of the binary heap */ public static void percDown(Integer[] a,int i, int n){ int child; int temp; for(temp= a[i]; leftChild(i) < n; i = child){ child = leftChild(i); if(child != n-1 && a[child] < a[child+1]) child++; if(temp < a[child]) a[i] = a[child]; else break; } a[i] = temp; } private static int leftChild(int i){ return 2 * i + 1; } public static void heapSort(Integer[] a){ /**初始化堆的时候是对所有的非叶子结点进行筛选。 最后一个非终端元素的下标是[n/2]向下取整,所以筛选只需要从第[n/2]向下取整个元素开始,从后往前进行调整。 */ for(int i = a.length/2-1; i>=0; i--){ //buildHeap percDown(a,i,a.length); } for(int i=a.length-1; i > 0; i--){ swapReferences(a,0,i); //deleteMax percDown(a,0,i); } } //deleteMax public static void swapReferences(Integer[] a, int i, int n){ Integer temp=a[i]; a[i] = a[n]; a[n] = temp; } }
堆排序方法对记录数较少的文件并不值得提倡,但对n较大的文件还是很有效的。因为其运行时间主要耗费在建初始堆和调整建新堆时进行的反复“筛选”上
堆排序在最坏的情况下,其时间复杂度也为O(nlogn)。相对于快速排序来说,这是堆排序的最大优点。
时间: 2024-10-09 16:50:31