PHP排序算法之堆排序(Heap Sort)实例详解


Posted in PHP onApril 21, 2018

本文实例讲述了PHP排序算法之堆排序(Heap Sort)。分享给大家供大家参考,具体如下:

算法引进:

在这里我直接引用《大话数据结构》里面的开头:

在前面讲到 简单选择排序 ,它在待排序的 n 个记录中选择一个最小的记录需要比较 n - 1 次,本来这也可以理解,查找第一个数据需要比较这么多次是正常的,否则如何知道他是最小的记录。

可惜的是,这样的操作并没有把每一趟的比较结果保存下来,在后一趟的比较重,有许多比较在前一趟已经做过了,但由于前一趟排序时未保存这些比较结果,所以后一趟排序时又重复执行了这些比较操作,因而记录的比较次数较多。

如果可以做到每次在选择到最小记录的同时,并根据比较结果对其他记录做出相应的调整,那样排序的总体效率就会非常高了。而堆排序,就是对简单选择排序进行的一种改进,这种改进的效果是非常明显的。

基本思想:

在介绍堆排序之前,我们先来介绍一下堆:

《大话数据结构》里的定义:堆 是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,成为大顶堆(大根堆);或者每个节点的值都小于或等于其左右节点的值,成为小顶堆(小根堆)。

当时我在看到这里的时候也对有“堆是否是完全二叉树”有过疑问,网上也有说不是完全二叉树的,但是无论堆是不是完全二叉树,尚且保留意见。我们只要知道,在这里我们采用完全二叉树形式的大根堆(小跟堆),主要是为了方便存储和计算(后面我们会看到带来的便利)。

PHP排序算法之堆排序(Heap Sort)实例详解

堆排序算法:

堆排序就是利用堆(假设利用大根堆)进行排序的方法,它的基本思想是:将待排序的序列构造成一个大根堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的 n - 1 个序列重新构造成一个堆,这样就会得到 n 个元素中的次小的值。如此反复执行,便能得到一个有序序列了。

大根堆排序算法的基本操作:

①建堆,建堆是不断调整堆的过程,从 len/2 处开始调整,一直到第一个节点,此处 len 是堆中元素的个数。建堆的过程是线性的过程,从 len/2 到 0 处一直调用调整堆的过程,相当于 o(h1) + o(h2) …+ o(hlen/2) 其中 h 表示节点的深度, len/2 表示节点的个数,这是一个求和的过程,结果是线性的 O(n)。

②调整堆:调整堆在构建堆的过程中会用到,而且在堆排序过程中也会用到。利用的思想是比较节点i和它的孩子节点 left(i) , right(i),选出三者最大(或者最小)者,如果最大(小)值不是节点i而是它的一个孩子节点,那边交互节点i和该节点,然后再调用调整堆过程,这是一个递归的过程。调整堆的过程时间复杂度与堆的深度有关系,是 lgn 的操作,因为是沿着深度方向进行调整的。

③堆排序:堆排序是利用上面的两个过程来进行的。首先是根据元素构建堆。然后将堆的根节点取出(一般是与最后一个节点进行交换),将前面 len-1 个节点继续进行堆调整的过程,然后再将根节点取出,这样一直到所有节点都取出。堆排序过程的时间复杂度是 O(nlgn)。因为建堆的时间复杂度是 O(n)(调用一次);调整堆的时间复杂度是 lgn,调用了 n-1 次,所以堆排序的时间复杂度是 O(nlgn)。

在这个过程中是需要大量的图示才能看的明白的,但是我懒。。。。。。

算法实现:

<?php
//堆排序(对简单选择排序的改进)
function swap(array &$arr,$a,$b){
  $temp = $arr[$a];
  $arr[$a] = $arr[$b];
  $arr[$b] = $temp;
}
//调整 $arr[$start]的关键字,使$arr[$start]、$arr[$start+1]、、、$arr[$end]成为一个大根堆(根节点最大的完全二叉树)
//注意这里节点 s 的左右孩子是 2*s + 1 和 2*s+2 (数组开始下标为 0 时)
function HeapAdjust(array &$arr,$start,$end){
  $temp = $arr[$start];
  //沿关键字较大的孩子节点向下筛选
  //左右孩子计算(我这里数组开始下标识 0)
  //左孩子2 * $start + 1,右孩子2 * $start + 2
  for($j = 2 * $start + 1;$j <= $end;$j = 2 * $j + 1){
    if($j != $end && $arr[$j] < $arr[$j + 1]){
      $j ++; //转化为右孩子
    }
    if($temp >= $arr[$j]){
      break; //已经满足大根堆
    }
    //将根节点设置为子节点的较大值
    $arr[$start] = $arr[$j];
    //继续往下
    $start = $j;
  }
  $arr[$start] = $temp;
}
function HeapSort(array &$arr){
  $count = count($arr);
  //先将数组构造成大根堆(由于是完全二叉树,所以这里用floor($count/2)-1,下标小于或等于这数的节点都是有孩子的节点)
  for($i = floor($count / 2) - 1;$i >= 0;$i --){
    HeapAdjust($arr,$i,$count);
  }
  for($i = $count - 1;$i >= 0;$i --){
    //将堆顶元素与最后一个元素交换,获取到最大元素(交换后的最后一个元素),将最大元素放到数组末尾
    swap($arr,0,$i);
    //经过交换,将最后一个元素(最大元素)脱离大根堆,并将未经排序的新树($arr[0...$i-1])重新调整为大根堆
    HeapAdjust($arr,0,$i - 1);
  }
}
$arr = array(9,1,5,8,3,7,4,6,2);
HeapSort($arr);
var_dump($arr);

运行结果:

array(9) {
 [0]=>
 int(1)
 [1]=>
 int(2)
 [2]=>
 int(3)
 [3]=>
 int(4)
 [4]=>
 int(5)
 [5]=>
 int(6)
 [6]=>
 int(7)
 [7]=>
 int(8)
 [8]=>
 int(9)
}

时间复杂度分析:

它的运行时间只要是消耗在初始构建对和在重建堆屎的反复筛选上。

总体上来说,堆排序的时间复杂度是 O(nlogn)。由于堆排序对原始记录的排序状态并不敏感,因此它无论是最好、最差和平均时间复杂度都是 O(nlogn)。这在性能上显然要远远好于冒泡、简单选择、直接插入的 O(n^2) 的时间复杂度了。

堆排序是一种不稳定排序方法。

本文参考自《大话数据结构》,在此仅作记录,方便以后查阅,大神勿喷!

PHP 相关文章推荐
PHP脚本数据库功能详解(下)
Oct 09 PHP
PHP setcookie设置Cookie用法(及设置无效的问题)
Jul 13 PHP
php数组中删除元素的实现代码
Jun 22 PHP
php启动时候提示PHP startup的解决方法
May 07 PHP
实例讲解PHP面向对象之多态
Aug 20 PHP
linux下实现定时执行php脚本
Feb 13 PHP
thinkphp实现图片上传功能
Jan 13 PHP
微信红包随机生成算法php版
Jul 21 PHP
ubutu 16.04环境下,PHP与mysql数据库,网页登录验证实例讲解
Jul 20 PHP
thinkphp5.1 文件引入路径问题及注意事项
Jun 13 PHP
php实现单笔转账到支付宝功能
Oct 09 PHP
浅谈php使用curl模拟多线程发送请求
Mar 08 PHP
PHP实现Huffman编码/解码的示例代码
Apr 20 #PHP
PHP排序算法之希尔排序(Shell Sort)实例分析
Apr 20 #PHP
PHP排序算法之直接插入排序(Straight Insertion Sort)实例分析
Apr 20 #PHP
PHP排序算法之简单选择排序(Simple Selection Sort)实例分析
Apr 20 #PHP
PHP排序算法之冒泡排序(Bubble Sort)实现方法详解
Apr 20 #PHP
PHP实现二叉树深度优先遍历(前序、中序、后序)和广度优先遍历(层次)实例详解
Apr 20 #PHP
PHP SPL 被遗落的宝石【SPL应用浅析】
Apr 20 #PHP
You might like
php Static关键字实用方法
2010/06/04 PHP
php出现内存位置访问无效错误问题解决方法
2014/08/16 PHP
php生成与读取excel文件
2016/10/14 PHP
laravel 5.1下php artisan migrate的使用注意事项总结
2017/06/07 PHP
PHP实现权限管理功能示例
2017/09/22 PHP
JS backgroundImage控制
2009/05/19 Javascript
关于javascript中的parseInt使用技巧
2009/09/03 Javascript
使用jQuery将多条数据插入模态框的实现代码
2014/10/08 Javascript
Javascript中实现trim()函数的两种方法
2015/02/04 Javascript
js由下向上不断上升冒气泡效果实例
2015/05/07 Javascript
JQuery实现鼠标滚轮滑动到页面节点
2015/07/28 Javascript
基于javascript实现tab选项卡切换特效调试笔记
2016/03/30 Javascript
jQuery 获取多选框的值及多选框中文的函数
2016/05/16 Javascript
js清除浏览器缓存的几种方法
2017/03/15 Javascript
Vue 配合eiement动态路由,权限验证的方法
2018/09/26 Javascript
微信小程序实现文字无限轮播效果
2018/12/28 Javascript
ant-design-vue 时间选择器赋值默认时间的操作
2020/10/27 Javascript
python操作摄像头截图实现远程监控的例子
2014/03/25 Python
python中的多重继承实例讲解
2014/09/28 Python
跟老齐学Python之坑爹的字符编码
2014/09/28 Python
python对json的相关操作实例详解
2017/01/04 Python
Python自然语言处理之词干,词形与最大匹配算法代码详解
2017/11/16 Python
对python中raw_input()和input()的用法详解
2018/04/22 Python
Python判断两个list是否是父子集关系的实例
2018/05/04 Python
flask框架视图函数用法示例
2018/07/19 Python
对python requests的content和text方法的区别详解
2018/10/11 Python
PyQt5 窗口切换与自定义对话框的实例
2019/06/20 Python
python利用os模块编写文件复制功能——copy()函数用法
2020/07/13 Python
python在linux环境下安装skimage的示例代码
2020/10/14 Python
Django实现简单的分页功能
2021/02/22 Python
美国温暖商店:The Warming Store
2018/12/15 全球购物
函授本科毕业生自我鉴定
2013/10/16 职场文书
简历自我评价怎么写呢?
2014/01/06 职场文书
关于保护环境的建议书
2014/08/26 职场文书
酒吧七夕情人节宣传语
2015/11/24 职场文书
导游词之白茶谷九龙峡
2019/10/23 职场文书