一、堆的定义
堆通常是一个可以被看做一棵树的数组对象,其任一非叶节点满足以下性质:
1)堆中某个节点的值总是不大于或不小于其父节点的值:
  每个节点的值都大于或等于其左右子节点的值,称为大顶堆。即:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]。
  或:
  每个节点的值都小于或等于其左右子节点的值,称为小顶堆。即:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]。
2)堆总是一棵完全二叉树。
注:上述公式,根节点从0开始。如果根节点从1开始,则左右子节点分别是2i和2i+1。
由上述性质可知:堆顶元素(或完全二叉树的根)必定是所有元素中最大值(大顶堆)或最小值(小顶堆)。
 
二、基本思想
以大顶堆为例,将待排序的序列构造成一个大根堆,此时,整个序列的最大值就是堆顶的根节点。将它移走(也就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次小的值。如此反复执行,便能得到一个有序序列了。
 
三、算法过程
1)将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
2)将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
3)重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
以大顶堆为例,
创建堆:
1)将n个元素从0到n-1(或从1到n)自顶向下、从左到右编码,转换成一棵完全二叉树;
2)从n/2的非叶节点开始到根节点,逐个扫描,如果子节点大于父节点,就交换;
3)直到根节点最大,如果子树不满足最大堆的条件,继续调节,直到所有的父节点都大于子节点为止。
堆排序:
1)排序开始,首先输出堆顶元素,因为它是最大值;
2)将堆顶元素和最后一个元素交换;
3)将前面n-1个节点继续进行堆调整的过程,再将根节点取出,交换堆顶和最后一个元素;
4)这样一直到所有节点都取出,则排序完成。
 
四、算法图解
待排序序列为49、38、65、97、76、13、27、50,其逻辑结构和存储结构如下:
1)构造大顶堆
① 从最后一个非叶子节点开始,第一个非叶子节点 arr.length/2-1=8/2-1=3,也就是97,在[97, 50]这个小堆里边,父节点97最大,所以不交换;
② 找到第2个非叶子节点,也就是位置为2的节点65,在[65, 13, 27]这个小堆里边,父节点65最大,所以不交换;
③ 找到第3个非叶子节点,也就是位置为1的节点38,在[38, 97, 76]这个小堆里边,97最大,38和97交换;
④ 找到第4个非叶子节点,也就是位置为0的节点49,在[49, 97, 65]这个小堆里边,97最大,49和97交换;
⑤ 交换导致了子根[49, 38, 76]结构混乱,继续调整,[49, 38, 76]中76最大,49和76交换;
⑥ 子根[38, 50]结构混乱,继续调整,[38, 50]中50最大,38和50交换;
至此,一个无序序列已经构造成一个大顶堆。
 
2)堆排序
① 将堆顶元素97和末尾元素38进行交换,得到最大元素97;
② 重新调整结构,使其继续满足大顶堆的定义;
③ 再将堆顶元素76与末尾元素27进行交换,得到第二大元素76;
④ 重新调整结构,使其继续满足大顶堆的定义;
⑤ 后续过程,继续进行交换、调整,如此反复进行,最终使得整个序列有序。
由排序过程可知:若想得到升序,则建立大顶堆,若想得到降序,则建立小顶堆。
 
五、PHP代码实现(大顶堆)
<?php
// 堆排序
function heapSort(&$arr) {
    $len = count($arr);
    // 先将数组构造成大根堆
    for ($i = floor($len / 2) - 1; $i >= 0; $i--) {
        adjustHeap($arr, $i, $len);
    }
    // 调整堆结构+交换堆顶元素与末尾元素
    for ($j = $len - 1; $j > 0; $j--) {
        swap($arr, 0, $j);  // 将堆顶元素与末尾元素进行交换
        adjustHeap($arr, 0, $j); // 重新对堆进行调整
    }
}
 
// 调整堆
function adjustHeap(&$arr, $i, $length) {
    $temp = $arr[$i];   // 先取出当前元素
    for ($k = 2 * $i + 1; $k < $length; $k = 2 * $k + 1) {// 左孩子2 * $i + 1,右孩子2 * $i + 2
        if ($k + 1 < $length && $arr[$k] < $arr[$k + 1]) {// 如果左子结点小于右子结点,k指向右子结点
            $k ++;
        }
        if ($temp < $arr[$k]) {
            $arr[$i] = $arr[$k]; // 将根节点设置为子节点的较大值
            $i = $k;             // 继续往下
        } else {
            break;  // 已经满足大根堆
        }
 
    }
    $arr[$i] = $temp;   // 将temp值放到最终的位置
}
 
// 交换2个值
function swap(&$arr, $a, $b) {
    $temp = $arr[$a];
    $arr[$a] = $arr[$b];
    $arr[$b] = $temp;
}
 
// 测试
$arr = array(49, 38, 65, 97, 76, 13, 27, 50);
heapSort($arr);
print_r($arr);
 
六、效率分析
1、时间复杂度:O(nlogn)
最坏,最好,平均时间复杂度均为O(nlogn)。
2、空间复杂度:堆排序仅需一个记录大小的供交换用的辅助存储空间,因此空间复杂度为O(1),是不稳定排序。

数据结构与算法之PHP排序算法(堆排序)的更多相关文章

  1. 插入排序算法--直接插入算法,折半排序算法,希尔排序算法(C#实现)

    插入排序算法主要分为:直接插入算法,折半排序算法(二分插入算法),希尔排序算法,后两种是直接插入算法的改良.因此直接插入算法是基础,这里先进行直接插入算法的分析与编码. 直接插入算法的排序思想:假设有 ...

  2. ZH奶酪:【数据结构与算法】基础排序算法总结与Python实现

    1.冒泡排序(BubbleSort) 介绍:重复的遍历数列,一次比较两个元素,如果他们顺序错误就进行交换. 2016年1月22日总结: 冒泡排序就是比较相邻的两个元素,保证每次遍历最后的元素最大. 排 ...

  3. 数据结构和算法 – 11.高级排序算法(上)

      对现实中的排序问题,算法有七把利剑可以助你马道成功. 首先排序分为四种:       交换排序: 包括冒泡排序,快速排序.       选择排序: 包括直接选择排序,堆排序.       插入排序 ...

  4. 【数据结构与算法】003—排序算法(Python)

    写在前面 常见排序算法可以分为两大类: 非线性时间比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此称为非线性时间比较类排序. 线性时间非比较类排序:不通过比较 ...

  5. 数据结构与算法之PHP排序算法(桶排序)

    一.基本思想 桶排序是将待排序的数据分割成许多buckets,然后每个bucket各自排序,或用不同的排序算法,或者递归的使用bucket sort算法.也是典型的分而治之(divide-and-co ...

  6. 数据结构与算法之PHP排序算法(希尔排序)

    一.基本思想 希尔排序算法是希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本. 该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接 ...

  7. 数据结构与算法之PHP排序算法(快速排序)

    一.基本思想 快速排序又称划分交换排序,是对冒泡排序的一种改进,亦是分而治之思想在排序算法上的典型应用. 它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部 ...

  8. 排序算法:七大排序算法的PHP实现

    由于最近在找工作,面试中难免会遇到一些算法题,所以就用PHP把七大排序算法都实现了一遍,也当做是一种复习于沉淀. 冒泡排序 2. 选择排序 3. 插入排序 4. 快速排序 5. 希尔排序 6. 归并排 ...

  9. 算法相关——Java排序算法之快速排序(三)

    0. 前言 本系列文章将介绍一些常用的排序算法.排序是一个非常常见的应用场景,也是开发岗位面试必问的一道面试题,有人说,如果一个企业招聘开发人员的题目中没有排序算法题,那说明这个企业不是一个" ...

随机推荐

  1. spring BeanFactory VS FactoryBean

    一.FactoryBean示例 public class DateStringFactoryBean implements FactoryBean<Object> { private bo ...

  2. shiro 角色与权限的解读

    1.为什么 shiro 有了<角色>后,还要设置<角色权限>呢?(问题) 思考:设置好角色了,那么就代表什么操作都可以执行了吗? 理解:如果上边回答是的话,那么只是<角色 ...

  3. 初学Vue 遇到Module not found:Error:Can`t resolve 'less-loader' 问题

    学习vue时,导入一个子组件时遇到Module not found:Error:Can`t resolve 'less-loader' 问题,实际上时在子组件中的样式里加了这么个代码 <styl ...

  4. 【学习笔记】Tensorflow+Inception-v3训练自己的数据

    导读 喵喵的,一个大坑.本文分为吐槽和干货两部分. 一.吐槽 大周末的,被导师扣下加班,嗨气,谁叫本狗子太弱鸡呢,看起来很简单的任务倒腾了两天还没完,不扣你扣谁? 自己刚接到微调Inception-v ...

  5. 【Linux】-NO.87.Assembly.1.滴水逆向.1.001-【介绍】-

    1.0.0 Summary Tittle:[Linux]-NO.87.Assembly.1.滴水逆向.1.001-[基础]- Style:Java Series:Log4j Since:2017-04 ...

  6. python 对反斜杠的处理问题

    首先,"/"左倾斜是正斜杠,"\"右倾斜是反斜杠,可以记为:除号是正斜杠一般来说对于目录分隔符,Unix和Web用正斜杠/,Windows用反斜杠,但是现在Wi ...

  7. 使用Sqlserver事务发布实现数据同步(zhuanqian)

    事务的功能在sqlserver中由来已久,因为最近在做一个数据同步方案,所以有机会再次研究一下它以及快照等,发现还是有很多不错的功能和改进的.这里以sqlserver2008的事务发布功能为例,对发布 ...

  8. AWS是怎么改写 MySQL的?

    五倍吞吐量的提升,跨可用区的六副本,低于一分钟的宕机恢复,兼容 MySQL协议,这是 AWS 推出 Aurora 数据库时给出的数据. 这种量级的提升不可能是小修小补,大都是在架构上有了变革性的突破才 ...

  9. proto 3 语法

    一.简介 proto3是新版本的protobuf语法.它简化了部分语法,并扩大了支持的语言,Ruby.C#.目前谷歌推荐只在是用新支持的语言或使用新的gRPC框架的时候使用.proto2和proto3 ...

  10. Gradle引人注目的特性集

    Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具.它使用一种基于Groovy的特定领域语言来声明项目设置,而不是传统的XML.当前其支持的语言限于Java. ...