转载

堆得简单介绍以及堆排序

首先看一下堆的定义:

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

K(i) <= K(2*i) && K(i) <= K(2*i+1)      此时的堆为小顶堆

K(i) >= K(2*i) && K(i) >= K(2*i+1)      此时的堆为大顶堆

(i = 1,2,……,n/2(下取整))

注意:堆得存储是用 一维数组 来存储的。

若将堆对应的序列看成是一个完全二叉树,则堆得含义表明:

完全二叉树中所有非终端结点的值均不大于(或不小于)其左右孩子结点的值。

因此,若序列{K1,K2,……,Kn} 是大顶堆,则堆顶元素必为序列中n个元素的最大值;反之,若序列是小顶堆,则堆顶元素必为序列中n个元素的最小值。

堆排序就是利用的这个性质。

堆排序的过程如下:

假设要从小到大排序,我们构建一个大顶堆,则堆顶元素是最大值。将堆顶元素和最后一个元素互换,则最后一个元素变成了n个元素中的最大值。之后再将剩下的 n-1 个元素调整成为大顶堆,将堆顶元素和第n-1 个元素互换,则第n-1 个元素变成了n个元素中的次大值……循环这个过程,不断调整堆,最后得到一个有序的序列。

在上面堆排序的过程中,有两个问题需要解决:

(1)如何将一个初始的序列构建成一个大顶堆?

(2)再得到最大元素后,剩下的n-1个元素如何再次调整成为一个大顶堆?

实际上, 初始序列构建大顶堆也是一个不断调整堆得过程 。因此,只要解决第二个问题就可以。

下图是一个大顶堆:

堆得简单介绍以及堆排序

当把堆顶元素20和最后一个元素互换之后,最后一个元素变成了序列中的最大值。如下图:

堆得简单介绍以及堆排序

但是,此时堆顶元素违反了大顶堆的性质,堆顶元素的左右孩子仍旧满足大顶堆的性质。因此,此时需要对堆进行调整。因为左子树的值大于右子树的值,所以将3和17互换,如下图:

堆得简单介绍以及堆排序

此时,左子树又违反了大顶堆得性质,所以需要调整左子树,如下图:

堆得简单介绍以及堆排序

至此,一次调整完毕,堆顶元素成为了次大元素。

实际上, 调整堆就是这样一个不断筛选比较的过程,不断的和左右子树比较,一直到不需要交换为止。

将一个无序序列构建成一个大顶堆的过程就是一个反复筛选的过程。将此序列看成是一个完全二叉树,则最后一个非叶子节点是第 n/2(下取整)个元素,因此,筛选只需从第 n/2(下取整)个元素开始。

假设有序列:{49,38,65,97,76,13,27},初始二叉树是:

堆得简单介绍以及堆排序

从第3个元素,也就是65开始调整堆,65大于左右子树的值,因此不需要调整。

然后是第2个元素,也就是从38开始调整堆,38和左右子树比较,将97和38互换,调整后如下图:

堆得简单介绍以及堆排序

然后是第1个元素,也就是从49开始调整堆,49和左右子树比较,将97和49互换,互换之后,因为49破坏了左子树大顶堆的性质,因此需要继续调整,将49和左右子树比较,然后将49和76互换,调整过程如下图:

堆得简单介绍以及堆排序

堆得简单介绍以及堆排序

至此,将一个无序的序列调整成为了一个大顶堆。

同理,堆排序也分为两个过程:(1)将初始化序列调整成为一个大顶堆(2)用最后一个元素和堆顶元素交换,然后不断调整剩下的元素成为一个新的大顶堆。

代码如下:

const int N = 8; int num[N] = {-1,49,38,65,97,76,13,27};  //从第一个元素开始存储  //调整堆的函数 void heapAdjust(int pos,int total){  int temp = num[pos];  for(int j = 2 * pos; j <= total; j *= 2){   if(j < total){  //说明还有右子树    if(num[j] < num[j + 1]){   //筛选出左右子树中较大的值     j += 1;    }   }   if(temp >= num[j])  //不需要再继续向下调整了    break;   num[pos] = num[j];   pos = j;  }  num[pos] = temp; }  void heapSort(){  //首先将数组构建成一个大顶堆  for(int i = (N-1) / 2;i >=1;--i){   heapAdjust(i,N - 1);  }  //开始堆排序  for(int i = N-1; i > 1; --i){   int temp = num[i];   //交换第一个元素和最后一个元素   num[i] = num[1];   num[1] = temp;   heapAdjust(1,i - 1);  //交换完之后,重新调整堆  } }
正文到此结束
Loading...