数据结构中的堆的定义:一组关键字序列称为堆,这组序列按照编号可以排成一颗完全二叉树,并且这棵完全二叉树必须满足双亲节点一定大于等于孩子节点的关键字值。(只考虑大顶堆)。堆一般用于数据的排序,并且只适用于顺序表,其他线性表或其他数据结构无法保证关键字序列能按照先后顺序排在一棵完全二叉树里。
给定一组关键字序列,要对它进行排序,最关键的是创建初始化堆,经过这个初始化的过程就可以使这组序列里面关键字最大的数据排到了堆顶,然后这时互换堆顶元素和堆最后一个元素的位置,让关键字最大的元素排到最后面。再将堆的元素个数减少1,并重新对减少容量后的堆进行筛选,将新堆中最大的元素排到堆顶的位置,接着互换新堆里堆顶和最后的元素。重复以上步骤,既可以将序列按照递增的方式排列在一棵完全二叉树内,当然原来这组序列是用顺序表存储的,自然该顺序表也会被排好序。
例:
下面的序列:D[9],D[0]不存储数据,用于保存中间值
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
49 | 38 | 65 | 97 | 76 | 13 | 27 | 49 |
算法如下:C语言
调整为堆的筛选函数:
哎,没说很清楚,请见谅void HeapAdjust(int D[],int s,int m)//D[]为序列数组,s刚开始指向最后一个节点的双亲节点,m始终指向要转成堆的二叉树的最后一个节点 {//下面提到的指针不是狭义的指针变量的那种指针,只是对某一元素的位置的指向,就是数据的下标 int j; D[0] = D[s];//先把s指针里的元素拿出来,用D[0]保存起来 for (j = 2 * s; j <= m ; j *= 2)//指向s指针的孩子节点,可以循环的话,j将指向原来j的左孩子节点,s将指向原来的j,D[s]与D[j] //仍为双亲节点与孩子节点的关系,当然j *= 2之后超出了节点范围,循环也就终止了。 { if(j < m && D[j] < D[j + 1]) j ++;//使j指向s孩子节点中较大的那个 if(D[0] >= D[j]) break;//此时s节点下面都满足堆的定义,循环终止 D[s] = D[j];//因为s的孩子节点比s大,所以将孩子节点升至s的位置,同时不能保证s一定比j下面的元素大,所以要循环找到合适的位置插入s,同时比s大的就应该上浮,这里说的s是D[0]里面保存的s,因为后面的s是会变的 s = j;//s指向j的位置,下一步j * = 2; } D[s] = D[0]; } 利用堆排序的函数: void HeapSort(int D[],int L)//L为数组的长度 { int i; for (i = L / 2;i > 0;i--) HeapAdjust(D,i,L);//创建初始化堆 for (i = L ;i> 1; i--) { D[0] = D[1]; D[1] = D[i]; D[i] = D[0]; HeapAdjust(D,1,i-1);//由于交换后打乱了原来堆的排序,所以需要重新筛选,使除最后一个元素之外的数据满足堆的定义,同时由于,只是第一个元素影响了堆的定义所以只需要调整D[1]的位置,所以是HeapAdjust(D,1,i-1)。 } }