1 <?php 2 #查找数组中最小的k个数 3 4 function swap(&$arr, $i, $j) { 5 $temp = $arr[$i]; 6 $arr[$i] = $arr[$j]; 7 $arr[$j] = $temp; 8 } 9 10 #第一种方法,使用选择排序直到排好序的部分元素个数为k,效率为O(kn) 11 function select_sort_k($arr, $k) { 12 if ($k >= count($arr)) { 13 return $arr; 14 } 15 for ($i = 0; $i < count($arr); $i++) { 16 $min = $i; 17 for ($j = $i; $j < count($arr); $j++) { 18 if ($arr[$j] < $arr[$min]) { 19 $min = $j; 20 } 21 } 22 23 swap($arr, $i, $min); 24 25 if ($i == $k - 1) { 26 return array_slice($arr, 0, $k); 27 } 28 } 29 } 30 31 #第二种方法,通过建立最小堆,然后弹出最小元素来实现,效率为O(klogn) 32 33 #调整堆节点 34 #@param a 待调整数组 35 #@param start 待调整部分开始坐标 36 #@param len 待调整数组的长度 37 function min_heapify(&$a, $start, $len) { 38 $min = $start; 39 $left = ($start + 1) * 2 - 1; #左孩子坐标 40 $right = $left + 1; #右孩子坐标 41 42 if ($left < $len) { 43 if ($a[$min] > $a[$left]) { 44 $min = $left; 45 } 46 } 47 48 if ($right < $len) { 49 if ($a[$min] > $a[$right]) { 50 $min = $right; 51 } 52 } 53 54 #递归调整节点 55 if ($min != $start) { 56 swap($a, $min, $start); 57 min_heapify($a, $min, $len); 58 } 59 } 60 61 #建立最小堆 62 function build_min_heap(&$a) { 63 #从最后一个非叶子节点开始调整堆 64 for ($i = floor(count($a) / 2) - 1; $i >= 0; $i--) { 65 min_heapify($a, $i, count($a)); 66 } 67 } 68 69 #利用类似于堆排序的过程查找最小的k个元素 70 function heap_min_k(&$a, $k) { 71 build_min_heap($a); 72 for ($i = 0; $i < $k; $i++) { 73 #交换首尾元素,并重新调整堆 74 swap($a, 0, count($a) - 1); 75 $r[] = array_pop($a); 76 min_heapify($a, 0, count($a)); 77 } 78 return $r; 79 } 80 81 #第三种方法,利用快排的partition来查找 82 function rand_partition(&$a, $start, $end) { 83 swap($a, $start, rand($start, $end)); 84 return partition($a, $start, $end); 85 } 86 87 function partition(&$a, $start, $end) { 88 $pivot = $start; 89 $low = $start + 1; 90 $high = $end; 91 while ($low < $high) { 92 while ($low < $high && $a[$low] <= $a[$pivot]) { 93 $low++; 94 } 95 96 #这里表示把所有与pivot相等的元素放到左边 97 while ($low < $high && $a[$high] > $a[$pivot]) { 98 $high--; 99 } 100 101 swap($a, $low, $high); 102 } 103 104 $change = $low; 105 if ($a[$pivot] < $a[$change]) { 106 $change--; 107 } 108 swap($a, $change, $pivot); 109 return $change; 110 } 111 112 #利用类似于快排的性质来查找k个最小的元素 113 function qsort_k(&$a, $start, $end, &$result, $k) { 114 $q = rand_partition($a, $start, $end); 115 $n = $q - $start + 1; #n表示包括q,小于等于q的元素的总数 116 117 if ($n == $k) { #如果n = k,说明左边的元素正好足够,全部加入结果集 118 array_splice($result, count($result), 0, array_slice($a, $start, $n)); 119 } else if ($n < $k) { #如果n < k, 说明左边的元素不够k个,将左边的元素和q一起归入结果集,并进入递归 120 array_splice($result, count($result), 0, array_slice($a, $start, $n)); 121 qsort_k($a, $q + 1, $end, $result, $k - $n); 122 } else { 123 #元素有多,进入左边继续递归 124 qsort_k($a, $start, $q - 1, $result, $k); 125 } 126 } 127 128 $a = array(5, 7, 3, 2, 4, 6, 9, 0, 1); 129 print_r($a); 130 echo "<br>"; 131 print_r(select_sort_k($a, 5)); 132 echo "<br>"; 133 echo "<br>"; 134 135 $a = array(5, 7, 3, 2, 4, 6, 9, 0, 1); 136 print_r($a); 137 echo "<br>"; 138 print_r(heap_min_k($a, 5)); 139 echo "<br>"; 140 echo "<br>"; 141 142 $a = array(5, 7, 3, 2, 4, 6, 9, 0, 1); 143 print_r($a); 144 echo "<br>"; 145 $r = array(); 146 qsort_k($a, 0, count($a) - 1, $r, 5); 147 print_r($r); 148 ?>
Array ( [0] => 5 [1] => 7 [2] => 3 [3] => 2 [4] => 4 [5] => 6 [6] => 9 [7] => 0 [8] => 1 )
Array ( [0] => 0 [1] => 1 [2] => 2 [3] => 3 [4] => 4 )
Array ( [0] => 5 [1] => 7 [2] => 3 [3] => 2 [4] => 4 [5] => 6 [6] => 9 [7] => 0 [8] => 1 )
Array ( [0] => 0 [1] => 1 [2] => 2 [3] => 3 [4] => 4 )
Array ( [0] => 5 [1] => 7 [2] => 3 [3] => 2 [4] => 4 [5] => 6 [6] => 9 [7] => 0 [8] => 1 )
Array ( [0] => 0 [1] => 1 [2] => 3 [3] => 2 [4] => 4 )