Merge pull request #392 from hkui/master

php堆的基本操作
This commit is contained in:
wangzheng0822 2019-09-16 08:28:07 +08:00 committed by GitHub
commit 3faa8278ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 455 additions and 1 deletions

337
php/10_heap/Heap.php Normal file
View File

@ -0,0 +1,337 @@
<?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];
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace Algo_10;
require_once '../vendor/autoload.php';
$arr = [9, 8, 11, 4, 2, 6, 5, 1, -1, 3, 20, 10];
//$arr=[9,8,11,4,2,6,5,100];
findMiddle($arr);
//动态数据实时获取中位数
function findMiddle($arr)
{
//大顶堆
$bigHeap = new Heap(0, 1);
//小顶堆
$smallHeap = new Heap(0, 0);
foreach ($arr as $k => $v) {
if ($bigHeap->isEmpty()) {
$bigHeap->insert($v);
} else {
$bigPeak = $bigHeap->peak();
if ($v < $bigPeak) {
$bigHeap->insert($v);
} else {
$smallHeap->insert($v);
}
if ($bigHeap->count - $smallHeap->count > 1) {
$bigPeak = $bigHeap->deleteFirst();
$smallHeap->insert($bigPeak);
} elseif ($smallHeap->count - $bigHeap->count > 1) {
$smallPeak = $smallHeap->deleteFirst();
$bigHeap->insert($smallPeak);
}
}
//实时获取中位数
echo "现在的中位数为:".implode(',', midPeak($bigHeap, $smallHeap)) . PHP_EOL;
}
}
function midPeak($heap1, $heap2)
{
if ($heap1->count == $heap2->count) {
$midArr = [$heap1->peak(), $heap2->peak()];
} elseif ($heap2->count > $heap1->count) {
$midArr = [$heap2->peak()];
} else {
$midArr = [$heap1->peak()];
}
return $midArr;
}

30
php/10_heap/main.php Normal file
View File

@ -0,0 +1,30 @@
<?php
namespace Algo_10;
require_once '../vendor/autoload.php';
$arr=[50,3,60,70,45,20,100,0,58];
$heap=new Heap();
foreach ($arr as $v){
$heap->insert($v);
}
while(($r=$heap->deleteFirst())!==null){
echo $r." ";
}
echo PHP_EOL;
$heap1=new Heap(10);
foreach ($arr as $v){
$heap1->insertOnly($v);
}
$heap1->heapAll();
//堆化后的
print_r($heap1->dataArr);
//堆排序
$heap1->heapSort();
print_r($heap1->dataArr);

26
php/10_heap/topn.php Normal file
View File

@ -0,0 +1,26 @@
<?php
/**
*2.动态数据集合求top n
*/
namespace Algo_10;
require_once '../vendor/autoload.php';
$static_data=[2,5,3,1,0,7,6,10];
//第3大
/*
2,5,3 2
2,5,3 1 2
2,5,3,1,0 2
2,5,3,1,0,7 3
2,5,3,1,0,7,6 5
2,5,3,1,0,7,6,10 6
维持1个小顶堆 大小为3即可
*/
$heap=new Heap(3);
foreach ($static_data as $v){
echo "现在的第3大=>".$heap->topn($v).PHP_EOL;
}

View File

@ -21,3 +21,7 @@
#### 09_stack
* 队列链表实现
#### 10_heap
* main 堆的基本操作,堆排序
* findmiddle 动态数据流求中位数
* topn 动态数据流求top k

View File

@ -9,7 +9,8 @@
"Algo_07\\": "07_linkedlist/",
"Algo_08\\": "08_stack/",
"Algo_09\\": "09_queue/",
"Algo_24\\": "24_tree/"
"Algo_24\\": "24_tree/",
"Algo_10\\": "10_heap/"
}
}
}