堆排序
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; //交换完位置之后,再进行与下面孩子的比较(这里需要注意一下)
}
}
}
最终显示结果如下:
堆排序特点:
稳定性: 不稳定
使用条件: 在待排序记录较少时不适用,记录数多时很有效
时间复杂度: 运行时间主要消耗在初始建堆和不断“筛选”的过程,所以平均时间为O(nlog2n),最坏情况下时间复杂度也为O(nlog2n)
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)
相关文章推荐
- 算法--堆排序
- Python 面试题 - 堆排序 & 演算过程
- 排序算法-堆排序
- 为什么从5000个数中找出10个最大的堆排序最快?
- 算法设计与分析基础-6.4、堆和堆排序
- 哈夫曼树结合堆排序 POJ(3253)
- 算法学习笔记----第二部分:排序和顺序统计量----第6章、堆排序
- 算法导论之插入排序,选择排序,归并排序,冒泡排序,希尔排序,堆排序,快速排序的c语言实现
- 堆排序 一个综合了插入排序和二路归并特点的排序算法(未测试)
- Heap-堆排序
- [字符串hash][堆排序][AC自动机][usaco3.1.5]Contact
- 堆排序
- 算法导论堆排序Java实现
- C++数据结构--堆排序
- C/C++排序之五(堆排序)
- 堆排序
- 图灵标准版堆排序
- 堆排序详解以及java实现
- 堆排序
- 堆排序