Pages

Monday, May 16, 2011

算法总结系列之一:堆排序(Heap Sort)


    在软件设计相关领域,"堆(Heap)"的概念主要涉及到两个方面:
    • 一种数据结构, 逻辑上是一颗完全二叉树,存储上是一个数组对象(二叉堆).
    • 垃圾收集存储区,是软件系统可以编程的内存区域.
    本文所说的堆,指的是前者.另外, 这篇文章中堆中元素的值, 均以整型为例.
    堆排序的时间复杂度是O(nlgN),与快速排序达到相同的时间复杂度. 但是在实际应用中,我们往往采用快速排序而不是堆排序. 这是因为快速排序的一个好的实现,往往比堆排序具有更好的表现. 堆排序的主要用途,是在形成和处理优先级队列方面. 另外, 如果计算要求是类优先级队列(比如, 只要返回最大或者最小元素, 只有有限的插入要求等), 堆同样是很适合的数据结构.
  • 基础知识
    堆一般用数组表示,比如数组A. 数组的长度Length(A), 堆在数组中的元素个数HeapSize(A). 一般说来, HeapSize(A) <= Length(A), 因为数组A当中可能有一些元素不在堆中.
    假设节点I是数组A中下标为i的节点.
    • Parent(i) : return Floor(i/2);    //I的父节点下标.Floor(i)表示比i小的最大整数.
    • Left(i) : return 2*i;               //I的左子节点
    • Right(i) : return 2*i+1;           //I的右子节点
    含有n个元素的堆A的高度是: Floor(lgn)
  • 堆的基本操作

  • MaxHeapify( A, i ):
        保持堆的性质.假设数组A和下标i, 假定以Left(i)和Right(i)为根结点的左右两棵子树都已经是最大堆, 节点i的值可能小于其子节点. 调整节点i的位置.
    算法的C#实现:
public void MaxHeapify( int[] A,  int heapSize, int i )

{

int left = 2*i;

int right = 2*i+1;

int larger = i;

//compare with left child

if ( left <= heapSize && A[left] > A[i])

{

larger = left;

}



//compare larger node with right child

if ( right <= heapSize && A[right] > A[i])

{

larger = right; 

}



//exchange i with the larger child

if(larger != i )

{

int tmp = A[i];

A[i] = A[larger];

A[larger] = tmp;



MaxHeapfy( A, heapSize, larger );

}

}

        时间复杂度与树的高度成正比, 为 O(lgn).

  • BuildMaxHeap( A ):
        从一个给定的数组建立最大堆.子数组A[ floor(n/2)+1 ....  ... n]中的元素都是树的叶节点(完全二叉树的基本性质) . 从索引 ceiling(n/2)开始一直到1, 对每一个元素都执行MaxHeapify, 最终得到一个最大堆.
算法的C#实现:
public void BuildMaxHeap( int[] A )

{

int heapSize = A.Length;

for( int i = Math.Ceiling(heapSize/2); i >= 1; i --)

{

MaxHeapify( A, heapSize, i );

}

}
 
 
时间复杂度依赖于MaxHeapify O(h) (h - 树的高度) ,BuildMaxHeap(A)的时间复杂度是O(n).
 
 
  • 堆排序 HeapSort( A ):
        堆排序算法的基本思想是, 将数组A创建为一个最大堆, 然后交换堆的根(最大元素)和最后一个叶节点x, 将x从堆中去掉形成新的堆A1, 然后重复以上动作,直到堆中只有一个节点.
算法的C#实现:
public void HeapSort( int[] A )

{

BuildMaxHeap(A);

int HeapSize = A.Length;



for(int i = HeapSize; i > 1; i--)

{

//assume the type of A is int[] for example

int tmp = A[1];

A[1] = A[i];

A[i] = A[1];



HeapSize -= 1; //cut off the sorted node.

MaxHeapify( A, HeapSize, 1 );  // sort the A1 to a max heap
}

}
 
堆排序的时间复杂度是O(nlgn).
 
  • 优先级队列算法-增加某元素的值(优先级) : HeapIncreaseKey( A, i, key )
增加某一个元素的优先级后(元素的值), 该元素应该向上移动,才能保持堆的性质.
算法的C#实现:
public void HeapIncreaseKey( int[] A, int index, int key )

{

if( key <= A[index])

return;

A[index] = key;

while( index>1 && A[Parent(index)] < A[index]]

{

//prompt child node

int tmp = A[index]:

A[index]= A[Parent(index)];

A[Parent(index)] = tmp;



index = Parent(index);

}

}
 
时间复杂度: O(lgn)
  • 优先级队列算法-插入一个元素: Insert( S, x ) 将x元素插入到优先级队列S中.
主要思路是, 将堆的最后一个叶节点之后, 扩展一个为无穷小的新叶节点, 然后增大它的值为x的值
 
算法的C#实现是:
public void HeapInsert( int[] A, int key )

{

A.Length += 1;

A[A.Length] = int.MinValue;



HeapIncreasekey( A, A.Length, key );

}
 
时间复杂度O(lgn)



作者:Jeffrey Sun
出处:http://sun.cnblogs.com/
本文以“现状”提供且没有任何担保,同时也没有授予任何权利。本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

No comments:

Post a Comment