1. 说明:
2. 时间复杂度
操作 | 时间复杂度 |
---|
入队 | O(logn) |
出队 | O(logn) |
3. 插入结点的上浮操作(为了将最大值放在最顶部)(在代码siftUp方法中)
4. 弹出最大结点后对最小值的下沉操作(在代码siftDown方法中)
5. 代码
<?php
/**
* content: 堆的实现
* auth: 俞佳明
* creat: 2020-11-09
*/
namespace HeapBundle;
use ArrayBundle\BaseArray;
class TreeHeap
{
/**
* 数据
* @var BaseArray
*/
protected $data;
public function setData(BaseArray $baseArray): self
{
$this->data = $baseArray;
return $this;
}
public function __construct()
{
$this->data = new BaseArray();
}
/**
* 获取堆的大小
* @return int
*/
public function getSize(): int
{
return $this->data->getSize();
}
/**
* 获取父结点的索引
* @param int $index
* @return int
* @throws \Exception
*/
public function parentIndex(int $index): int
{
if ($index <= 0) {
throw new \Exception('索引必须大于0!');
}
return ($index - 1) / 2;
}
/**
* 获取左儿子的索引
* @param int $index
* @return int
*/
public function leftChildIndex(int $index): int
{
return $index * 2 + 1;
}
/**
* 获取右儿子的索引
* @param int $index
* @return int
*/
public function rightChildIndex(int $index): int
{
return $index * 2 + 2;
}
/**
* 获取最大的值
* @return string|int|null
*/
public function getMaxValue()
{
return $this->data->getSize() > 0 ? $this->data->getFirst() : null;
}
/**
* 弹出堆中最大的元素
* @return int|string|null
* @throws \Exception
*/
public function popMaxValue()
{
// 如果不存在堆,就直接返回
$maxValue = $this->getMaxValue();
if (is_null($maxValue)) return null;
$this->data->swap(0, $this->data->getSize() - 1);
$this->data->del($this->data->getSize() - 1);
// 最小值下沉
$this->siftDown(0);
return $maxValue;
}
/**
* 添加数据
* @param $value
* @throws \Exception
*/
public function add($value)
{
// 往数组的末尾追加元素
$this->data->addLast($value);
// 获取最后一个索引 并做 上浮操作
$this->siftUp($this->data->getSize() - 1);
}
/**
* 上浮操作,将最大的值上浮到最顶部
* @param int $index 当前的索引
* @throws \Exception
*/
protected function siftUp(int $index)
{
// 如果索引大于0 且 父结点的值比当前结点的值要来的小, 则进入循环
while ($index > 0 && bccomp($this->data->get($this->parentIndex($index)), $this->data->get($index)) < 0) {
// 交换数组的索引下标
$this->data->swap($index, $this->parentIndex($index));
// 将父节点的索引变为当前索引,继续循环
$index = $this->parentIndex($index);
}
}
/**
* 下沉操作,将最小值下沉到最底部
* @param int $index
* @throws \Exception
*/
protected function siftDown(int $index)
{
while ($this->leftChildIndex($index) < $this->data->getSize()) {
// 获取儿子结点的索引
$childIndex = $this->leftChildIndex($index);
// 比较左儿子和右儿子结点的值,将最大值的结点索引赋值给儿子结点索引
if (bccomp($this->data->get($this->rightChildIndex($index)), $this->data->get($childIndex)) > 0) {
$childIndex = $this->rightChildIndex($index);
}
// 比较结点值与儿子结点的值,如果比儿子结点要大就不处理
if (bccomp($this->data->get($index), $this->data->get($childIndex)) >= 0) {
break;
}
// 结点与儿子结点做换位
$this->data->swap($index, $childIndex);
// 往下赋值
$index = $childIndex;
}
}
/**
* 替换堆中的最大值
* @param $newValue
* @return bool
* @throws \Exception
*/
public function replaceMaxValue($newValue): bool
{
$maxValue = $this->getMaxValue();
if (is_null($maxValue)) return false;
$this->data->set(0, $newValue);
$this->siftDown(0);
return true;
}
/**
* 堆化
* @throws \Exception
*/
public function heapify()
{
for ($i = $this->data->getSize() - 1; $i >= 0; $i--) {
$this->siftDown($i);
}
}
/**
* 打印数据
* @return void
*/
public function varDump(): void
{
echo (string)$this->data. PHP_EOL;
}
}
6. 示例
<?php
require_once __DIR__ . '/../../vendor/autoload.php';
$heap = new HeapBundleTreeHeap();
1
for ($i = 0; $i < 10; $i++) {
$heap->add($i);
}
for ($i = 0; $i < 10; $i++) {
var_dump($heap->popMaxValue());
}
int(9)
int(8)
int(7)
int(6)
int(5)
int(4)
int(3)
int(2)
int(1)
int(0)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。