《算法4》中的堆排序

原创 2015年07月10日 11:04:42

因为要写STL中的heap,所以又将这本书中的归并排序复习了一下,并且有所收获,为了避免忘记,我准备将这些思路记下来:
堆排序有两个重要的动作:swimsink
下面写一下两个函数

void swim(int* num, int k, int N)
{
    while(k > 1 && a[k/2] < a[k])
    {
        swap(a[k/2], a[k]);
        k = k/2;
    }
}
void sink(int* num, int k, int N)
{
    while(2*k <= N)
    {
        int j = 2*k;
        if(2*k < N && num[2*k] < num[2*k+1]) 
            j++;
        if(num[k] >= num[j])
            break;
        swap(num[k], num[j]);
        k = j;
    }
}

有了这两个函数,我们就可以构建堆排序了;
首先要构建堆,在构建堆的过程中,数组从左向右遍历并利用swim函数时可以构造成一个堆的,但这需要遍历数组中的全部数字;然而采用数组从右向左遍历并利用sink函数就可以仅仅遍历一般的数字,因为叶子节点不需要进行sink
对于构建堆需要的复杂度是O(2N),因为需要2N次的比较和N次的交换(交换和比较时一个算法中的主要成本,如果没有这两者再考虑存取的成本),这里可以举例:对于一个127个元素的堆,在构建的时候:32个大小为3的堆(需要一次交换,两次比较),16个大小为7的堆(两次交换三次比较),8个大小为15的堆,4个大小为31的堆,2个大小为63的堆和1个大小为127的堆,一共就需要120交换和240比较。

之后就可以开始排序了:首先将堆顶的元素和数组的尾部元素交换,然后刷新数组的长度,让算法无法再访问这个数组尾部元素;
将每次都从最高的点插入堆尾的元素,然后利用sink下沉,重新adjust这个堆,以此类推,可以参考我这段代码;

while(N > 1)
{
    swap(num[1], num[N--]);
    sink(num, 1, N);
}

总体的思路就是这个样子,但是,如果遇到“比较”的成本特别大的时候,还可以采用如下的方法:

先下沉后上浮

我们可以这样来思考,处在数组尾部的元素一般都是比较小的元素,这样我们在把它放到堆顶后进行sink,一般它还是会下来的,那么我们为什么不直接将这个元素放到堆尾然后在swim上去呢?这样会减少很多次的比较,具体做法如下:
1.当我们将堆顶的元素放在数组尾部后,我们利用辅助空间暂时存储之前的堆尾元素temp,并不是将它放在堆顶;
2.现在堆顶是空的,我们不将temp放在上面,而是直接将堆顶对应的子节点中较大的那个节点bigger直接放上去(这样我们只需要比较一次哪个子节点大就可以了),然后这个bigger的位置就产生了空缺;
3.这个空缺的左右子节点继续比较产生较大的节点bigger`放在父节点空缺的位置上;
4.依次类推直到空缺的节点没有子节点;
5.将空缺的节点放上temp;
6.swim上浮
这个方法需要一个辅助空间,如果条件允许的话便可以用;而这种方法产生的比较次数几乎就成为了N;这个方法是Floyd在1964年改善的,我觉得十分不错;只是《算法4》中的描述我不太习惯,换成我习惯方式^^;

【算法总结】堆及堆排序总结

【前言】 堆排序是什么? 堆排序的核心是将数组构建成为一个堆,然后从堆顶逐个逐个数字获取,堆分成最大堆(大顶堆)及最小堆(小顶堆)。 下面还是先说明什么是堆。 堆是...
  • abcd1f2
  • abcd1f2
  • 2015年08月03日 18:18
  • 1283

堆排序

堆排序的最坏时间复杂度是O(nlogn),平均时间复杂度是O(nlogn)。但是堆排序的时间常数比较大,因此从平均来看堆排序的时间复杂度反而是最差的。 基本接口: 1.插入:插入一个新元素的时候,...
  • SKY453589103
  • SKY453589103
  • 2016年04月21日 17:31
  • 631

堆排序算法研究_唐开山

  • 2015年01月07日 11:18
  • 201KB
  • 下载

算法(第四版)学习笔记之java实现基于堆的优先队列

一台电脑之所以能同时运行多个应用程序的时候,是通过为每个应用程序的事件分配一个优先级,并总是处理下一个优先级最高的事件来实现的。在这种情况下,一个合适的数据结构应该支持两种操作:删除最大元素和插入元素...
  • l243225530
  • l243225530
  • 2015年07月25日 17:49
  • 842

《算法(第四版)》排序-----堆排序

1.什么是堆? 讲堆排序之前,先了解一下什么是堆。堆其实相当于一种数据结构,它的本质是一种数组对象,但是它里面的内同又是一颗完全二叉树结构,它的特点是父节点的值大于(或小于)两个子节点的值,常常用于优...
  • kwang0131
  • kwang0131
  • 2016年04月09日 12:00
  • 455

算法(第四版)学习笔记之java实现堆排序

继上一篇实现基于堆的优先队列后,这次将利用上一次完成的基于堆的能够重复删除最大元素操作的优先队列来实现一种经典而优雅的排序算法,称之为堆排序。 堆排序可分为两个阶段: 1.构建堆:在堆的构建过程中...
  • l243225530
  • l243225530
  • 2015年07月26日 16:02
  • 1053

《算法4》中的堆排序

堆排序
  • markjenny
  • markjenny
  • 2015年07月10日 11:04
  • 719

关于堆的一点总结

一、什么是堆 堆是一颗被完全填满的二叉树,可能的例外是在底层,底层上的元素从左到右填入,这样的树被称为完全二叉树。 二、堆的特性 1、如果一个堆有N个节点,那么堆的高度为 h = [lgN] 。...
  • zhutulang
  • zhutulang
  • 2017年07月01日 23:22
  • 209

【java面试】算法篇之堆排序

一、堆的概念 堆是一棵顺序存储的完全二叉树。完全二叉树中所有非终端节点的值均不大于(或不小于)其左、右孩子节点的值。 其中每个节点的值小于等于其左、右孩子的值,这样的堆称为小根堆; 其中每个节点的值大...
  • qq_21492635
  • qq_21492635
  • 2017年06月12日 14:23
  • 536

【排序算法】堆排序原理及Java实现

1、基本思想堆是一种特殊的树形数据结构,其每个节点都有一个值,通常提到的堆都是指一颗完全二叉树,根结点的值小于(或大于)两个子节点的值,同时,根节点的两个子树也分别是一个堆。 堆排序就是利用堆(...
  • jianyuerensheng
  • jianyuerensheng
  • 2016年04月27日 18:34
  • 8362
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:《算法4》中的堆排序
举报原因:
原因补充:

(最多只允许输入30个字)