Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I was solving very similar problem recently, it seems like SPL doesn't support updates. So </p> <p>I had to write my own heap.</p> <p>It isn't extra efficient, but it does what I need and it's much faster than sorting an array repeatedly... SPL heap is still much faster though...</p> <p>here it is...</p> <pre><code>class heap { public $members=array(); // these two are just for statistics private $swaps=0; private $recurs=array('lups'=&gt;0, 'ldowns'=&gt;0); public function insert($val){ if(is_array($val) &amp;&amp; empty($this-&gt;members)){ // because heapify is (in theory) more efficient foreach($val as $v){ $this-&gt;members[]=$v; } $this-&gt;heapify(); }else{ $emptyPosition=count($this-&gt;members); // count(members) gets index of first empty position, not last key $this-&gt;members[]=$val; // puts $val in $this-&gt;ladderup($emptyPosition); } } public function heapify(){ /* in case all the heap is broken, we can always use this to repair it. It should be more efficient to fill $members randomly and "repair" it with heapify after, than inserting things one by one*/ $start=max(0, floor( (count($this-&gt;members)-1)/2)); // find last parent for($i=$start;$i&gt;=0;$i--){ $this-&gt;ladderdown($i); } } private function ladderdown($index){ // recursively sifts down $index $this-&gt;recurs['ldowns']++; /* indexes of children they are stored at parent_position*2 and parent_position*2+1 but becouse php uses null-based array indexing, we have to modify it a little */ $iA=$index*2+1; $iB=$index*2+2; if($iA&lt;count($this-&gt;members)){ // check if children exist if($iB&lt;count($this-&gt;members)){ if($this-&gt;compare($iA, $iB)&gt;=0) $bigger=$iA; // if both exist, compare them, cause we want to swap with the bigger one ; I'm using "&gt;=" here, that means if they're equal, left child is used else $bigger=$iB; }else{ $bigger=$iA; // if only one children exists, use it } if($this-&gt;compare($bigger, $index)&gt;0){ // not using "&gt;=" here, there's no reason to swap them if they're same $this-&gt;swap($bigger, $index); $this-&gt;ladderdown($bigger); // continue with $bigger because that's the position, where the bigger member was before we swap()ped it } } } private function ladderup($index){ // sift-up, $this-&gt;recurs['lups']++; $parent=max(0, floor( ($index-1)/2)); // find parent index; this way it actualy swaps one too many times: at the end of sift-up-ing swaps the root with itself if($this-&gt;compare($index, $parent)&gt;0){ $this-&gt;swap($index, $parent); $this-&gt;ladderup($parent); } } public function root(){ if(count($this-&gt;members)){ return $this-&gt;members[0]; } return false; } public function extract(){ // removes and returns root member if(!count($this-&gt;members)) return false; $this-&gt;swap(0,count($this-&gt;members)-1); // swaps root with last member $result=array_pop($this-&gt;members); // removes last member (now root) $this-&gt;ladderdown(0); // root is on wrong position, sifts it down return $result; } public function update($index, $value){ if($index&lt;count($this-&gt;members)){ $this-&gt;members[$index]=$value; $this-&gt;ladderup($index); $this-&gt;ladderdown($index); } } public function delete($index){ // removes index from heap the same way as root is extracted $this-&gt;swap(count($this-&gt;members)-1, $index); // swaps index with last one array_pop($this-&gt;members); $this-&gt;ladderup($index); $this-&gt;ladderdown($index); } private function swap($iA, $iB){ // swaps two members $this-&gt;swaps++; $swap=$this-&gt;members[$iA]; $this-&gt;members[$iA]=$this-&gt;members[$iB]; $this-&gt;members[$iB]=$swap; } private function compare($iA, $iB){ $result=$this-&gt;members[$iA] - $this-&gt;members[$iB]; return $result; } public function stats($text=""){ // prints and resets statistics echo "STATS: $text... Sift-ups: ".$this-&gt;recurs['lups']." Sift-downs: ".$this-&gt;recurs['ldowns']." Swaps: ".$this-&gt;swaps." &lt;br&gt;"; $this-&gt;recurs=array('lups'=&gt;0, 'ldowns'=&gt;0); $this-&gt;swaps=0; } } //here's how to use it... $h=new heap; for($i=0; $i&lt;10000; $i++){ $h-&gt;insert(rand(1,1000)); } $h-&gt;stats("after inserting one-by-one"); while($biggest=$h-&gt;extract()); // note that $h-&gt;extract might return FALSE, but might return zero as well, if there was zero in the heap $h-&gt;stats("after extracting all roots (like in heapsort)"); echo "Now, heap is empty. Let's try whole array at once &lt;br&gt;"; for($i=0; $i&lt;10000; $i++){ $a[]=rand(1,1000); } $h-&gt;insert($a); // inserting whole array here, so heap will use more efficient heapify() $h-&gt;stats("after heapify"); echo "let's update two indexes&lt;br&gt;"; $h-&gt;update(1234,44444);// sure on top $h-&gt;stats("after update"); $h-&gt;update(8888,40000);// second place $h-&gt;stats("after update"); echo "extract biggest three indexes&lt;br&gt;"; echo $h-&gt;extract()." - this should be 44444&lt;br&gt;"; echo $h-&gt;extract()." - this should be 40000&lt;br&gt;"; echo $h-&gt;extract()." - this should be biggest number given by rand(1,1000)&lt;br&gt;"; $h-&gt;stats("after three extracts"); while($h-&gt;extract()); $h-&gt;stats("after extracting the rest"); </code></pre> <p>and result is:</p> <blockquote> <p>STATS: after inserting one-by-one... Sift-ups: 22651 Sift-downs: 0 Swaps: 12651 <br>STATS: after extracting all roots (like in heapsort)... Sift-ups: 0 Sift-downs: 116737 Swaps: 116737 <br>Now, heap is empty. Let's try whole array at once <br>STATS: after heapify... Sift-ups: 0 Sift-downs: 12396 Swaps: 7396 <br>let's update two indexes<br>STATS: after update... Sift-ups: 11 Sift-downs: 1 Swaps: 10 <br>STATS: after update... Sift-ups: 13 Sift-downs: 1 Swaps: 12 <br>extract biggest three indexes<br>44444 - this should be 44444<br>40000 - this should be 40000<br>1000 - this should be biggest number given by rand(1,1000)<br>STATS: after three extracts... Sift-ups: 0 Sift-downs: 42 Swaps: 42 <br>STATS: after extracting the rest... Sift-ups: 0 Sift-downs: 116652 Swaps: 116652 <br></p> </blockquote> <p>You will have to modify it a bit, but anyway, hope it helps..</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload