關於堆排序和topK算法的PHP實現

問題描述

topK算法,簡而言之,就是求n個數據裏的前m大個數據,通常而言,m<<n,也就是說,n可能有幾千萬,而m只是10或者20這樣的兩位數。php

思路

最簡單的思路,固然是使用要先對這n個數據進行排序,由於只有排序之後,才能按照順序來找出排在前面的,或者排在後面的數據。算法

假如說咱們用快拍,那麼時間複雜度是O(nlogn),可是仔細看題目,會發現實際上不要要將全部的數據就進行排序,由於咱們找的是前m個數據,因此對全部數據排序實際上有些浪費了。因此能夠想到,只維護一個大小爲m的數組,而後掃一遍原來的數組n,只將大於數組m裏的最小值的數據插入到m數組裏,而且從新調整m數組的順序。數組

若是使用樸素的方法對m數組進行調整,那麼時間複雜度將會是O(n*m),這顯然不是最優的結果。對於維護的數組m,咱們能夠經過維護一個堆結構,來達到每次排序O(logm)的時間複雜度,這樣topK算法,整體的複雜度也就變成了O(nlogm)。this

關於堆

二叉堆是徹底二叉樹或者是近似徹底二叉樹。spa

二叉堆知足二個特性:3d

1.父結點的鍵值老是大於或等於(小於或等於)任何一個子節點的鍵值。code

2.每一個結點的左子樹和右子樹都是一個二叉堆(都是最大堆或最小堆)。blog

當父結點的鍵值老是大於或等於任何一個子節點的鍵值時爲最大堆。當父結點的鍵值老是小於或等於任何一個子節點的鍵值時爲最小堆。下圖展現一個最小堆:通常都用數組來表示堆,i結點的父結點下標就爲(i – 1) / 2。它的左右子結點下標分別爲2 * i + 1和2 * i + 2。如第0個結點左右子結點下標分別爲1和2。排序

PHP實現的堆

  1 class Heap {
  2 
  3  
  4 
  5     protected $listSize;
  6 
  7     protected $tree;
  8 
  9  
 10 
 11     public function __construct($list) {
 12 
 13         $this->listSize = count($list);
 14 
 15         $i = 1;
 16 
 17         foreach ($list as $li) {
 18 
 19             $this->tree[$i++] = $li;
 20 
 21         }
 22 
 23         unset($list);
 24 
 25         $this->initHeap();
 26 
 27     }
 28 
 29  
 30 
 31     public function getSortedResult() {
 32 
 33         $this->initHeap();
 34 
 35         $this->sortHeap();
 36 
 37         return $this->tree;
 38 
 39     }
 40 
 41  
 42 
 43     public function getHeapResult() {
 44 
 45         return $this->tree;
 46 
 47     }
 48 
 49  
 50 
 51     public function getTopNode() {
 52 
 53         return $this->tree[1];
 54 
 55     }
 56 
 57  
 58
 59     public function setTopNode($value) {
 60 
 61         $this->tree[1] = $value;
 62 
 63         $this->adjustHeap(1, $this->listSize);
 64 
 65     }
 66 
 67  
 68 
 69     public function sortHeap() {
 70 
 71         for ($end = $this->listSize; $end > 1; $end--) {
 72 
 73             $this->swap($this->tree[1], $this->tree[$end]);
 74 
 75             $this->adjustHeap(1, $end - 1);
 76 
 77         }
 78 
 79     }
 80 
 81  
 82 
 83     private function initHeap() {
 84 
 85         for ($start=floor($len / 2); $start >= 1; $start--) {
 86 
 87             $this->adjustHeap($start, $this->listSize);
 88 
 89         }
 90 
 91     }
 92 
 93  
 94 
 95     private function adjustHeap($start, $len) {
 96 
 97         $tmp = $start;  // 臨時變量,用於保存最大值或者最小值的下標索引
 98 
 99         $lChildInx = $start * 2;
100 
101         $rChildInx = $lChildInx + 1;
102 
103         if ($start <= floor($len / 2)) {
104 
105             if($lChildInx <= $len && $this->tree[$lChildInx] < $this->tree[$tmp]) {
106 
107                 $tmp = $lChildInx;
108 
109             }
110 
111             if($rChildInx <= $len && $this->tree[$rChildInx] < $this->tree[$tmp]) {
112 
113                 $tmp = $rChildInx;
114 
115             }
116 
117             if ($tmp != $start) {
118 
119                 $this->swap($this->tree[$tmp], $this->tree[$start]);
120 
121                 $this->adjustHeap($tmp, $len);
122 
123             }
124 
125         }
126 
127     }
128 
129  
130 
131     private function swap(&$a, &$b) {
132 
133         $temp = $a;
134 
135         $a = $b;
136 
137         $b = $temp;
138 
139     }
140 
141  
142 
143 }

 

topK

 1 include 'Heap.class.php';   
 2 
 3  
 4 
 5 $list = range(1,10000);
 6 
 7 shuffle($list);
 8 
 9 $k = 15;
10 
11  
12 
13 $initHeapNodes = array_slice($list, 0, $k);
14 
15 $heap = new Heap($initHeapNodes);
16 
17  
18 
19 $n = count($list);
20 
21  
22 
23 for ($i=$k; $i<$n; $i++) {
24 
25     if ($list[$i] > $heap->getTopNode()) {
26 
27         $heap->setTopNode($list[$i]);
28 
29     }
30 
31 }
32 
33  
34 
35 print_r($heap->getSortedResult());