一、堆的定义
堆通常是一个可以被看做一棵树的数组对象,其任一非叶节点满足以下性质:
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. 3、jeecg 笔记之 模糊查询

    1.前言 jeecg 考虑到默认模糊查询的话,会增加系统压力,导致查询慢,本来系统就挺那啥的... 2.方式一之实体赋值 实体重新赋值查询,用 * %% * 实现,我们知道 sql 中通常使用 % 去 ...

  2. 解决win10无法访问共享

    一台win10共享的文件夹,有的电脑是可以访问的,我的win10 访问不了,说什么 遇到未知错误,用以下方法得以解决 ----------------------------------------- ...

  3. vue-cli 搭建的项目关闭 eslint

    一般不会关闭eslint,基于接手的代码用eslint的时候报错太多,强迫症的人实在忍受不了报错,先实行关闭: 1.在build 下面的 webpack.base.conf.js 找到 module- ...

  4. RPC服务和HTTP服务对比

    RPC服务和HTTP服务对比 RPC(即Remote Procedure Call,远程过程调用) 协议区别: RPC主要是基于TCP/IP协议的,而HTTP服务主要是基于HTTP协议的,我们都知道H ...

  5. SpringBoot框架的使用

    什么是SpringBoot Spring Boot是Spring社区发布的一个开源项目,旨在帮助开发者快速并且更简单的构建项目.大多数SpringBoot项目只需要很少的配置文件. SpringBoo ...

  6. Python基础(八) yaml在python中的使用

    yaml 通常用来存储数据,类似于json YAML 简介 YAML(Yet Another Markup Language),一种直观的能够被电脑识别的数据序列化格式,是一个可读性高并且容易被人类阅 ...

  7. python队列基本使用

    Python queue队列 作用: 解耦:使程序直接实现松耦合,修改一个函数,不会有串联关系. 提高处理效率:FIFO = 现进先出,LIFO = 后入先出.   队列: 队列可以并发的派多个线程, ...

  8. 关于12C中optimizer_adaptive_features参数介绍

    optimizer_adaptive_features参数在OLAP数据仓库环境中可以获得较好的效果,实际在重上传轻查询的OLTP系统上,可以关闭这项新功能. 其主要功能是为了在语句执行过程中实时收集 ...

  9. fiddler学习总结--Web端抓包

    步骤一: Fiddler的基本配置:Tools-->option-->Connections: 就可以进行抓包了 步骤二: 可以通过一些设置过滤: 步骤三: 抓取HTTPS的请求:1.安装 ...

  10. Linux文件检索

    title: Linux文件检索 date: 2017-12-11 19:03:01 tags: linux categories: linux whereis 只要执行 whereis ls 就可以 ...