• 经典算法系列三----堆排序


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

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

    首先是堆的概念。

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

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

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

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

    首先堆的插入操作:

    上代码:

     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 堆

  • 相关阅读:
    tensorflow源码资源,github
    数据挖掘实战
    Ansible--快速入门
    gRPC C#学习
    Linq之Expression高级篇(常用表达式类型)
    十大经典排序算法(动图演示)
    ASP.NET Web API 管道模型
    docker面试题和解答(一)
    浅谈委托和事件(一)
    面试题三
  • 原文地址:https://www.cnblogs.com/my-life/p/3489808.html
Copyright © 2020-2023  润新知