堆排序引入了另外一种算法设计技巧:使用一种称之为堆的数据结构来进行信息管理。(因为形式类似二叉树所以被称为二叉堆)二叉堆是一个数组,它可以被看作一个近似的完全二叉树,树上的每个节点对应一个数组元素。标识堆的数组有两个属性:lenngth(数组元素个数),heap-size(表示有多少个堆元素存储在数组中)。也就是说数组length都可能存有数据,但是只有[0-heap-size]存储的是有效数据。这里,[0<=heap-size<=length]。树的根节点为A[i],得出它的父节点坐标为:(i/2)。左孩子为:(2*i)。右孩子为:(2*i + 1)。
关于堆的几个性质。
1>二叉堆可以分为两种形式,最大堆和最小堆。在最大堆中,除了根节点外,其他节点需要满足:A[PARENT(i)] >= A[i]。PARENT(i)表示父节点坐标。同理最小堆满足:A[PARENT[i]] <= A[i]。
2>两种堆使用场景不同,最小堆通常用于构造优先队列。使用的时候需要根据实际情况选择。
堆的几个基本操作:
.MAX-HEAPIFY:时间复杂度O(lgn),用于维护堆的基本性质,即最大以及最小。
.BUILD-MAX-HEAP:功能是从无序输入数据数组中构建一个最大堆。
.HEAPSORT过程:功能是对一个数组进行原址排序。
.MAX-HEAP-INSERT/HEAP-EXTERACT-MAX/HEAP-INCREASE-KEY:利用堆构建一个优先队列。
(一).MAX-HEAP操作
MAX-HEAP输入为一个需要排序的数组以及一个数组下标(index)。在进行MAX-HEAP的时候前提是假设只有index及其只节点可能存在比index节点大的情况。这个时候我们让A[index]的值逐步下降的方式,重塑数组使得满足最大堆的性质。
算法描述:
int is_leaf(size_t nHeapSize, int nIndex) { return (nIndex >= (LEAF_START(nHeapSize)) ? 1 : 0); } void max_heapify(int* pnArray, size_t nHeapSize, int nIndex) { int nLindex = LEFT_INDEX(nIndex); int nRindex = RIGHT_INDEX(nIndex); int nLargestindex = nIndex; if((nLindex <= nHeapSize) && (pnArray[nLindex] > pnArray[nIndex])) { nLargestindex = nLindex; } else { nLargestindex = nIndex; } if((nRindex <= nHeapSize) && (pnArray[nRindex] > pnArray[nLargestindex])) { nLargestindex = nRindex; } if(nLargestindex != nIndex) { _exchange_val(pnArray, nLargestindex, nIndex); max_heapify(pnArray, nHeapSize, nLargestindex); } return; }
交换数据: #define PARENT_INDEX(idx) (idx / 2) #define LEFT_INDEX(idx) (2 * (idx + 1) - 1) #define RIGHT_INDEX(idx) (2 * (idx + 1)) #define LEAF_START(heap_size) (heap_size / 2) _inline void max_heapify_exchange(int* pnArray, int nIndex1, int nIndex2) { int nTempVal = pnArray[nIndex1]; pnArray[nIndex1] = pnArray[nIndex2]; pnArray[nIndex2] = nTempVal; return; }(二).BUILD-MAX-HEAP操作
void build_max_heap(int* pnArray, size_t nHeapSize) { int nIndex = 0; if(pnArray == NULL || nHeapSize == 0) { return; } for(nIndex = (nHeapSize / 2) - 1; nIndex >= 0 ; nIndex--) { max_heapify(pnArray, nHeapSize, nIndex); } return; }现在写一个简单的测试程序验证下前面的思路。
#define LEAF_START(heap_size) (heap_size / 2) int main(int argc, char** argv) { int anTest[] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7}; int nIndex = 0; build_max_heap( anTest, sizeof(anTest) / sizeof(anTest[0])); for(nIndex = 0; nIndex < LEAF_START(sizeof(anTest) / sizeof(anTest[0])); nIndex++) { assert(anTest[nIndex] >= anTest[LEFT_INDEX(nIndex + 1) - 1] && anTest[nIndex] >= anTest[RIGHT_INDEX(nIndex + 1) - 1]); } return (0); }三).MAX-SORT操作
void heap_sort(int* pnArray, size_t nLength) { int nIndex = 0; if(pnArray == NULL || nLength == 0) { return (0); } build_max_heap(pnArray, nLength); for(nIndex = nLength - 1; nIndex >=1; nIndex--) { _exchange_val(pnArray, 0, nIndex); max_heapify(pnArray, nIndex -1, 0); } }
int main(int argc, char** argv) { int anTest[] = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7}; int nIndex = 0; build_max_heap(anTest, sizeof(anTest) / sizeof(anTest[0])); heap_sort( anTest, sizeof(anTest) / sizeof(anTest[0])); for(nIndex = 1; nIndex < (sizeof(anTest) / sizeof(anTest[0])) ; nIndex++) { assert(anTest[nIndex - 1] <= anTest[nIndex]); printf("%d ", anTest[nIndex - 1]); } printf("\n"); return (0); }
再把前面提到的测试模块跑一遍,基本可以排除基本的错误了,注意,堆排序算法还没有进行抽象封装,实际应用还需要进一步完善它。
——————The End。