Srilm 阅读文档6

取自 自然语言处理百科

跳转到: 导航, 搜索

IntervalHeap.h IntervalHeap.cc

文档作者:jianzhu

创立时间:08.08.30


1、基本类


   这两个文件主要以模板方式定义了一个区间堆(IntervalHeap)。该区间堆是一个最大值堆和最小值堆的结合。通过在堆的每个节点上保存两个元素:left、right。其中由left组成的部分构成了一个最小值堆,由right组成的部分构成了一个最大值堆。底层的数据存储采用Array类来管理。
  

IntervalHeap类

  该类通过区间的方式将最大值堆和最小值堆有机地结合在一起。 结合堆的特性该类提供了以下函数。

   a) 构造函数
   b) 析构函数
   c) top_min函数
   d) top_max函数
   e) push函数
   f) pop_min函数
   g) pop_max函数
   h) size函数
   i) empty函数
  

2、函数功能解释


a) 构造函数

<src>
0  template<class T, class Less, class Greater, class Equal>
1  IntervalHeap<T, Less, Greater, Equal>::IntervalHeap(int IntervalHeapSize)
2    :heap(0, IntervalHeapSize / 2 + IntervalHeapSize % 2), CurrentSize(0)
3  {
4  }
</src> 
   功能:用于对当前对象初始化,同时根据传入的堆大小分配合适的空间。
  
   细解:
   第1行的区间堆模板显示,要生成一个合理的区间堆对象需要传入三个类型
        T     --- 要存储的数据类型
        Less   --- 函数对象,对T类型的数据比较是否符合小于关系
        Greater --- 函数对象,对T类型的数据比较是否符合大于关系
        Equal    --- 函数对象,对T类型的数据比较是否符合等于关系
   第2行使用成员初始化列表的方式调用Array类的构造函数对heap进行初始化。
   heap用于存储实际的数据。同时将堆已经保存的元素数初始化为0。
  
   注:在调用Array构造函数对heap初始化时,传入的要构造的Array大小为
       IntervalHeapSize / 2 + IntervalHeapSize % 2
   这是因为heap中的每个单元保存了两个元素,一个属于最大值堆,一个属于
   最小值堆。同时加上IntervalHeapSize % 2是为了防止奇数个元素的情况。
   这时heap最后一个单元中只存储了一个有效元素。

b) 析构函数

<src>
0  template<class T, class Less, class Greater, class Equal>
1  IntervalHeap<T, Less, Greater, Equal>::~IntervalHeap()
2  {
3  }
</src>
   功能:用于对当前对象进行内存回收等操作。
  
   注:由于储存数据的成员为Array类,因此其具备自我管理的功能,不需要
   在析构函数里进行其他操作。
  

c) top_min函数

<src>
0  template<class T, class Less, class Greater, class Equal>
1  T IntervalHeap<T, Less, Greater, Equal>::top_min()
2  {
3    assert(CurrentSize != 0);
4       
5       return heap[1].left;
6  }
</src>
   功能:用于获得当前储存在堆中的最小的元素
  
   注:  第3行通过调用assert函数判断当前堆是否非空。若非空则直接返回根
   节点,即数组第一个储存数据元素位置的left成员。assert函数一般用于
   debug模式,因此实际使用该函数的时候需要通过调用empty函数判断当前堆是
   否非空。
       由于当前堆以区间方式定义了最小值堆和最大值的结合,其中left成员对
   应最小值堆,right成员对应最大值堆,因此获取当前堆中储存的最小值元素时
   直接返回heap[1].left。
  

d) top_max函数

<src>
0  template<class T, class Less, class Greater, class Equal>
1  T IntervalHeap<T, Less, Greater, Equal>::top_max()
2  {
3    assert(CurrentSize != 0);
4       
5       return heap[1].right;
6  }
</src>
   功能:用于获得当前储存在堆中的最大的元素
  

e) push函数

<src>
0  template<class T, class Less, class Greater, class Equal>
1  void IntervalHeap<T, Less, Greater, Equal>::push(const T& x)
2  {
3    /* Insert x into the interval heap.
4    * handle CurrentSize < 2 as a special case
5    */
6
7    if (CurrentSize < 2) {
8      if (CurrentSize) { // CurrentSize is 1
9        if ( less(x, heap[1].left) )
10         heap[1].left = x;
11       else
12         heap[1].right = x;
13     }
14     else { // CurrentSize is 0
15       heap[1].left = x;
16       heap[1].right = x;
17     }
18     CurrentSize++;
19     return;
20   }
21
22   // CurrentSize >= 2
23   int LastNode = CurrentSize / 2 + CurrentSize % 2;
24   Boolean minHeap; // true iff x is to be inserted in the min heap part of the interval heap
25   if (CurrentSize % 2) {
26     // odd number of elements
27     if ( less(x, heap[LastNode].left) )
28       // x will be an interval left end
29       minHeap = true;
30     else
31       minHeap = false;
32   }
33   else { // even number of elements
34     LastNode++;
35     if ( less(x, heap[LastNode / 2].left) || equal(x, heap[LastNode / 2].left) )
36       minHeap = true;
37     else
38       minHeap = false;
39   }
40
41   // make sure memory for lastNode gets allocated
42   // if not, heap[LastNode].xxx = heap[LastNode/2].xxx may cause problem if LastNode triggers  memory allocation
43   T &dummy = heap[LastNode].left;
44 
45   if (minHeap) {// fix min heap of interval heap
46     // find place for x
47     // i starts at LastNode and moves up tree
48     int i = LastNode;
49
50     while (i != 1 && (less(x, heap[i / 2].left) || equal(x, heap[i / 2].left)) ) {
51       // cannot put x in heap[i]
52       // move left element down
53       heap[i].left = heap[i / 2].left;
54       i /= 2; // move to parent
55     }
56     heap[i].left = x;
57     CurrentSize++;
58     if (CurrentSize % 2)
59       // new size is odd, put dummy in LastNode
60       heap[LastNode].right = heap[LastNode].left;
61   }
62   else {// fix max heap of interval heap
63     // find place for x
64     // i starts at LastNode and moves up tree
65     int i = LastNode;
66     while (i != 1 && (greater(x, heap[i / 2].right) || equal(x, heap[i / 2].right)) ) {
67       // cannot put x in heap[i]
68       // move right element down
69       heap[i].right = heap[i / 2].right;
70       i /= 2; // move to parent
71     }
72     heap[i].right = x;
73     CurrentSize++;
74     if (CurrentSize % 2)
75       // new size is odd, put dummy in LastNode
76       heap[LastNode].left = heap[LastNode].right;
77   }
78   return;
79 }
</src>
   功能:用于将新的数据插入到当前堆中,同时维持最大值和最小值堆的特性。
  
   细解:
   第7-20行对堆中保存的元素数小于2的情况做特殊处理。当堆中已经保存了1个元素
   的时候,将x与其做比较,若小于其,则将x储存到left部分,否则将其储存到right
   部分。当堆中没有元素时,将heap[1]的left和right部分均设为x。上述两种情况下
   构建的堆均符合left部分构成最小值堆,right部分构成最大值堆的特性。
   第23-79行对堆中已有元素数大于2的情况做处理。
   第23行首先获得最大最小值堆的最后一个节点位置。
   第25-39行判断当前元素应该储存在最大值堆中还是应该储存在最小值堆中。当堆中
   已有元素数为奇数情况时,说明最后一个节点储存了一个元素,通过将x与其比较来
   判断是将该元素存储在最大值堆还是最小值堆中。当堆中已有的元素数为偶数时,说
   明当前元素需要被存储到一个新的节点上,因此通过将最后一个节点位置增一之后除
   以2得到其父节点,然后判断当前元素是否小于等于父节点的left成员,若小于等于,
   说明当前元素需要存储在最小值堆中,否则将其存储在最大值堆中。(注:将一个新
   的元素插入到区间堆中的时候,有3种情况:i、当前元素小于等于与其比较的left元
   素,这时需要将当前元素保存到最小值堆中,同时对最小值堆进行堆化操作;ii、当
   前元素大于left元素但小于right元素,这时需要将其插入到最大值堆中,不需要进行
   堆化操作;iii、当前元素大于等于right元素,这时需要将其插入到最大值堆中,同
   时需要对最大值堆进行堆化操作。
   第45-61行对最小值堆进行堆化操作,同时将元素x保存到相应的位置。当当前元数小于
   等于其父节点的left成员时,将父节点的left成员保存到当前节点left成员中,直到当
   前元素大于父节点的left成员位置,这时将当前元素保存到相应节点的left成员中。同
   时将堆保存的元素数增1,并在堆中保存的元素数为奇数的情况下,将堆的最后一个节点
   的right成员设为left成员。
   第65-77行对最大值堆进行堆化操作,同时将元素x保存到相应的位置。当当前元数大于
   等于其父节点的right成员时,将父节点的right成员保存到当前节点righ成员中,直到当
   前元素小于父节点的right成员位置,这时将当前元素保存到相应节点的right成员中。同
   时将堆保存的元素数增1,并在堆中保存的元素数为奇数的情况下,将堆的最后一个节点
   的left成员设为right成员。
  

f) pop_min函数

<src>
0  template<class T, class Less, class Greater, class Equal>
1  void IntervalHeap<T, Less, Greater, Equal>::pop_min()
2  {
3    // Set x to min element and delete
4    // min element from interval heap.
5    // check if interval heap is empty
6    if (CurrentSize == 0)
7      assert(0);
8
9    T x;
10   x = heap[1].left; // min element
11   // restructure min heap part
12   int LastNode = CurrentSize / 2 + CurrentSize % 2;
13   T y; // element removed from last node
14   if (CurrentSize % 2) { // size is odd
15     y = heap[LastNode].left;
16     LastNode--;
17   }
18   else { // size is even change to y = .left in attempt to speed up luck in insertion
19     y = heap[LastNode].left;
20     heap[LastNode].left = heap[LastNode].right;
21   }
22   CurrentSize--;
23   // find place for y starting at root
24   int i = 1, // current node of heap
25   ci = 2; // child of i
26   while (ci <= LastNode) { // find place to put y
27     // heap[ci].left should be smaller child of i
28     if (ci < LastNode &&
29       greater(heap[ci].left, heap[ci+1].left) )
30       ci++;
31     // can we put y in heap[i]?
32     if ( less(y, heap[ci].left) || equal(y, heap[ci].left) )
33       break; // yes
34
35     // no
36     heap[i].left = heap[ci].left; // move child up
37     if (greater(y, heap[ci].right) )
38      IntHeapSwap(y, heap[ci].right);
39     i = ci; // move down a level
40     ci *= 2;
41   }
42   // when CurrentSize is 1, we don't want to put y back on the heap, it was the element removed  (and is equal to x) -- instead we leave heap[1] alone (which means that .left was copied to .right in the else statement above
43   if (CurrentSize > 1)
44     heap[i].left = y;
45
46   return;
47 }
</src>
 功能:将堆中最小的元素移除,同时对最小值堆重新进行堆化,以便移除最小元素后的堆
 仍然保持区间堆的特性。
 
 细解:
 第6-7行通过调用assert函数处理当堆为空时报错的情况,因此调用该函数之前需要调用empty()函数
 第10行获取堆的最小元素。第12-22行获取最小值堆的最后一个元素(y),同时将堆保存的元素数减1。
 第26-44行将元素y插入到最小值堆的顶部,并对最小值堆进行堆化。此处采用的堆化方式和往最小值
 堆中插入元素有所不同,往最小值堆中插入元素采用的堆化方式为自底向上的堆化方法(bottom-up)。
 此处由于将最小值堆的最后一个元素插入到堆的顶部,因此需要采用自顶向下的堆化方法(top-down)。
 即将y目前所处节点的叶子结点最小元素取出(第28-30行),并将其和y进行比较,若该值小于y,则将
 该值储存到到y所在的位置(第36行),否则该值大于等于y(第32-33行),则y当前所处的y位置即为y可
 以储存的位置,于是跳出循环。
 第37-38行判断堆化过程中y是否大于所在结点的right元素,若大于该元素,则将y与其交换,交换后
 并不会影响最大值堆的堆特性(注:因为y一定小于该节点的父节点的right区域,否则y处于父节点位置
 时就进行了交换),同时交换后也不会影响最小值堆的堆特性因为被交换上去的叶子结点的left部分一定
 小于right部分。
 第39行将叶子结点设为当前结点,第40将叶子结点索引乘2,以便走到叶子结点的叶子结点处。(注:堆
 满足二叉树(binary tree)的一些特性,因此当以数组方式表示时,当前结点的第一个叶子结点所在的
 索引为当前结点索引的2倍。
 
 

g) pop_max函数

<src>
0  template<class T, class Less, class Greater, class Equal>
1  void IntervalHeap<T, Less, Greater, Equal>::pop_max()
2  {
3    /* Set x to max element and delete
4    * max element from interval heap.
5    */
6    if (CurrentSize == 0)
7      assert(0);
8    T x;
9    x = heap[1].right; // max element
10  
11   // restructure max heap part
12   int LastNode = CurrentSize / 2 + CurrentSize % 2;
13   T y; // element removed from last node
14   if (CurrentSize % 2) { // size is odd
15     y = heap[LastNode].left;
16     LastNode--;
17   }
18   else { // size is even
19     y = heap[LastNode].right;
20     heap[LastNode].right = heap[LastNode].left;
21   }
22   CurrentSize--;
23   // find place for y starting at root
24   int i = 1, // current node of heap
25   ci = 2; // child of i
26   while (ci <= LastNode) { // find place to put y
27     // heap[ci].right should be larger child of i
28     if (ci < LastNode &&
29       less(heap[ci].right, heap[ci+1].right) )
30       ci++;
31     // can we put y in heap[i]?
32     if ( greater(y, heap[ci].right) || equal(y, heap[ci].right) )
33       break; // yes
34     // no
35     heap[i].right = heap[ci].right; // move child up
36     if ( less(y, heap[ci].left) )
37       IntHeapSwap(y, heap[ci].left);
38     i = ci; // move down a level
39     ci *= 2;
40   }
41   // when CurrentSize is 1, we don't want to put y back on the heap, it was the element removed  (and is equal to x) -- instead we leave heap[1] alone (which means that .left was copied to .right in the else statement above
42   if(CurrentSize > 1)
43     heap[i].right = y;
44
45   return;
46 }
</src>
 功能:将堆中最大的元素移除,同时对最大值堆重新进行堆化,以便移除最大元素后的堆
 仍然保持区间堆的特性。
 
 细解:
 第6-7行通过调用assert函数处理当堆为空时报错的情况,因此调用该函数之前需要调用empty()函数
 第9行获取堆的最大元素。第12-22行获取最小值堆的最后一个元素(y),同时将堆保存的元素数减1。
 第26-43行将元素y插入到最大值堆的顶部,并对最大值堆进行堆化。此处采用的堆化方式和往最大值
 堆中插入元素有所不同,往最大值堆中插入元素采用的堆化方式为自底向上的堆化方法(bottom-up)。
 此处由于将最大值堆的最后一个元素插入到堆的顶部,因此需要采用自顶向下的堆化方法(top-down)。
 即将y目前所处节点的叶子结点最大元素取出(第28-30行),并将其和y进行比较,若该值大于y,则将
 该值储存到到y所在的位置(第35行),否则该值小于等于y(第32-33行),则y当前所处的y位置即为
 y可以储存的位置,于是跳出循环。
 第37-38行判断堆化过程中y是否小于所在结点的left元素,若小于该元素,则将y与其交换,交换后
 并不会影响最小值堆的堆特性(注:因为y一定大于该节点的父节点的left区域,否则y处于父节点位置
 时就进行了交换),同时交换后也不会影响最大值堆的堆特性因为被交换上去的叶子结点的right部分一
 定大于left部分。
 第38行将叶子结点设为当前结点,第39将叶子结点索引乘2,以便走到叶子结点的叶子结点处。

知识点:


1、最大值堆

      所谓最大值堆即堆顶的元素为最大元素,整个堆以二叉树的方式表示,往堆中插入元素时,需要进行
  自底向上的堆化。从堆中删除最大的元素时,一般将堆的最后一个元素插入到堆的顶部,同时进行自顶向
  下的堆化过程。最大值堆一般提供以下几个公共函数,也即功能。
  a) top_max
        返回堆顶部最大的元素
  b) pop_max
        将堆中的最大元素从堆中删除
  c) size
        获取堆中的保存的元素数
  d) empty
        判断堆是否为空

2、最小值堆

      所谓最小值堆即堆顶的元素为最小元素,整个堆以二叉树的方式表示,往堆中插入元素时,需要进行
  自底向上的堆化。从堆中删除最小的元素时,一般将堆的最后一个元素插入到堆的顶部,同时进行自顶向
  下的堆化过程。最小值堆一般提供以下几个公共函数,也即功能。
  a) top_min
        返回堆顶部最小的元素
  b) pop_min
        将堆中的最小元素从堆中删除
  c) size
        获取堆中的保存的元素数
  d) empty
        判断堆是否为空

3、区间堆

      区间堆为最小值堆和最大值堆的结合,因此同时提供了最大值堆和最小值的功能,往堆中插入元素时,
  需要判断是插入在最大值堆还是最小值堆中,然后进行相应的堆化操作。将区间堆中的最小元素删除时,需
  要将最小值堆的最后一个元素插入到最小值堆的顶部,并对其进行堆化。将区间堆中的最大元素删除时,需
  要将最大值堆的最后一个元素插入到最大值堆的顶部,并对其进行堆化。区间堆一般提供以下几个公共函数,
  也即功能。
  a) top_max
        返回堆顶部最大的元素
  b) top_min
        返回堆顶部最小的元素
  c) pop_max
        将堆中的最大元素从堆中删除
  d) pop_min
        将堆中的最小元素从堆中删除
  e) size
        获取堆中的保存的元素数
  f) empty
        判断堆是否为空

转自:http://blog.chinaunix.net/u1/58264/showart_1333599.html

个人工具
工具箱