zoukankan      html  css  js  c++  java
  • 数据结构——堆

    一、堆的定义与基本操作

      堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子结点的值。其中,如果父亲结点的值大于或等于孩子结点的值,那么称这样的堆为大顶堆,这时候每个结点的值都是以它为根结点的子树的最大值;如果父亲结点的值小于或等于孩子结点的值,那么称这样的堆为小顶堆,这时候每个结点的值都是以它为根结点的子树的最小值。

      对完全二叉树来说,比较简洁的实现方法是使用数组来存储完全二叉树。这样结点按层序存储于数组中,其中第一个结点将存储在数组的 1 号位,并且数组 i 号位表示的结点的左孩子就是 2i 号位,而右孩子则是 (2i+1) 号位。于是定义如下:

    1 #define maxn 100
    2 // heap 为堆,n 为元素个数 
    3 int heap[maxn], n=10; 

      下面介绍向下调整方法:总是将当前结点 V 与它的左右孩子比较(如果有的话),假如孩子中存在权值比结点 V 的权值大的,就将其中权值最大的那个孩子结点与结点 V 交换;交换完毕后继续让结点 V 和孩子比较,直到结点 V 的孩子的权值都比结点 V 的权值小或是结点 V 不存在孩子结点。代码如下:

     1 // 对 heap 数组在 [low,high] 范围进行向下调整
     2 // 其中 low 为欲调整结点的数组下标,high 一般为最后一个元素的数组下标
     3 void downAdjust(int low, int high) {
     4     int i=low, j=2*i;        // i 指向当前交换节点,j指向左孩子 
     5     while(j <= high) {
     6         // 若右孩子存在,且右孩子大于左子树 
     7         if(j+1 <= high && heap[j+1] > heap[j]) {
     8             j=j+1;            // 右孩子更大 
     9         }
    10         // 如果最大权值比欲调整结点大 
    11         if(heap[j] > heap[i]) {
    12             swap(&heap[j], &heap[i]);    // 交换 
    13             i = j;                        // 保持 i 指向欲调整结点,j 指向左孩子 
    14             j = 2*i;
    15         } else {
    16             break;                        // 孩子结点比根结点小,调整结束 
    17         }
    18     }
    19 }

      那个建堆的过程也就很容易了。假设序列中元素的个数为 n,数组下标在 $left [ 1,left lfloor frac{n}{2} ight floor ight ]$ 范围内的结点都是非叶子结点。于是可以从 $left lfloor frac{n}{2} ight floor$ 号开始倒着枚举结点,对每个遍历到的结点 i 进行 [i,n] 范围的调整。代码如下:

    1 // 建堆
    2 void createHeap() {
    3     int i;
    4     for(i=n/2; i>=1; --i) {                // 倒着枚举 
    5         downAdjust(i, n);                // 向下调整 
    6     }
    7 }

      

      另外,如果要删除堆中的最大元素(也就是删除堆顶元素),并让其仍然保持堆的结构,那么只需要最后一个元素覆盖栈顶元素,然后对根结点进行调整即可。代码如下:

    1 // 删除堆顶元素
    2 void deleteTop() {
    3     heap[1] = heap[n--];                // 用最后一个元素覆盖栈顶元素,并让元素个数-1
    4     downAdjust(1, n);                    // 向下调整栈顶元素 
    5 } 

      那么,如果想要往堆里添加一个元素,可以把想要添加的元素放在数组最后,然后进行向下调整操作。向上调整总是把欲调整结点与父亲结点比较,如果权值比父亲结点大,那么就交换其与父亲结点,这样反复比较,直到到达堆顶或是父亲结点的权值较大为止。代码如下:

     1 // 对 heap 数组在 [low,high] 范围进行向上调整
     2 // 其中 low 为欲调整结点的数组下标,high 一般为最后一个元素的数组下标
     3 void upAdjust(int low, int high) {
     4     int i=high, j=i/2;                     // i 为欲调整结点,j 为其父亲
     5     while(j >= low) {
     6         // 如果父亲结点小于欲调整结点 
     7         if(heap[j] < heap[i]) {
     8             swap(&heap[j], &heap[i]);    // 交换 
     9             i = j;                        // 保持 i 指向欲调整结点,j 指向父亲结点 
    10             j = i/2;
    11         } else {
    12             break;                        // 孩子结点比根结点小,调整结束 
    13         }
    14     } 
    15 }

      在此基础上就很容易实现添加元素的代码了:

    1 // 添加元素 x
    2 void insert(int x) {
    3     heap[++n] = x;                        // 让元素个数+1,然后将数组末位赋值为 x
    4     upAdjust(1, n);                     // 向上调整插入的结点 
    5 } 

    二、堆排序

      堆排序是指使用堆结构对一个序列进行排序。此处讨论递增排序的情况。

      可以倒着遍历数组,假设当前遍历到 i 号位,那么堆顶元素与 i 号位的元素交换,接着在 [1,i-1] 范围内对栈顶元素进行一次向下调整即可。代码如下:

    1 // 堆排序
    2 void heapSort() {
    3     int i;
    4     for(i=n; i>1; --i) {                // 倒着枚举 
    5         swap(&heap[i], &heap[1]);        // 交换 heap[i] 与堆顶
    6         downAdjust(1, i-1);                // 调整堆顶 
    7     }
    8 }

      完整测试代码如下:

      1 /*
      2   3 */
      4 
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <math.h>
      8 #include <stdlib.h>
      9 #include <time.h>
     10 #include <stdbool.h>
     11 
     12 #define maxn 100
     13 // heap 为堆,n 为元素个数 
     14 int heap[maxn], n=10; 
     15 
     16 // 交换 a,b
     17 void swap(int* a, int* b) {
     18     int temp = *a;
     19     *a = *b;
     20     *b = temp;
     21 } 
     22 
     23 // 对 heap 数组在 [low,high] 范围进行向下调整
     24 // 其中 low 为欲调整结点的数组下标,high 一般为最后一个元素的数组下标
     25 void downAdjust(int low, int high) {
     26     int i=low, j=2*i;        // i 指向当前交换节点,j指向左孩子 
     27     while(j <= high) {
     28         // 若右孩子存在,且右孩子大于左子树 
     29         if(j+1 <= high && heap[j+1] > heap[j]) {
     30             j=j+1;            // 右孩子更大 
     31         }
     32         // 如果最大权值比欲调整结点大 
     33         if(heap[j] > heap[i]) {
     34             swap(&heap[j], &heap[i]);    // 交换 
     35             i = j;                        // 保持 i 指向欲调整结点,j 指向左孩子 
     36             j = 2*i;
     37         } else {
     38             break;                        // 孩子结点比根结点小,调整结束 
     39         }
     40     }
     41 } 
     42 
     43 // 建堆
     44 void createHeap() {
     45     int i;
     46     for(i=n/2; i>=1; --i) {                // 倒着枚举 
     47         downAdjust(i, n);                // 向下调整 
     48     }
     49 } 
     50 
     51 // 删除堆顶元素
     52 void deleteTop() {
     53     heap[1] = heap[n--];                // 用最后一个元素覆盖栈顶元素,并让元素个数-1
     54     downAdjust(1, n);                    // 向下调整栈顶元素 
     55 } 
     56 
     57 // 对 heap 数组在 [low,high] 范围进行向上调整
     58 // 其中 low 为欲调整结点的数组下标,high 一般为最后一个元素的数组下标
     59 void upAdjust(int low, int high) {
     60     int i=high, j=i/2;                     // i 为欲调整结点,j 为其父亲
     61     while(j >= low) {
     62         // 如果父亲结点小于欲调整结点 
     63         if(heap[j] < heap[i]) {
     64             swap(&heap[j], &heap[i]);    // 交换 
     65             i = j;                        // 保持 i 指向欲调整结点,j 指向父亲结点 
     66             j = i/2;
     67         } else {
     68             break;                        // 孩子结点比根结点小,调整结束 
     69         }
     70     } 
     71 }
     72 
     73 // 添加元素 x
     74 void insert(int x) {
     75     heap[++n] = x;                        // 让元素个数+1,然后将数组末位赋值为 x
     76     upAdjust(1, n);                     // 向上调整插入的结点 
     77 } 
     78 
     79 // 堆排序
     80 void heapSort() {
     81     int i;
     82     for(i=n; i>1; --i) {                // 倒着枚举 
     83         swap(&heap[i], &heap[1]);        // 交换 heap[i] 与堆顶
     84         downAdjust(1, i-1);                // 调整堆顶 
     85     }
     86 }
     87 
     88 // 输出堆 
     89 void print() {
     90     int i; 
     91     for(i=1; i<=n; ++i) {                
     92         printf("%d ", heap[i]);
     93     }
     94     printf("
    ");    
     95 } 
     96 
     97 int main() {
     98     int i;
     99     for(i=1; i<=n; ++i) {
    100         scanf("%d", &heap[i]);
    101     } 
    102     createHeap();                        // 建堆 
    103     print();                            // 输出堆 
    104     deleteTop();                        // 删除栈顶元素 
    105     print();                            // 输出堆 
    106     insert(99);                            // 插入99
    107     print();                            // 输出堆 
    108     heapSort();                            // 堆排序
    109     print();                            // 输出排序结果 
    110 
    111     return 0;
    112 }
  • 相关阅读:
    arcgis 图幅结合图表工具
    Geoprocessor clip
    Spring Boot总结,核心功能,优缺点
    代码下载python 简体和繁体的转换
    电话错误fmxl Missing handler script
    nullnull使用ps绘制像word里面的箭头
    nullnulljava把过滤掉的 % 号拿回来
    问题总结两天来两场实习面试(中科创达、华为)
    模板拷贝【便利贴】Struts性能优化
    匹配优先级Spring攻略学习笔记(3.04)指定Aspect优先级
  • 原文地址:https://www.cnblogs.com/coderJiebao/p/Algorithmofnotes25.html
Copyright © 2011-2022 走看看