PHP 实现二叉堆的操作类 以及 堆排序

tips:  此处默认最小堆

应用:优先队列

<?php
/**
 * 堆的代码实现
 * 二叉堆本质上是一种完全二叉树
 * 二叉堆虽然是一颗完全二叉树,但它的存储方式并不是链式存储,而是顺序存储。换句话说,二叉堆的所有节点都存储在数组当中。
 */
class HeapOperator 
{	
	/**
	 * 上浮调整
	 * @param array     待调整的堆
	 * @param up_key    堆中上调的下标(默认最后一位)
	 */
	public function upAdjust($array,$up_key = -1) {	
		$count = count($array);	 
		if ($up_key == -1) {
			$childIndex = $count-1;   # 插入节点值
		} else {
			$childIndex = $up_key;
		}
		$parentIndex = ($childIndex-1)/2;  #当前父节点值 
		// temp保存插入的叶子节点值,用于最后的赋值   
		$temp = $array[$childIndex];
		while ($childIndex > 0 && $temp < $array[$parentIndex]) {
			//无需真正交换,单向赋值即可
			$array[$childIndex] = $array[$parentIndex];
        	$childIndex = $parentIndex;
        	$parentIndex = ($parentIndex-1) / 2;
		}
		$array[$childIndex] = $temp;
		return $array;
	}

	/**
	 * 下沉调整()
	 * @param array     待调整的堆
	 * 把最后一位补到删除节点位置,然后比较
	 * @param parentIndex    要下沉的父节点
	 * @param parentIndex    堆的有效大小
	 */
	public function downAdjust($array, $parentIndex, $length) {
		// temp保存父节点值,用于最后的赋值
		$temp = $array[$parentIndex];

		$childIndex = 2 * $parentIndex +1;
		while($childIndex < $length) {      
			// 如果有右孩子,且右孩子小于左孩子的值,则定位到右孩子       
			if($childIndex + 1 < $length && $array[$childIndex + 1] < $array[$childIndex]) {
            	$childIndex++;   # 当前孩子节点为右节点
			} 
			#  否则为左节点
			// 如果父节点小于任何一个孩子的值,直接跳出    
			if ($temp <= $array[$childIndex]) {
				break;
			}
			//无需真正交换,单向赋值即可 (父节点与子节点交换)
		    $array[$parentIndex] = $array[$childIndex];  # 父=子 换值  
		    $parentIndex = $childIndex;  
        	$childIndex = 2* $childIndex + 1;  # 换下标   
        }
        # 此时的 parentIndex 是最终参与交换的子节点
	    $array[$parentIndex] = $temp;
	    return $array;
	}
	/**
	 * 构建二叉堆
	 * 构建二叉堆,也就是把一个无序的完全二叉树调整为二叉堆,本质上就是 从最后一个非叶子节点开始,让所有非叶子节点依次下沉。
	 * @param arr     待调整的堆
	 */
	public function buildHeap($arr) {
		// 从最后一个非叶子节点开始,依次下沉调整
		$count = count($arr);
		for($i = floor($count / 2);$i >= 0; $i--){
	        $arr = self::downAdjust($arr,$i, $count);
	    }
	    return $arr;
	}
	/**
	 * 插入节点
	 * 思路:先插入最后位置节点,然后上浮
	 * @param arr     待插入的堆(已有序的完全二叉堆)
	 * @param value   插入的节点值
	 */
	public function insertNode($arr,$value) {
		$count = count($arr);
		# $value 的下标为$count
		$arr[$count] = $value;
		$array = self::upAdjust($arr);
		return $array;
	}
	/**
	 * 删除节点
	 * 思路:删除该节点,最后一个节点补位,然后依次下沉
	 * @param arr     待调整的堆(已有序的完全二叉堆)
	 * @param del_key   删除的节点下标值
	 */
	public function deleteNode($arr,$del_key) {
		# 删除节点
		$count = count($arr);
		if ($del_key || $del_key == 0) {
		    unset($arr[$del_key]);
		} 
		if ($del_key > floor($count/2) ) {
			return $arr;
		}
		# 补位
		$arr[$del_key] = $arr[$count-1];
		unset($arr[$count-1]);
		# 按键值排序
		ksort($arr);
		$length = count($arr);
		# 下沉
		$array = self::downAdjust($arr, $del_key, $length);
		return $array;
	}

    /**
	 * 堆排序
	 * 思路:1. 把无序数组构建成二叉堆。
	 *      2. 循环删除堆顶元素,移到集合尾部,调节堆产生新的堆顶。
	 * 空间复杂度:O(1)   为开辟新的新的空间
	 * 时间复杂度:O(nlogn)「 不稳定排序 」
	 * @param arr     待调整的堆(已有序的完全二叉堆,此处有序是指符合二叉堆特性,并非数组有序,如果是无序数组,可以先调用上方 buildHeap 构建二叉堆,这里不做赘述)
	 */
	public function heapSort($arr) {
		$count = count($arr);
		for ($i = $count-1; $i > 0 ; $i--) { 
			# 取出第一个元素与最后一个元素交换位置
			$first = $arr[0];

			$arr[0] = $arr[$i];
			$arr[$i] = $first;
			# 之后最后一个元素(原第一个元素)不参与下沉操作
			# 将现在的二叉堆,首元素进行下沉操作
			$arr = self::downAdjust($arr, 0, $i);
			// print_r(implode(",", $arr));
			// echo "\n";
		}

		return $arr;
	}
}

调用:

<?php

require_once("erchadui.php");

$tes = new HeapOperator;

echo "上浮节点";
$array_o = [1,3,2,6,5,7,8,9,10,0];
$res_o = $tes->upAdjust($array_o); 
// var_dump($res_o);   
print_r(implode(",", $res_o));   
echo "\n" ;

echo "下沉节点";
$array_r = [1,11,2,6,5,7,8,9,10,9];
$res_r = $tes->downAdjust($array_r, 1, count($array_r));
print_r(implode(",", $res_r));
echo "\n" ;

echo "构建二叉树 \n";
$array_t = [7,1,3,10,5,2,8,9,6];
$res_t = $tes->buildHeap($array_t);
// var_dump($res_t);
print_r(implode(",", $res_t));
echo "\n" ;

echo "插入节点 \n";
$array_t = [1,5,2,6,7,3,8,9,10];
$res_t = $tes->insertNode($array_t,0);
print_r(implode(",", $res_t));
echo "\n" ;

echo "删除节点";
$array_f = [0,1,2,6,5,3,8,9,10,7];
$res_f = $tes->deleteNode($array_f,0);
print_r(implode(",", $res_f));
echo "\n" ;

echo "堆排序";
$array_f = [0,1,2,6,5,3,8,9,10,7];
$res_f = $tes->heapSort($array_f,0);
print_r(implode(",", $res_f));    # 此时是由大到小排序,如果想从小到大可以用最大堆
echo "\n" ;

思路参考:程序员小灰微信公众号

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值