堆排序:是一种特殊形式的选择排序,他是简单选择排序的一种改进。

什么是堆?

具有n个元素的序列:{k1,k2,ki,…,kn}

(ki <= k2i,ki <= k2i+1) 或者 (ki >= k2i,ki >= k2i+1), (i = 1,2,3,4...n/2)

满足这个条件时,该序列就是一个堆。第一个条件称为小顶堆,第二个条件称为大顶堆。

为了方便,下边论述基于大顶堆,并且下标从1开始。

将堆的元素放在一棵完全二叉树中,方便我们讨论堆的特性和排序,一般谈到堆也都是一棵完全二叉树。

完全二叉树:若二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

堆的特性(括号道标下标从0开始的计算方法) :

  • 非叶子结点个数:n / 2 (n / 2 - 1)
  • 节点i的左子树下标 :2*i (2*i+ 1)
  • 节点i的右子树下标:2*i + 1 (2*i + 2)

堆排序:

核心思想:

  1. 将原始序列构成一个堆。(建立初始堆)
  2. 交换堆的第一个元素(最大值)和最后一个元素的位置,把堆长度减一后剩余的序列再转换为一个堆。(调整堆)
  3. 重复2过程n-1次。

经过上述操作,就可以将一个无序序列从小到大排序。(因为大顶堆每次把最大值交换到最后了,所以想要降序排列就要用小顶堆)

先说调堆,调堆就是把当前节点和其左子树,右子树中最大值交换,依次把需要调整的节点调一遍。这样一次循环下来根节点肯定放的是最大值。但是有可能循环中的某次交换把之前排好序的结构打乱,需要递归调整。

再说建堆,建堆就是从最后一个非叶子节点开始,到第一个节点为止,依次进行调堆。

for ($i = floor(count($arr) / 2) - 1; $i >=0; $i--) {
adjustHeap($arr);
}

借用网上的一张图说明一下:

从floor(9/2)-1开始,也就是key=3,val=5的地方开始,循环调堆,到图1.6处循环完成,最大值顺利到达根节点,但是发现val=1节点还是需要调整,这就需要在调堆里执行循环调整,使其符合堆的特性。

代码实现:

$arr = [49, 38, 65, 97, 76, 13, 27, 50];
sortHeap($arr);
print_r($arr); function sortHeap(&$arr)
{
//先建堆
buildHeap($arr); //把第一个节点和最后一个节点交换,直到节点数为1
$count = count($arr);
while ($count > 1) {
swap($arr, $count - 1, 0); //交换第一个和最后一个元素
$count--; //去掉最后一个元素后剩余元素再进行调整
adjustHeap($arr, $count, 0);
}
} function buildHeap(&$arr)
{
$node = floor(count($arr) / 2) - 1; //非叶子节点的最大节点,下标从0开始
for ($i = $node; $i >= 0; $i--) { //从最大非叶子节点循环调整每个节点
adjustHeap($arr, count($arr), $i);
}
} //调整堆,接受maxLen为当前堆需要调整的元素最大值,node为当前要调整的节点
function adjustHeap(&$arr, $maxLen, $node)
{
$lchild = 2 * $node + 1; //左子树
$rchild = 2 * $node + 2; //右子树 $max = $node; //设置当前节点为最大值的节点,方便后边最大值节点变化时与当前节点比较,确认是否需要交换 while ($lchild < $maxLen || $rchild < $maxLen) { //左子树和右子树任一个符合条件就进入循环 if ($lchild < $maxLen && $arr[$lchild] > $arr[$max]) { //左子树大于当前节点值得话设置设置$max
$max = $lchild;
} if ($rchild < $maxLen && $arr[$rchild] > $arr[$max]) {
$max = $rchild;
} if ($max != $node) {
swap($arr, $max, $node);
$node = $max; //把当前节点切换为最大值的那个节点,迭代使其符合堆特性
$lchild = 2 * $node + 1;
$rchild = 2 * $node + 2;
} else {
break; //没有发生交换就退出
}
}
} function swap(&$arr, $m, $n)
{
$arr[$m] = $arr[$m] ^ $arr[$n];
$arr[$n] = $arr[$n] ^ $arr[$m];
$arr[$m] = $arr[$m] ^ $arr[$n];
}

堆排序分析及php实现的更多相关文章

  1. Java实现---堆排序 Heap Sort

    堆排序与快速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法.学习堆排序前,先讲解下什么是数据结构中的二叉堆. 堆的定义 n个元素的序列{k1,k2,…,kn}当且仅当满足下列关 ...

  2. 浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析之后续补充说明(有图有真相)

    如果你觉得我的有些话有点唐突,你不理解可以想看看前一篇<C++之冒泡排序.希尔排序.快速排序.插入排序.堆排序.基数排序性能对比分析>. 这几天闲着没事就写了一篇<C++之冒泡排序. ...

  3. 八大排序算法——堆排序(动图演示 思路分析 实例代码java 复杂度分析)

    一.动图演示 二.思路分析 先来了解下堆的相关概念:堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆:或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆.如 ...

  4. 排序算法(三)堆排序及有界堆排序Java实现及分析

    1.堆排序基数排序适用于大小有界的东西,除了他之外,还有一种你可能遇到的其它专用排序算法:有界堆排序.如果你在处理非常大的数据集,你想要得到前 10 个或者前k个元素,其中k远小于n,它是很有用的. ...

  5. Javascript中的冒泡排序,插入排序,选择排序,快速排序,归并排序,堆排序 算法性能分析

    阿里面试中有一道题是这样的: 请用JavaScript语言实现 sort 排序函数,要求:sort([5, 100, 6, 3, -12]) // 返回 [-12, 3, 5, 6, 100],如果你 ...

  6. 9, java数据结构和算法: 直接插入排序, 希尔排序, 简单选择排序, 堆排序, 冒泡排序,快速排序, 归并排序, 基数排序的分析和代码实现

    内部排序: 就是使用内存空间来排序 外部排序: 就是数据量很大,需要借助外部存储(文件)来排序. 直接上代码: package com.lvcai; public class Sort { publi ...

  7. STL堆排序&时间复杂度分析

    1. 逻辑&时间复杂度分析 pop 和 initialize 的时间复杂度请参考: [DSAAinC++] 大根堆的pop&remove&initialize 将数组初始化为一 ...

  8. 堆排序与优先队列——算法导论(7)

    1. 预备知识 (1) 基本概念     如图,(二叉)堆是一个数组,它可以被看成一个近似的完全二叉树.树中的每一个结点对应数组中的一个元素.除了最底层外,该树是完全充满的,而且从左向右填充.堆的数组 ...

  9. 堆排序(python实现)

    堆排序是利用最大最或最小堆,废话不多说: 先给出几个概念: 二叉树:二叉树是每个节点最多有两个子树的树结构.通常子树被称作“左子树”(left subtree)和“右子树” 完全二叉树:除最后一层外, ...

随机推荐

  1. AngularJS 系列 02 - 模块

    引导目录: AngularJS 系列 学习笔记 目录篇 前言: 其实,在上篇文章介绍数据绑定的时候,我们的HelloWorld的代码案例中就已经使用了模块(module).哈哈. 本篇就着重介绍一下a ...

  2. 【Linux】虚拟机安装Archlinux

    参考:https://wiki.archlinux.org/index.php/Installation_guide_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87) 安装 ...

  3. [游戏开发-学习笔记]菜鸟慢慢飞(四)-Camera

    游戏开发中,主相机应该是最重要的GameObject之一,毕竟游戏呈现给玩家,就是通过它. 相机的使用,在不同的游戏中,有很大的不同.这里总结一下自己学到的一些相关知识. 固定位置-游戏过程中相机的T ...

  4. javascript中的自执行函数

    学习es6的时候遇到了自执行函数,感觉有必要写下来,一方面加深自己的记忆,另一方面还能分享给大家. 什么是自执行函数? 自执行函数就是为了不污染全局变量命名空间的一中匿名函数,相当于自己创建了一个作用 ...

  5. Nhibernate mapping 文件编写

    生成工具软件 现在生成工具软件有很多了,例如商业软件:NMG.CodeSmith.Visual NHibernate,开源软件:MyGeneration.NHibernate Modeller.AjG ...

  6. 机器学习之sklearn——聚类

    生成数据集方法:sklearn.datasets.make_blobs(n_samples,n_featurs,centers)可以生成数据集,n_samples表示个数,n_features表示特征 ...

  7. [LeetCode] Graph Valid Tree 图验证树

    Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), ...

  8. Spark Rdd coalesce()方法和repartition()方法

    在Spark的Rdd中,Rdd是分区的. 有时候需要重新设置Rdd的分区数量,比如Rdd的分区中,Rdd分区比较多,但是每个Rdd的数据量比较小,需要设置一个比较合理的分区.或者需要把Rdd的分区数量 ...

  9. 语义网 (Semantic Web)和 web 3.0

    语义网=有意义的网络. "如果说 HTML 和 WEB 将整个在线文档变成了一本巨大的书,那么 RDF, schema, 和 inference languages 将会使世界上所有的数据变 ...

  10. web前端面试总结

    本文由我收集总结了一些前端面试题,初学者阅后也要用心钻研其中的原理,重要知识需要系统学习.透彻学习,形成自己的知识链.万不可投机取巧,临时抱佛脚只求面试侥幸混过关是错误的!也是不可能的! 前端还是一个 ...