
堆定义、建堆算法、堆的插入删除算法、堆排序算法
1、堆(heap)定义
堆其实就是二叉堆的简称,那何为二叉堆呢?
二叉堆其实是完全二叉树或者是近似二叉树。二叉堆必须满足两个特性:
- 父节点的键值必须大于等于或小于等于任何一个子节点的键值。
- 每个结点的左子树和右子树必须都是一个二叉堆(都是最大堆或最小堆)。
当父节点的键值总是大于等于任何一个子结点的键值时为最大堆。当父节点的键值总是小于等于任何一个子结点的键值时为最小堆。
2、建堆算法及排序算法
堆的存储:一般都用数组来表示堆,按从上到下,从左到右对节点进行编号,规律:i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2。
# includevoid main() { void Sort(int[]); int a[10] ={26,5,77,1,61,11,59,15,48,19}; Sort(a); } //堆排序算法 void Sort(int a[] ) { void Display(int [],char []); void Adjust(int [],int , int ) ; int n = 10;//取得数组总长度,及堆最大的序号 int temp = 0; Display(a, "Before sort : "); //输出函数,下面有定义 for(int i=n/2; i>0; i--) { Adjust(a, i - 1, n);//从倒数第二层的最后一个根节点开始调整堆 Display(a, "建立最大堆 : "); } printf("---------------------------------------n"); for (int i = n - 2; i >= 0; i--) {//这是堆排序的具体算法,思想是每次取出堆的最顶层根节点,即数组下标为0,然后与最后一个节点即i+1交换, //这样对于最大堆而言,随着最后一个结点的前移,最大值总是被留在后面。。循环过后就是倒序了。。 temp = a[i + 1];//取出最后一个元素 a[i + 1] = a[0];//取出第一个元素,即顶层根节点 a[0] = temp;//交换位置 Adjust(a, 0, i + 1);//调整堆 Display(a, "重建立最大堆 : "); } Display(a, "After sort : "); } /** * 调整最大堆的算法 * @param a 要调整的数组,即堆 * @param i 调整的根节点,即起始位置 * @param n 要调整的终止位置 */ void Adjust(int a[],int i, int n) { int j = 0; int temp = 0; temp = a[i]; //取出根节点 j = 2 * i + 1; //左孩子节点 while (j <= n - 1) { if (j < n - 1 && a[j] < a[j + 1])//比较左右孩子,取出较大的孩子 j++; if (temp >= a[j]) //如果根节点大于孩子节点则退出循环,不用调整 break; a[(j - 1) / 2] = a[j];//较大的孩子节点值赋值给根节点 j = 2 * j + 1;//继续寻找左孩子 } a[(j - 1) / 2] = temp;//将根节点赋值给最后一个空出来的节点 } //打印堆内容 void Display(int a[], char b[]) { printf("%sn",b); for (int i = 0; i < 10; i++) printf("%d ",a[i]); printf("n"); } </code></pre> 3、插入删除算法 * 堆的插入 每次插入都是将新数据放在数组最后。可以发现从这个新数据的父结点到根结点必然为一个有序的数列,现在的任务是将这个新数据插入到这个有序数据中——这就类似于直接插入排序中将一个数据并入到有序区间中,代码如下: // 新加入i结点 其父结点为(i - 1) / 2 void MinHeapInsert(int a[], int i) { int j, temp; temp = a[i]; j = (i - 1) / 2; //父结点 while (j >= 0 && i != 0) { if (a[j] <= temp) break; a[i] = a[j]; //把较大的子结点往下移动,替换它的子结点 i = j; j = (i - 1) / 2; } a[i] = temp; }* 堆的删除
按定义,堆中每次都只能删除第0个数据。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。调整时先在左右儿子结点中找最小的,如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。代码如下:
// 从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2 void MinHeapDelete(int a[], int i, int n) { int j, temp; temp = a[i]; j = 2 * i + 1; while (j < n) { if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的 j++; if (a[j] >= temp) break; a[i] = a[j]; //把较小的子结点往上移动,替换它的父结点 i = j; j = 2 * i + 1; } a[i] = temp; } //在最小堆中删除数 void MinHeapDeleteNumber(int a[], int n) { Swap(a[0], a[n - 1]); MinHeapFixdown(a, 0, n - 1); }




近期评论