完全二叉树是效率很高的数据结构,堆是一种完全二叉树或者近似完全二叉树,所以效率同样极高。目前十分常用的排序算法、Dijkstra算法、Prim算法等都要用堆才能优化。
堆排序是一种选择排序算法,与原序列的初始排列次序无关,即最好、最坏和一般情况排序的时间复杂度不变,均为O(nlgn)。而且,堆排序只需要一个记录记录元素大小的辅助空间(供交换使用),故空间复杂度为O(1)。正由于堆排序不仅时间复杂度小,而且空间复杂度O(1)也是最小,所以是用于排序的最佳选择。
堆的定义
n个元素序列 { k0,k1,..., kn } 当且仅当满足下列条件之一时,称之为堆(其中 i = 0, 1, ..., n/2 向下取整)。
- ki <= k2i+1 且 ki <= k2i+2(最小堆)
- ki >= k2i+1 且 ki >= k2i+2(最大堆)
堆排序实现
方法1:
利用最小堆性质实现堆排序
/*交换数组中两个元素的值*/ void swap(int *arr, int a, int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } /*(小根)堆调整*/ void minHeapify(int *arr, int heapSize, int curNode) { /*找出当前节点和其左右节点三者的最小值*/ int minNode = curNode; int left = curNode * 2 + 1; int right = curNode * 2 + 2; if(left < heapSize && arr[left] < arr[minNode]) //左孩子存在且较小 minNode = left; if(right < heapSize && arr[right] < arr[minNode]) //右孩子存在且较小 minNode = right; if(minNode != curNode) //当前节点不是最小则需要交换 { swap(arr, minNode, curNode); minHeapify(arr, heapSize, minNode); //子节点需重新调整 } } /*构建堆*/ void createHeap(int *arr, int heapSize) { for(int i = heapSize/2 - 1; i >= 0; i--) //从最后一个非叶节点开始 { minHeapify(arr, heapSize, i); } } /*(最小)堆排序 *思想: *1、构建最小堆 *2、从最后一个元素开始只第二个元素将依次与首元素交换 *3、全局调整堆
*上面步骤2和步骤3将所有元素都与首元素交换,然后进行全局范围的堆调整,利用最小堆的性质实现将所有元素按从小到大的方式排序 */ void heapSort(int *arr, int length) { createHeap(arr, length); //构建堆 for(int i = length-1; i > 0; i--) { swap(arr, 0, i); //与首元素交换 minHeapify(arr, length, 0); //调整堆 } }
方法2
利用最大堆性质实现堆排序
/*交换数组中两个元素的值*/ void swap(int *arr, int a, int b) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } /*(大根)堆调整*/ void maxHeapify(int *arr, int heapSize, int curNode) { /*找出当前节点和其左右节点三者的最小值*/ int maxNode = curNode; int left = curNode * 2 + 1; int right = curNode * 2 + 2; if(left < heapSize && arr[left] > arr[maxNode]) //左孩子存在且较小 maxNode = left; if(right < heapSize && arr[right] > arr[maxNode]) //右孩子存在且较小 maxNode = right; if(maxNode != curNode) //当前节点不是最小则需要交换 { swap(arr, maxNode, curNode); maxHeapify(arr, heapSize, maxNode); //子节点需重新调整 } } /*构建堆*/ void createHeap(int *arr, int heapSize) { for(int i = heapSize/2 - 1; i >= 0; i--) //从最后一个非叶节点开始 { maxHeapify(arr, heapSize, i); } } /*(最大)堆排序 *思想: *1、构建最大堆 *2、从最后一个元素开始只第二个元素将依次与首元素交换 *3、进行数组中当前元素前面的所有元素的局部调整堆 *上面步骤2和步骤3将所有元素都与首元素交换,将局部范围内的最大值沉到后面,然后进行当前元素前面所有元素的局部范围内的堆调整 *即在数组A中,当前元素索引为i,将A[i]与A[0]交换,将A[0,1,...,i]中最大的值放到A[i],然后进行A[0,1,...,i-1]堆调整, *将A[0,1,...,i-1]内的最大值放到A[0]中,待下一次交换使用,实现将局部的最大值依次沉到数组后面,完成排序 */ void heapSort(int *arr, int length) { createHeap(arr, length); //构建堆 for(int i = length-1; i > 0; i--) { swap(arr, 0, i); //与首元素交换 maxHeapify(arr, i, 0); //调整堆 } }