commit
3faa8278ca
337
php/10_heap/Heap.php
Normal file
337
php/10_heap/Heap.php
Normal 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];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
56
php/10_heap/findmiddle.php
Normal file
56
php/10_heap/findmiddle.php
Normal 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
30
php/10_heap/main.php
Normal 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
26
php/10_heap/topn.php
Normal 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;
|
||||
}
|
@ -21,3 +21,7 @@
|
||||
|
||||
#### 09_stack
|
||||
* 队列链表实现
|
||||
#### 10_heap
|
||||
* main 堆的基本操作,堆排序
|
||||
* findmiddle 动态数据流求中位数
|
||||
* topn 动态数据流求top k
|
||||
|
@ -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/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user