2019-09-05 08:59:32 +08:00
|
|
|
|
<?php
|
2019-09-05 09:04:53 +08:00
|
|
|
|
/**
|
|
|
|
|
* Created by PhpStorm.
|
|
|
|
|
* User: 764432054@qq.com
|
|
|
|
|
* Date: 2019/9/5
|
|
|
|
|
* Time: 9:01
|
|
|
|
|
* 堆的基本操作(大顶堆,小顶堆 几种堆化方式,堆排序,动态数据流查top k ,查中位数)
|
2019-09-23 19:19:07 +08:00
|
|
|
|
*
|
2019-09-05 09:04:53 +08:00
|
|
|
|
*/
|
2019-09-05 08:59:32 +08:00
|
|
|
|
namespace Algo_10;
|
|
|
|
|
|
|
|
|
|
class Heap
|
|
|
|
|
{
|
|
|
|
|
public $dataArr = [];
|
|
|
|
|
public $count = 0;
|
|
|
|
|
public $size; //堆的大小 0表示不限制大小自动扩容
|
|
|
|
|
public $heapType = 1; //1 表示大根堆 0表示小根堆
|
|
|
|
|
|
|
|
|
|
public function __construct($size = 0, $heapType = 1)
|
|
|
|
|
{
|
|
|
|
|
$this->size = $size;
|
|
|
|
|
$this->heapType = $heapType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $data
|
|
|
|
|
* 插入并堆化
|
|
|
|
|
*/
|
|
|
|
|
public function insert($data)
|
|
|
|
|
{
|
|
|
|
|
if ($this->isFull()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$this->dataArr[$this->count + 1] = $data;
|
|
|
|
|
$this->count++;
|
|
|
|
|
if ($this->heapType) {
|
|
|
|
|
$this->bigHeapLast();
|
|
|
|
|
} else {
|
|
|
|
|
$this->smallHeapLast();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return bool
|
|
|
|
|
* 堆是否满
|
|
|
|
|
*/
|
|
|
|
|
public function isFull()
|
|
|
|
|
{
|
|
|
|
|
if ($this->size == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if ($this->count >= $this->size) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
public function isEmpty(){
|
|
|
|
|
return empty($this->count)?true:false;
|
|
|
|
|
}
|
|
|
|
|
//返回堆顶的元素
|
|
|
|
|
public function peak(){
|
|
|
|
|
if($this->isEmpty()){
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return $this->dataArr[1];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//只插入
|
|
|
|
|
public function insertOnly($data)
|
|
|
|
|
{
|
|
|
|
|
if ($this->isFull()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
$this->dataArr[$this->count + 1] = $data;
|
|
|
|
|
$this->count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 删除堆顶的元素
|
|
|
|
|
* 把最后1个元素插入到堆顶
|
|
|
|
|
* 然后从堆顶开始堆化
|
|
|
|
|
* 返回堆化后的堆顶元素
|
|
|
|
|
*/
|
|
|
|
|
public function deleteFirst()
|
|
|
|
|
{
|
|
|
|
|
$first = $this->dataArr[1];
|
|
|
|
|
$last = array_pop($this->dataArr);
|
|
|
|
|
if($this->isEmpty()){
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
$this->count--;
|
|
|
|
|
$i = 1;
|
|
|
|
|
$this->dataArr[$i] = $last;
|
|
|
|
|
if ($this->heapType) {
|
|
|
|
|
$this->bigHeapFirst();
|
|
|
|
|
} else {
|
|
|
|
|
$this->smallHeapFirst();
|
|
|
|
|
}
|
|
|
|
|
return $first;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 从某一个结点开始向下堆化
|
|
|
|
|
*/
|
|
|
|
|
protected function heapFromOneToDown($i)
|
|
|
|
|
{
|
|
|
|
|
//大根堆
|
|
|
|
|
if ($this->heapType) {
|
|
|
|
|
$maxPos = $i;
|
|
|
|
|
while (true) {
|
|
|
|
|
if (2 * $i <= $this->count) {
|
|
|
|
|
if ($this->dataArr[$maxPos] < $this->dataArr[2 * $i]) {
|
|
|
|
|
$maxPos = 2 * $i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (2 * $i + 1 <= $this->count) {
|
|
|
|
|
if ($this->dataArr[$maxPos] < $this->dataArr[2 * $i + 1]) {
|
|
|
|
|
$maxPos = 2 * $i + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//不需要交换
|
|
|
|
|
if ($i == $maxPos) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
$tmp = $this->dataArr[$maxPos];
|
|
|
|
|
$this->dataArr[$maxPos] = $this->dataArr[$i];
|
|
|
|
|
$this->dataArr[$i] = $tmp;
|
|
|
|
|
//继续往下堆化
|
|
|
|
|
$i = $maxPos;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//小根堆
|
|
|
|
|
$minPos = $i;
|
|
|
|
|
while (true) {
|
|
|
|
|
if (2 * $i <= $this->count) {
|
|
|
|
|
if ($this->dataArr[$minPos] > $this->dataArr[2 * $i]) {
|
|
|
|
|
$minPos = 2 * $i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (2 * $i + 1 <= $this->count) {
|
|
|
|
|
if ($this->dataArr[$minPos] > $this->dataArr[2 * $i + 1]) {
|
|
|
|
|
$minPos = 2 * $i + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//不需要交换
|
|
|
|
|
if ($i == $minPos) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
$tmp = $this->dataArr[$minPos];
|
|
|
|
|
$this->dataArr[$minPos] = $this->dataArr[$i];
|
|
|
|
|
$this->dataArr[$i] = $tmp;
|
|
|
|
|
//继续往下堆化
|
|
|
|
|
$i = $minPos;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 对于1个完全不符合堆性质的 整体堆化
|
|
|
|
|
*/
|
|
|
|
|
public function heapAll()
|
|
|
|
|
{
|
|
|
|
|
for ($i = intval($this->count / 2); $i >= 1; $i--) {
|
|
|
|
|
$this->heapFromOneToDown($i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 堆排序
|
|
|
|
|
* 把堆顶部的元素和数组尾部元素交换
|
|
|
|
|
*/
|
|
|
|
|
public function heapSort()
|
|
|
|
|
{
|
|
|
|
|
$sorted = 0;//已经有序的个数
|
|
|
|
|
while ($sorted < $this->count) {
|
|
|
|
|
$i = 1;
|
|
|
|
|
$head = $this->dataArr[$i];
|
|
|
|
|
$this->dataArr[$i] = $this->dataArr[$this->count - $sorted];
|
|
|
|
|
$this->dataArr[$this->count - $sorted] = $head;
|
|
|
|
|
$sorted++;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
$maxPos = $i;
|
|
|
|
|
if (2 * $i <= $this->count - $sorted && $this->dataArr[$maxPos] < $this->dataArr[2 * $i]) {
|
|
|
|
|
$maxPos = 2 * $i;
|
|
|
|
|
}
|
|
|
|
|
if (2 * $i + 1 <= $this->count - $sorted && $this->dataArr[$maxPos] < $this->dataArr[2 * $i + 1]) {
|
|
|
|
|
$maxPos = 2 * $i + 1;
|
|
|
|
|
}
|
|
|
|
|
if ($i == $maxPos) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
$tmp = $this->dataArr[$i];
|
|
|
|
|
$this->dataArr[$i] = $this->dataArr[$maxPos];
|
|
|
|
|
$this->dataArr[$maxPos] = $tmp;
|
|
|
|
|
$i = $maxPos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*小顶堆 堆化
|
|
|
|
|
* 插入时
|
|
|
|
|
* 堆化最后1个元素
|
|
|
|
|
*/
|
|
|
|
|
public function smallHeapLast()
|
|
|
|
|
{
|
|
|
|
|
$i = $this->count;
|
|
|
|
|
while (true) {
|
|
|
|
|
$smallPos = $i;
|
|
|
|
|
$parent = intval($i / 2);
|
|
|
|
|
if ($parent >= 1) {
|
|
|
|
|
if ($this->dataArr[$smallPos] < $this->dataArr[$parent]) {
|
|
|
|
|
$smallPos = $parent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($smallPos == $i) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
$tmp = $this->dataArr[$smallPos];
|
|
|
|
|
$this->dataArr[$smallPos] = $this->dataArr[$i];
|
|
|
|
|
$this->dataArr[$i] = $tmp;
|
|
|
|
|
$i = $smallPos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 小根堆
|
|
|
|
|
* 堆化根部元素(第一个元素)
|
|
|
|
|
*/
|
|
|
|
|
public function smallHeapFirst()
|
|
|
|
|
{
|
|
|
|
|
$i = 1;
|
|
|
|
|
while (true) {
|
|
|
|
|
$smallpos = $i;
|
|
|
|
|
$left = 2 * $i;
|
|
|
|
|
if ($left <= $this->count) {
|
|
|
|
|
if ($this->dataArr[$smallpos] > $this->dataArr[$left]) {
|
|
|
|
|
$smallpos = $left;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$right = $left + 1;
|
|
|
|
|
if ($right <= $this->count) {
|
|
|
|
|
if ($this->dataArr[$smallpos] > $this->dataArr[$right]) {
|
|
|
|
|
$smallpos = $right;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($smallpos == $i) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
$tmp = $this->dataArr[$i];
|
|
|
|
|
$this->dataArr[$i] = $this->dataArr[$smallpos];
|
|
|
|
|
$this->dataArr[$smallpos] = $tmp;
|
|
|
|
|
$i = $smallpos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 大根堆
|
|
|
|
|
* 堆化根部元素(第一个元素)
|
|
|
|
|
*/
|
|
|
|
|
public function bigHeapFirst()
|
|
|
|
|
{
|
|
|
|
|
$i = 1;
|
|
|
|
|
while (true) {
|
|
|
|
|
$maxpos = $i;
|
|
|
|
|
$left = 2 * $i;
|
|
|
|
|
if ($left <= $this->count) {
|
|
|
|
|
if ($this->dataArr[$maxpos] < $this->dataArr[$left]) {
|
|
|
|
|
$maxpos = $left;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$right = $left + 1;
|
|
|
|
|
if ($right <= $this->count) {
|
|
|
|
|
if ($this->dataArr[$maxpos] < $this->dataArr[$right]) {
|
|
|
|
|
$maxpos = $right;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if ($maxpos == $i) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
$tmp = $this->dataArr[$i];
|
|
|
|
|
$this->dataArr[$i] = $this->dataArr[$maxpos];
|
|
|
|
|
$this->dataArr[$maxpos] = $tmp;
|
|
|
|
|
$i = $maxpos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
//大根堆, 插入节点后放到数组最后面,然后从插入的节点自下而上开始堆化
|
|
|
|
|
//这里只堆化插入元素相关的节点(就是说,如果没插入这个元素,这个是一个堆)
|
|
|
|
|
public function bigHeapLast()
|
|
|
|
|
{
|
|
|
|
|
$i = $this->count;
|
|
|
|
|
while (intval($i / 2) > 0 && $this->dataArr[$i] > $this->dataArr[intval($i / 2)]) {
|
|
|
|
|
$tmp = $this->dataArr[$i];
|
|
|
|
|
$this->dataArr[$i] = $this->dataArr[intval($i / 2)];
|
|
|
|
|
$this->dataArr[intval($i / 2)] = $tmp;
|
|
|
|
|
$i = $i / 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $data
|
|
|
|
|
*/
|
|
|
|
|
public function topn($data)
|
|
|
|
|
{
|
|
|
|
|
//堆满了
|
|
|
|
|
if ($this->isFull()) {
|
|
|
|
|
if ($data > $this->dataArr[1]) {
|
|
|
|
|
$this->dataArr[1] = $data;
|
|
|
|
|
$this->smallHeapFirst();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$this->dataArr[$this->count + 1] = $data;
|
|
|
|
|
$this->count++;
|
|
|
|
|
$this->smallHeapLast();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return $this->dataArr[1];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|