一、堆的定义
堆通常是一个可以被看做一棵树的数组对象,其任一非叶节点满足以下性质:
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. 【LeetCode每天一题】Merge Intervals(合并区间)

    Given a collection of intervals, merge all overlapping intervals. Example 1: Input: [[1,3],[2,6],[8, ...

  2. go语言的安装与开发环境

    安装golang编译器: https://studygolang.com/dl 之后设置环境变量GOPATH(项目目录)  GOROOT(默认已经设置好) 安装编辑器:IDEA安装和破解 https: ...

  3. manjaro使用国内软件源

    虽然manjaro是基于arch修改的,但毕竟还是有些改动,如果可以用manjaro仓库里的,尽量不要用arch的源.如果嫌官方的软件源慢,可以直接一条命令修改成国内的软件源 sudo pacman- ...

  4. Form数据迁移

    --------------------------------------添加linkserver--------------------------------------- --EXEC mas ...

  5. 艾妮记账本微信小程序开发(失败版)

    这是一个寒假假期作业,要求是用web开发或者微信小程序或者手机app开发的,我本来是打算用微信小程序开发的,但由于这个后台数据库连接需要通过https认证后的浏览器再访问MySQL.所以做到后台数据库 ...

  6. ili9325--LCD寄存器配置研究

    2011-06-22 22:18:12 自己根据ili9325的规格书编写驱动.发现LCD屏没显示.于是怀疑是某些寄存器设置错误.要调试的话最好还是先熟悉寄存器的作用,调试的时候只要看到现象就能分析了 ...

  7. vue常见前端UI库

    vue常见前端UI库 VUE资源汇总:https://github.com/RuMengkai/awesome-vue VUX 项目主页:https://vux.li/#/ github地址:http ...

  8. 外网访问内网Elasticsearch WEB

    外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...

  9. c语言格式化打印

    printf的格式化打印 unsigned int          -------------------     %u        打印无符号数 int          ----------- ...

  10. KVO的使用一

    概述 KVO即Key-Value Observing,它允许一个对象被另一个对象在改变指定的属性值后进行通知.iOS中的应用场景很多,比如model的值发生变化,controller里对model进行 ...