zoukankan      html  css  js  c++  java
  • 堆排序

    根据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已经在有序序列中

    }

    }

  • 相关阅读:
    java经典算法四十题
    java经典算法四十题
    数据库表的设计总结
    (转)Eclipse 远程调试 WebSphere Application Server (WAS)
    Oracle 与 DB2 数据类型分类对应说明
    使用 XSLT 将 XML 转换为 XHTML
    寓言一则
    操作系统底层工作的整体认识
    Mysq事务
    算法导论概率发生器
  • 原文地址:https://www.cnblogs.com/caidi/p/6003893.html
Copyright © 2011-2022 走看看