php 堆排序实用场合,PHP实现堆、堆排序以及索引堆

a4df18e884d933a30f4ba3dabc605d6f.gif

本文转载自:http://sunms.codefly.top/2016/12/20/php实现堆堆排序以及索引堆/

堆是一种非常常用的数据结构,常常用来实现优先队列。下面来说一下,堆的一些特性。

堆(二叉堆)的成立条件如下:具有n个元素的序列: ,(ki <= k2i,ki <= k2i+1) 或者 (ki >= k2i,ki >= k2i+1), (i = 1,2,3,4…n/2)。满足第一个条件的我们称之为最小堆,满足第二个条件的我们称之为最大堆。其实堆(二叉堆)我们可以用完全二叉树进行描述(堆本身我们可以理解成完全二叉树,不理解完全二叉树的可以先看一下),每一个父节点对应最多两个子节点,且父节点的值都是大于或者两个子节点的值的。下面的图片就是用完全二叉树描述的最大堆以及最小堆,大家可以看一下。

1460000007903915?w=300&h=125

在实现堆这种数据结构的时候,通常都会采用链表或者数组的形式,这里我们以数组的形式来实现一个堆,事实上利用数组来实现堆是一种经典的堆的实现方式。

利用数组实现堆的原理:假设我们将以数组中index为1的位置开始来存放堆中的元素,假设index为n的位置上存在一个元素,那么它的子元素在数组中存放位置就是index = 2*n 以及index = 2*n +1 。这里需要注意的是我们是以index为1的位置开始存放元素的,并不是以index=0来存放的,如果以index=0来存放,那么它的子元素在数组中存放的位置就是index = 2*n + 1以及index = 2*n + 2。

下面我们就用php初始化一个堆,先来看一下具体的思路。假设现在我们向length=n的数组堆中添加一个元素,那么数组堆中的前n个元素继续保持堆的特性,新添加的元素以后,length=n+1的数组就不再满足堆的特性了,所以我们需要对最后一个元素,也就是新添加的元素进行位置的调整。根据新添加的元素在数组中的索引,来计算出其父节点的索引,然后对新元素和父节点元素进行大小的比较,如果新添加的元素大于父节点元素,就需要进行数据的交换,如果新添加的元素小于等于父节点元素,就不需要进行比较了,因为新添加的元素正在其合适的位置之上。数据交换完成以后,可能还是破坏了堆的特性,需要继续进行刚才的步骤,直到需要比较的节点的索引小于1为止。

下面来看代码:

//初始化数组堆$heap_arr = [];//定义向堆中添加元素的方法function heap_add(&$heap_arr,$value){

$heap_arr[] = $value;

$count_arr = count($heap_arr); //进行堆的调整

if($count_arr > 1){

$n = $count_arr-1; while($n >= 2){ //查找父节点id

$parent_n = floor($n/2); //如果子节点的value大于其父节点的value,就进行交换

if($heap_arr[$n]>$heap_arr[$parent_n]){ //进行数据的交换

$temp = $heap_arr[$n];

$heap_arr[$n] = $heap_arr[$parent_n];

$heap_arr[$parent_n] = $temp;

$n = $parent_n;

}else{ //如果子节点的value小于等于父节点的value,直接退出

break;

}

}

}

}

heap_add($heap_arr,0);

heap_add($heap_arr,9);

heap_add($heap_arr,6);

heap_add($heap_arr,10);

heap_add($heap_arr,4);

heap_add($heap_arr,1);

heap_add($heap_arr,10);

heap_add($heap_arr,11);

heap_add($heap_arr,5);

print_r($heap_arr);

上面的代码我们就实现了向数组堆中添加元素,并且保证了数组$heap_arr一直保持堆的性质。下面,我们就来看一下如何获取上面数组堆中的数据。先看一下思路:我们上面实现的堆其实是一个最大堆,也就是说数组中索引为1的位置存放着整个数组中最大的元素(最小堆实现方式一样)。当我们获取出数组堆中最大值以后,当前的数组就不再满足树形的结构了,也就不再满足堆的特性了。但是因为堆本身就是一个完全二叉树,那么我们可以将数组中的最后一个元素放到索引为1的位置上去,这样的话,数组继续保持这树形结构,但仍然可能不是堆的特性。为了保证数组继续保持堆的特性,就需要调整当前的数组。调整的步骤,回去当前节点(索引为1的节点)的子节点,获取子节点中的最大值,然后对比当前节点以及子节点中的最大值进行比较,如果当前节点小于子节点中的最大值,就进行交换,依此类推,直到需要比较的节点索引大于数组最大的索引值为止。

具体看代码:

//定义获取最大值元素的函数function heap_del(&$heap_arr){

$count_arr = count($heap_arr); if($count_arr<=1){ return "";

} if($count_arr==2){ return $heap_arr[1];

} //如果数组的长度大于2个的时候

$last_value = $heap_arr[$count_arr-1]; //删除最后一个元素

unset($heap_arr[$count_arr-1]); //将数组中最后一个元素放到数组的第二个位置上去

$heap_arr[1] = $last_value; //进行下沉操作

$n = 1;

$max_node = $count_arr-2; while($n

$left_son_node = $n*2;

$right_son_node = $n*2+1; if($left_son_node>$max_node && $right_son_node>$max_node){ break;

} if($left_son_node<=$max_node && $right_son_node>$max_node){

$swap_node = $left_son_node;

} if($left_son_node>$max_node && $right_son_node<=$max_node){

$swap_node = $right_son_node;

} if($left_son_node<=$max_node && $right_son_node<=$max_node){ //进行比较,取出较大值

if($heap_arr[$left_son_node]>=$heap_arr[$right_son_node]){

$swap_node = $left_son_node;

}else{

$swap_node = $right_son_node;

}

} //进行数据的交换

if($heap_arr[$swap_node]>$heap_arr[$n]){

$temp = $heap_arr[$swap_node];

$heap_arr[$swap_node] = $heap_arr[$n];

$heap_arr[$n] = $temp;

$n = $swap_node;

}else{ break;

}

}

}

heap_del($heap_arr);

print_r($heap_arr);

上面我们就实现了获取最大值的方法。那么如何进行堆排序那?其实上面的获取最大值方式就是堆排序的实现方式,依次获取当前数组堆中的最大值,并且保持堆的性质。堆排序也是非常重要的一种排序方式,其时间复杂度是O(NlogN),并且是一种稳定的排序方式。但是上面的堆排序的代码我们可以继续优化,以提高排序的性能。排序的思路如下:根据上面的介绍,传递进来的数组是满足最大完全二叉树的结构的,但是整体不一定满足堆的特性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值