飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 4110|回复: 4

二叉堆的实现 && 海量数据中堆的应用

[复制链接]

该用户从未签到

发表于 2011-2-7 10:48:50 | 显示全部楼层 |阅读模式
本帖最后由 Knuth学徒 于 2011-2-7 11:04 编辑

首先介绍下什么是堆,这里的堆指的是一种数据结构, 它的用途很广泛,包括排序,包括求N个元素中的最小的k个元素等。。。

      堆是一种树形结构,它是一棵完全二叉树,其中堆又分为最小堆和最大堆。

      最小堆:每个节点的键值都小于它的子树中的所有节点,所以最小堆的根节点的键值是整个堆中最小的。

      最大堆:每个节点的键值都大于它的子树中的所有节点,所以最大堆的根节点的键值是整个堆中最大的。

      于是,我们利用最大(小)堆的性质,可以在O(1)时间内得到n个元素中最小的值。

      现在,我以最小堆为例,介绍一下它的实现方式:

     用C++的类对heap进行封装:

     MinHeap.h文件:
  1.      class MinHeap{
  2.             private:
  3.                    int count;               //当前堆中的元素个数
  4.                    int val [MAX];     
  5.                      //用一维数组存储堆,其中节点 i 的左孩子是 2*i ,右孩子是 2*i + 1 ,根节点的键值是 val [1]
  6.                    void down(int position);     //最小堆的调整函数
  7.                    //void up(int position)        最大堆的调整函数
  8.             public:
  9.                   MinHeap();                       //构造函数
  10.                   void Insert(int data );       //将data插入堆中
  11.                   int  Top_Remove() ;        //返回堆顶元素,并把堆顶元素删去,重新调整堆
  12.                   bool  is_empty();            //返回堆是否为空,空则为 true,非空则为 false
  13.                   int size();                        //返回当前堆的大小——堆中元素的个数
  14.      };
复制代码
MinHeap.cpp 文件:
  1.     //这里的代码风格借鉴了 Nisy老师上次共享的 DebuggerVC代码中的风格,好的代码风格就应该模仿。
  2.      ///////////////////////////////////////////////////////////
  3.      //     函数名:MinHeap()
  4.      //     描述:构造函数,生成一个空堆
  5.      //     参数:None
  6.      //     前提:None
  7.      //     用法:MinHeap  对象名
  8.      ///////////////////////////////////////////////////////////
  9.      MinHeap :: MinHeap(){
  10.                count = 0;
  11.      }
  12.      
  13.      ///////////////////////////////////////////////////////////
  14.      //     函数名:is_empty()
  15.      //     描述:判断堆是否为空
  16.      //     参数:None
  17.      //     前提:None
  18.      //     用法:对象名.is_empty()
  19.      ///////////////////////////////////////////////////////////
  20.      bool  MinHeap :: is_empty(){
  21.                  return (count == 0);
  22.      }

  23.      ///////////////////////////////////////////////////////////
  24.      //     函数名:size()
  25.      //     描述:返回堆的大小
  26.      //     参数:None
  27.      //     前提:None
  28.      //     用法:对象名.size()
  29.      ///////////////////////////////////////////////////////////
  30.     int  MinHeap :: size(){
  31.                    return count;
  32.     }


  33.      ///////////////////////////////////////////////////////////
  34.      //     函数名:Insert()
  35.      //     描述:往堆中插入一个元素,并重新调整堆结构
  36.      //     参数:Data 表示插入元素的键值
  37.      //     前提:None
  38.      //     用法:对象名.Insert( xx )
  39.      ///////////////////////////////////////////////////////////
  40.      void MinHeap :: Insert( int data)
  41.      {
  42.                     val [++ count ] = data;       //先将元素放入堆尾
  43.                     int  p = count ;                    //调整用指针
  44.                     while(p > 1) {                      //在到达根节点之前
  45.                               if( val[ p>>1 ] > val [p] ){     //如果p的父节点键值>p,进行交换
  46.                                          int tmp = val [p] ;
  47.                                          val [p] = val [ p>>1 ] ;
  48.                                          val[p>>1] = tmp;
  49.                                          p >> = 1;                //指针向上移
  50.                              }
  51.                              else break;                          //如果没有破坏堆结构直接跳出。
  52.                     }
  53.       }

  54.               
  55.      ///////////////////////////////////////////////////////////
  56.      //     函数名:down(int position)
  57.      //     描述:将当前破坏了的堆 通过下降调整为堆
  58.      //     参数:position 表示需要调整的位置
  59.      //     前提:None
  60.      //     用法:None
  61.      ///////////////////////////////////////////////////////////   
  62.      void MinHeap :: down(int position)
  63.      {
  64.                while( (position << 1) <= size )   //如果位置还没下降到叶子节点
  65.                {
  66.                           int tmp = position << 1;
  67.                            if( ( position << 1 ) + 1 <= size && val[ (position<<1)+1] < val[tmp] )
  68.                                     //如果当前节点的右孩子小于当前节点的左孩子
  69.                                            tmp = ( possition<<1 )+ 1;
  70.                            if( val [tmp] < val[position] ) {
  71.                                     //如果当前节点的右孩子 小于当前节点
  72.                                     int temp = val [tmp] ;
  73.                                     val [tmp ] = val[position];
  74.                                     val [position ] = temp;
  75.                                     position = tmp;
  76.                          }
  77.                          else break;
  78.                }
  79.      }


  80.      ///////////////////////////////////////////////////////////
  81.      //     函数名:Top_Remove()
  82.      //     描述:返回堆顶元素,并删除之,再重新调整堆结构
  83.      //     参数:None
  84.      //     前提:None
  85.      //     用法:对象名.Top_Remove()
  86.      ///////////////////////////////////////////////////////////
  87.      int MinHeap :: Top_Remove(){
  88.                    int temp = val[1] ; //保存堆顶元素
  89.                    val [1] = val [ count];
  90.                    val [count ] = temp;
  91.                    count --;
  92.                    down(1);  调整整个堆
  93.                    return val[count + 1];
  94.     }
复制代码
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    +            以上给出了一个最小堆的具体实现方式,下面给出堆的实际应用
    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

     一、堆排序:
             因为我们可以在O(1)时间复杂度内求出n个元素中的最值,于是我们就可以用它来排序。

             思想是,将待排序元素 a[MAX] 一一插入到这个堆中,那么,堆顶元素永远是最小的,于是,我们把堆顶元素放到最后,然后用新插入的元素来代替,接着重新调整堆,这样逐步迭代后,我们就可以得到一个 从大到小的有序序列。
             时间复杂度: T(n) = N*O(logN) 插入时间  = O(NlogN)
            与快速排序算法的时间复杂度都很高,所以堆排序也是一种很好的稳定排序方式,它不太会像快排那样退化成O(n^2)

    二、求出海量数据中的最小的n个元素——这里要用到最大堆,和最小堆的实现方式正好相反。

           如果有海量的数据,求前n个元素并不简单,如果要先排序的话,时间复杂度非常高,这里就可以用到堆的思想。

           对海量数据进行一次线性的扫描,然后把每个元素插入最大堆中,如果下一个元素小于堆顶元素,那么把堆顶元素删去,用下一个元素来代替,然后重新调整堆,再继续插入。

        这样,最后堆中的n个元素就是最小的n个元素。

  +++++++++++++++++++++++++++后记++++++++++++++++++++++++++

       堆还有更多的用途,我这里只起到一个抛砖引玉的作用。      

       转载时请注明出处。

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
PYG19周年生日快乐!

该用户从未签到

 楼主| 发表于 2011-2-7 11:14:17 | 显示全部楼层
我上面说的堆 别和程序中的堆栈搞起来。。。一个是数据结构ADT,一个是系统的内存空间
PYG19周年生日快乐!

该用户从未签到

发表于 2011-3-6 10:59:39 | 显示全部楼层
飘云真是太好了
PYG19周年生日快乐!
  • TA的每日心情
    难过
    7 天前
  • 签到天数: 11 天

    [LV.3]偶尔看看II

    发表于 2011-3-10 14:11:46 | 显示全部楼层
    人才!不去分析XX,真是浪费了,你懂的~~
    PYG19周年生日快乐!

    该用户从未签到

     楼主| 发表于 2011-3-10 19:47:32 | 显示全部楼层
    回复 4# 飘云


        谢谢老大赞赏…… 最近比较忙, 抽空看了几个破解视频, 觉得破解也是很有意思的。
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表