堆排序

根据http://www.cnblogs.com/mengdd/archive/2012/11/30/2796845.html改编而来

 

堆的定义

  n个元素的序列{k1,k2,…,kn}当且仅当满足下列关系之一时,称之为堆。

  情形1:ki <= k2i 且ki <= k2i+1 最小化堆小顶堆

  情形2:ki >= k2i 且ki >= k2i+1化堆大顶堆

  其中i=1,2,…,n/2向下取整;

 

  若将和此序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。

  由此可以得到,若序列{k1,k2,…,kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。

 

 堆排序的思想

   利用大顶堆(小顶堆)堆顶记录的是最大关键字(最小关键字)这一特性,使得每次从无序中选择最大记录(最小记录)变得简单。

   其基本思想为(大顶堆):

    1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区;

    2)将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn),且满足R[1,2...n-1]<=R[n]; 

    3)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

     对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有的非叶节点都进行调整。

构造初始堆:将R[1..n]构造为堆;

  初始化堆是对所有的非叶子结点进行筛选。最后一个非终端元素的下标是[n/2]向下取整,所以筛选只需要从第[n/2]向下取整个元素开始,从后往前进行调整。

  比如,给定一个数组,首先根据该数组元素构造一个完全二叉树。

  然后从最后一个非叶子结点开始,每次都是从父结点、左孩子、右孩子中进行比较交换,交换可能会引起孩子结点不满足堆的性质,所以每次交换之后需要重新对被交换的孩子结点进行调整。

进行堆排序:将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。

  有了初始堆之后就可以进行排序了。

  排序开始,首先输出堆顶元素(因为它是最值),将堆顶元素和最后一个元素交换,这样,第n个位置(即最后一个位置)作为有序区,前n-1个位置仍是无序区,对无序区进行调整,得到堆之后,再交换堆顶和最后一个元素,这样有序区长度变为2。。。

  不断进行此操作,将剩下的元素重新调整为堆,然后输出堆顶元素到有序区。每次交换都导致无序区-1,有序区+1。不断重复此过程直到有序区长度增长为n-1,排序完成。

 

堆排序实例

 

   首先,建立初始的堆结构如图:

 

  

 

 

  然后,交换堆顶的元素和最后一个元素,此时最后一个位置作为有序区(有序区显示为黄色),然后进行其他无序区的堆调整,重新得到大顶堆后,交换堆顶和倒数第二个元素的位置……

 

  

 

  重复此过程:

  

  最后,有序区扩展完成即排序完成:

  

 

 

堆排序最重要的三个功能函数,伪代码如下:

堆调整:

void heapAdjust(int[] a, int start, int end) {

int startV = a[start];

for (int i = 2*start + 1; i <= end; i *= 2) {

  if (i<end && a[i]<a[i+1]) i++;  // 将索引指向较大的孩子节点

  if (a[i]<startV) break;      // 如果父节点的值大于孩子节点,调整完成

  a[start] = a[i];             // 父节点的值小于孩子节点,二者交换,并继续调整后继堆

  start = i;

}

a[start] = startV;

}

初始化堆:

void heapInit(int[] a, int n) {

// 初始化从最后一个非终端节点开始调整,直到第0个节点;这样保证在调整每一个节点时,其左右孩子节点都是合格的堆

for (int i = n/2; i >= 0; i--) heapAdjust(a, i, n);

}

排序:

void heapSort(int[] a, int n) {

heapInit(a, n);     // 第一步要求对堆进行初始化调整

for (int i = n; i >= 0; i--) {

swap(a[0], a[i]);    // 交换a[0]和堆中的最后一个元素

heapAdjust(a, 0, i - 1);   // 调整堆的0~i-1,i已经在有序序列中

}

}

 

posted @ 2016-10-27 15:48  hunterDing  阅读(311)  评论(0编辑  收藏  举报