zoukankan      html  css  js  c++  java
  • 经典算法系列三----堆排序

    花了些时间好好看了堆排序的内容,代码也敲了,现在来总结一下。

    为了说明白点,有些图片我就从网上截取了。

    首先是堆的概念。

    数据结构中的堆,又叫二叉堆

    一般用数组来表示堆得结构,或者说是把堆数组化。举个列子来看:

    这样就很清楚的看出了堆的储存结构。

    接着就是堆得操作处理了。

    首先堆的插入操作:

    上代码:

     1 void Heap_insert_fix(int a[],int n)
     2 {
     3      int temp;
     4      int i,j;
     5      i = n;
     6      j = (i - 1) / 2;/*父节点*/
     7      temp = a[i];/*记录插入的数据*/
     8      while(i != 0 && j >= 0)
     9      {
    10             if(a[j] <= temp)
    11                     break;
    12             a[i] = a[j];
    13             i = j;
    14             j = (i - 1) / 2;         
    15      }
    16      a[i] = temp;
    17 }

    上面代码解析:

    先明白这点,

    *父节点 i 子节点则为  j = i*2 +1
    *子节点 i 父节点则为  j = (i-1) / 2

    然后,插入的流程是,每次插入的数据都是在叶子节点,接着调整。直到满足堆得性质为止!

    然后是堆的删除操作:

    原理:每次删除根节点,就是用叶子节点覆盖根节点

    上代码:

     1 /*
     2 *删除操作
     3 *删除根结点,然后调整
     4 *从i节点开始,总数有n个 
     5 */ 
     6 void Heap_del_fix(int a[],int i,int n)
     7 {
     8     int j;
     9     int temp;
    10     temp = a[i];
    11     j = i * 2 + 1;/*子节点*/
    12     while(j < n)
    13     {
    14          if(a[j] > a[j+1] && j+1 < n)
    15          j++;
    16          if(a[j] >= temp)
    17          break;
    18          a[i] = a[j];
    19          i = j;
    20          j = i*2 + 1;    
    21     }
    22     a[i] = temp;
    23 }

    它的调用函数:

    1 /*
    2 *调用 
    3 */
    4 void Heap_sub(int a[],int n)
    5 {
    6      swap(&a[0],&a[n-1]);
    7      Heap_del_fix(a,0,n-1);         
    8 }


    有了上面这两个操作的基础,下面我就来写排序操作!

    思想:

    我们有了删除操作,每次删除根节点,而且我们知道根节点是数组中数值最小的那个值(我这里是小堆模式,大堆相反),这样我们一次次的删除,就形成了一个有序的序列。获取这个序列就是我们想要的堆排序结果。

    怎么获取,使用数组,看代码:

     1 /*
     2 *堆排序 
     3 */
     4 void Heap_sort(int a[],int n)
     5 {
     6      int i;
     7      for(i=n-1;i>=1;i--)
     8      {
     9           swap(&a[0],&a[i]);
    10           Heap_del_fix(a,0,i);
    11      }     
    12 }

    这样倒序便是我们的序列。

    至于如何将堆数组化,代码如下:

     1 /*
     2 *堆化数组  
     3 */
     4 void create_heap(int a[], int n)
     5 {
     6      int i;
     7      for(i=n/2-1;i>=0;i--)/*将数组转成堆,分开调整*/
     8      {
     9           Heap_del_fix(a,i,n);                     
    10      }      
    11 }

    这里的i=n/2-1;我们最好画图来看,这样理解会容易些。这部分我是在纸上实现的,读者可以自己尝试。

    对于swap函数,一个比较好的写法如下:

    1 void swap(int *i,int *j) 
    2 {
    3      *i = *i ^ *j;
    4      *j = *i ^ *j;
    5      *i = *i ^ *j; 
    6 }

    异或来交换两个数的写法,不需额外的变量。

    整体代码如下:

      1 /*
      2 *堆排序
      3 *丁洋
      4 *说明:堆用数组来表示,那么
      5 *父节点 i 子节点则为  j = i*2 +1
      6 *子节点 i 父节点则为  j = (i-1) / 2 
      7 * 
      8 */
      9 #include<stdio.h>
     10 #include<stdlib.h>
     11 
     12 void swap(int *i,int *j) 
     13 {
     14      *i = *i ^ *j;
     15      *j = *i ^ *j;
     16      *i = *i ^ *j; 
     17 }
     18 /*
     19 *插入操作
     20 *堆尾插入,然后调整 
     21 */
     22 void Heap_insert_fix(int a[],int n)
     23 {
     24      int temp;
     25      int i,j;
     26      i = n;
     27      j = (i - 1) / 2;/*父节点*/
     28      temp = a[i];/*记录插入的数据*/
     29      while(i != 0 && j >= 0)
     30      {
     31             if(a[j] <= temp)
     32                     break;
     33             a[i] = a[j];
     34             i = j;
     35             j = (i - 1) / 2;         
     36      }
     37      a[i] = temp;
     38 }
     39 /*
     40 *调用
     41 */
     42 void Heap_add(int a[],int n,int Num)
     43 {
     44      a[n] = Num;
     45      Heap_insert_fix(a,n); 
     46 }
     47 
     48 /*
     49 *删除操作
     50 *删除根结点,然后调整
     51 *从i节点开始,总数有n个 
     52 */ 
     53 void Heap_del_fix(int a[],int i,int n)
     54 {
     55     int j;
     56     int temp;
     57     temp = a[i];
     58     j = i * 2 + 1;/*子节点*/
     59     while(j < n)
     60     {
     61          if(a[j] > a[j+1] && j+1 < n)
     62          j++;
     63          if(a[j] >= temp)
     64          break;
     65          a[i] = a[j];
     66          i = j;
     67          j = i*2 + 1;    
     68     }
     69     a[i] = temp;
     70 }
     71 /*
     72 *调用 
     73 */
     74 void Heap_sub(int a[],int n)
     75 {
     76      swap(&a[0],&a[n-1]);
     77      Heap_del_fix(a,0,n-1);         
     78 }
     79 
     80 /*
     81 *堆化数组  
     82 */
     83 void create_heap(int a[], int n)
     84 {
     85      int i;
     86      for(i=n/2-1;i>=0;i--)/*将数组转成堆,分开调整*/
     87      {
     88           Heap_del_fix(a,i,n);                     
     89      }      
     90 }
     91 
     92 /*
     93 *堆排序 
     94 */
     95 void Heap_sort(int a[],int n)
     96 {
     97      int i;
     98      for(i=n-1;i>=1;i--)
     99      {
    100           swap(&a[0],&a[i]);
    101           Heap_del_fix(a,0,i);
    102      }     
    103 }
    104 int main()
    105 {
    106     int i;
    107     int a[] = {2,4,8,1};
    108     
    109     create_heap(a,4);
    110     Heap_sort(a,4);
    111     for(i=0;i<4;i++)
    112         printf("%d ",a[i]);
    113     printf("
    ");
    114     system("pause");
    115 }

    时间复杂度:

    由于每次重新恢复堆的时间复杂度为O(logN),共N - 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN)。二次操作时间相加还是O(N * logN)。故堆排序的时间复杂度为O(N * logN)。STL也实现了堆的相关函数,可以参阅《STL系列之四 heap 堆

  • 相关阅读:
    06 is和==的区别 encode()编码 decode()解码
    05 dic的增删改查 字典的嵌套 考试题dic.get()的相关使用
    03 编码 int ,bool,str的常用操作 主要讲str
    01 基本数据类型 变量 if语句
    04 列表的增删改查 常用方法 元祖 range
    02 while循环 格式化输出 运算符
    多校2 Harmonious Army hdu6598 网络流
    P3159 [CQOI2012]交换棋子 网络流
    P2172 [国家集训队]部落战争 最大流
    P2402 奶牛隐藏 网络流
  • 原文地址:https://www.cnblogs.com/my-life/p/3489808.html
Copyright © 2011-2022 走看看