您的位置:首页 > 其它

排序总结---堆排序

2017-03-18 22:07 155 查看
//
// Created by liyuanshuo on 2017/3/17.
//
#include "heap_sort.h"

/*
* 堆的定义:
* ki<k(2i+1) && k(i) < k(2i)  |
* 或者
* k(i) > k(2i) && k(i) > k(2i+1)
* 若以一维数组存储堆,则堆对应一个完全二叉树,并且所有的非叶节点的值均不大于(或者不小于)其子女的值
* 根节点(堆顶元素)的值是最小的(或者最大的)
*
* 初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之
* 成为一个堆,将堆顶的元素输出,便得到n个元素中最小(或最大)的元素,这时根节点的数最小(或者最大),
* 然后对后面的(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n个元素中次小(或此大)的元素,依此类推
* 直到只有两个节点的堆,并对它们作交换,最后得到n个节点的有序序列,这个过程称为堆排序。
*
*
*/
/*
* 因此,实现堆排序需解决两个问题:
*
* 1. 如何将n 个待排序的数建成堆
*
* 2. 输出堆顶元素后,怎样调整剩余n-1 个元素,使其成为一个新堆
*
* 首先讨论第二个问题:输出堆顶元素后,对剩余n-1元素重新建成堆的调整过程。
* 调整小顶堆的方法:
*
* 1)设有m 个元素的堆,输出堆顶元素后,剩下m-1 个元素。将堆底元素送入堆顶((最后一个元素与堆顶进行交
* 换),堆被破坏,其原因仅是根结点不满足堆的性质。
*
* 2)将根结点与左、右子树中较小元素的进行交换。
*
* 3)若与左子树交换:如果左子树堆被破坏,即左子树的根结点不满足堆的性质,则重复方法 (2).
*
* 4)若与右子树交换,如果右子树堆被破坏,即右子树的根结点不满足堆的性质。则重复方法 (2).
*
* 5)继续对不满足堆性质的子树进行上述交换操作,直到叶子结点,堆被建成。
*
* 称这个自根结点到叶子结点的调整过程为筛选
*/

/*
* 再讨论对n 个元素初始建堆的过程。
*
* 建堆方法:对初始序列建堆的过程,就是一个反复进行筛选的过程
*
* 1)n 个结点的完全二叉树,则最后一个结点是第[n/2]个结点的子树。
*
* 2)筛选从第[n/2]个结点为根的子树开始,该子树成为堆。
*
* 3)之后向前依次对各结点为根的子树进行筛选,使之成为堆,直到根结点。
*/

//从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个
//函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。

/*
* 已知H[s…m]除了H[s] 外均满足堆的定义
* 调整H[s],使其成为大顶堆.即将对第s个结点为根的子树筛选,
*/
// H是待调整的堆数组
// s是待调整的数组元素的位置
// length是数组的长度

void heap_adjust( int H[], int s, int len )
{
int tmp = H[s];
int child = 2 * s  + 1;
while ( child < len )
{
if( child+1 < len && H[child] < H[child+1] )
child++;
if ( H[s] < H[child] )
{
H[s] = H[child];
s = child;
child = 2 * child + 1;
}
else
break;
H[s] = tmp;
}
}

void build_heap( int H[], int len )
{
for (int i = (len-1)/2 ; i >= 0 ; --i )
{
heap_adjust (H, i, len);
}
}

void heap_sort( int H[], int len )
{
build_heap (H, len);
for (int i = len-1; i > 0 ; --i)
{
int tmp = H[i];
H[i] = H[0];
H[0] = tmp;
heap_adjust (H, 0, i);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  排序
相关文章推荐