您的位置:首页 > 其它

堆排序

2017-09-24 10:37 239 查看
堆排序是对直接选择排序的一种优化:直接选择排序,在n个键值中选出最小值,至少进行n-1次比较。然而继续在剩余的n-1个键值中选出次小值,要比较n-2次。怎样才能利用前面n-1次的比较所得的信息,来减少以后比较的次数,所以有了堆排序。

2个问题值得思考:

1)如何由一个初始序列建成一个堆?

2)如何在输出堆顶元素之后调整剩余元素成为一个堆?

其实这就是一个反复“筛选”的过程:

首先将要排序的所有键值看成一颗完全二叉树的各个结点(这时的完全二叉树并不一定具备堆的特性),

根据完全二叉树性质最后一个非终端节点是第[n/2]个元素,即对于i>[n/2]的结点Ki都没有孩子结点,因此以这样的Ki为根的子树已经是堆,

所以“筛选”只需要从[n/2]开始,逐步把以K[n/2],K[n/2]-1,K[n/2]-2,...,K1为根的子树“筛选”成堆,就完成建堆的过程了。

代码展示:

/// <summary>
/// 建立最小堆的过程
/// </summary>
/// <param name="a"></param>
/// <param name="k"></param>
/// <param name="m"></param>
void Sift(int[] a, int k, int m)
{
int i, j, x;
i = k; //父结点
j = 2 * i; //父结点的左孩子
x = a[k]; //父结点的值
while (j<=m )
{
if ((j<m) && a[j]>a[j+1]) //如果有右孩子,并且右孩子<左孩子
{
j++; //右孩子
}
if (x<a[j]) //父结点值小于左孩子
{
break; //跳出这个循环,判断下一个结点
}
else //父结点与子结点交换位置
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
i = j;
j = 2 * i; //交换完位置之后,再进行与下面孩子的比较(这里需要注意一下)
}
}
}
public string Heap(int[] a)
{
string shu = "排好序的数组:";
int n = 10;
for (int i = n/2; i >=1; i--)    //最后一个非终端节点是第[n/2]个元素,即对于i>[n/2]的结点Ki都没有孩子结点,因此以这样的Ki为根的子树已经是堆。所以“筛选”只需要从[n/2]开始,逐步把以K[n/2],K[n/2]-1,K[n/2]-2,...,K1为根的子树“筛选”成堆,就完成建堆的过程了。
{
Sift(a, i, n); //调用建堆的方法
}
for (int i= n; i>=2; i--)
{
//将堆顶记录和堆中最后一个记录互换
int temp = a[1];
a[1] = a[i];
a[i] = temp;
Sift(a, 1, i - 1);  //现在从堆顶开始与左右孩子比较
}
for (int i = 1; i <=n ; i++)
{
shu = shu + "    " + a [i].ToString();
}
return shu ;
}
//自动生成10个数
Random rd = new Random();
for (int i = 1; i <= 10; i++)
{
a[i] = rd.Next(1, 100);
label2.Text = label2.Text + "  " + a[i].ToString();
}
label1.Text = sort.Heap(a);


最终显示结果如下:



堆排序特点:

稳定性: 不稳定

使用条件: 在待排序记录较少时不适用,记录数多时很有效

时间复杂度: 运行时间主要消耗在初始建堆和不断“筛选”的过程,所以平均时间为O(nlog2n),最坏情况下时间复杂度也为O(nlog2n)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: