algo/php/10_heap/Heap.php

339 lines
8.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Created by PhpStorm.
* User: 764432054@qq.com
* Date: 2019/9/5
* Time: 9:01
* 堆的基本操作(大顶堆,小顶堆 几种堆化方式堆排序动态数据流查top k ,查中位数)
*
*/
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];
}
}