您的位置:首页 > 理论基础 > 数据结构算法

数据结构——堆排序

2017-05-20 19:28 127 查看

算法介绍

堆排序只需要记录大小的辅助空间,每个待排序额记录仅占有一个储存空间。

堆的定义如下:

设有n个元素的序列{k1,k2,k3……kn},当且仅当k(i)<=k(2i),k(i)<=k(2i+1),或者k(i)>=k(2i),k(i)>=k(2i+1)时称之为堆。

因此,我们可以将这个一维数组看做是一个完全二叉树,而这个完全二叉树满足任意孩子节点的值要小于(大于)其父节点。(也就是说根节点的数值是所有数据当中最小(大)的)

算法分析

在堆排序当中,重点的算法就是堆的建立。

因为堆的特性。堆的顶端一定是这些数据的最大或者最小值。

以最小值为例子。

在数组当中建立了一个堆,因此,我们就可以获取堆顶元素,而这个堆顶元素就是数组数据当中的最小值。将这个最小值输出出来之后,重构这个堆,就可以获取出这组数据当中的第二小的数值,以此类推,就可以从小到大获取了这个数组当中的所有的数值。而按照获取的顺序进行排序之后,就是一个有序的序列

代码实现

/*
堆排序算法
算法思想

将一个待排序的数组看作是一个由n个元素组成的完全二叉树,而这个完全二叉树的根节点一定会比子节点小(堆顶元素为最大值)
这样,若输出了堆的最大值,那么这个堆就会变成了一个由n-1个元素组成的二叉树,得到的是一个n-1个元素当中
的最大值。那么一次类推,我们就可以得到了一个有序的序列。

由此我们可以将这个算法的过程表示出来

1 构建一颗由n个元素组成的完全二叉树(其实只是将数组看做是完全二叉树)而这个完全二叉树的根节点一定会比子节点小

2 输出这个二叉树的根节点(因为根节点是数组当中最大的数)

3 记录这个根节点,并将这个根节点移出这颗完全二叉树当中,n--

重复1,2,3步骤,直到n=0的时候,则表示已经计算并记录了所有的最大值,此时,也就得到了一组有序的序列

这个排序的耗时操作主要在建立初始堆与调整新建的堆的反复“筛选”上
这个排序的时间复杂度为O(nlogn)

*/
void heapSoft(int *arr, int length) {

//临时变量
int temp;

//建立二叉树(因为第length/2-1个节点为完全二叉树的最后一个父节点,因此,i到length/2-1为止)
//从后往前开始调整,建立一颗符合堆标准的完全二叉树
for (int i = length / 2 - 1; i >= 0; i--) {
heapAdjust(arr, i, length);
}

//从最后一个元素开始调整,保证第i项是二叉树中输出的最大的节点(i=length-1,length-2,.....,0)
for (int i = length - 1; i > 0; i--) {
temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;

//每进行一次互换之后,都要进行一次二叉树的调整
//因为每互换一次,二叉树的结构都发生了一次变化
//但是,不能破坏已经输出的结果,因此,每一次排序,二叉树都会变短
heapAdjust(arr, 0, i);
}
}

/*
调整堆排序算法当中的完全二叉树
保证这颗完全二叉树的根节点是树中最大的节点

int *arr   数组的头指针
int index  需要调整的位置
int length 数组的长度
*/
void heapAdjust(int *arr, int index, int length) {

int child;//记录树的孩子节点
int temp;

for (; 2 * index + 1 < length; index = child) {

//获取左孩子节点
child = 2 * index + 1;

//判断右孩子节点是否存在
//并且对比左右子树,若右子树的值比左子树的值大则选择右子树的值
if (child < length - 1 && arr[child + 1] > arr[child]) {
child++;
}

//若父节点小于孩子节点,则调整位置
if (arr[index] < arr[child]) {
temp = arr[index];
arr[index] = arr[child];
arr[child] = temp;
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构