最小索引堆
性质:
1、最小堆的根节点小于等于他的子节点
2、完全二叉树。
方法:
add($val) 向对添加元素
getMin() 获取对的最小元素
extractMin() 获取堆的最小元素,并且从堆中删除这个最小元素
replace($val) 使用这个元素,替代根节点,并且重新维持堆的性质
heapify($data) 把数组构造一个堆。
siftdown和siftUP,维持堆的性质的操作。
class TreeNode {
public $val = null;
public $left = null;
public $right = null;
function __construct($value) { $this->val = $value; }
}
class MinIndexHeap{
public $size = 0 ;
public $data = [];
public $index_arr = [];
public function add($val){
$this->size ++;
$this->data[] = $val;
$k = $this->size - 1;
$this->index_arr[] = $k;
$this->siftUp($k);
}
public function siftUp($index){
while($index > 0 && $this->data[$this->index_arr[$index]] < $this->data[$this->index_arr[$this->_parent($index)]] ){
$this->swap($index, $this->_parent($index));
$index = $this->_parent($index);
}
}
public function swap($i,$j){
if($i<0 || $i >= $this->size || $j < 0 || $j >= $this->size ){
throw new \Exception('数组越界');
}
$tmp = $this->index_arr[$i];
$this->index_arr[$i] = $this->index_arr[$j];
$this->index_arr[$j] = $tmp;
}
public function replace($val){
$min = $this->getMin();
$this->data[$this->index_arr[0]] = $val;
$this->siftDown(0);
return $min;
}
public function getMin(){
if($this->size == 0){
throw new \Exception('没有元素');
}
return $this->data[$this->index_arr[0]];
}
public function extractMin(){
$min = $this->getMin();
$tmp = $this->index_arr[0];
$this->index_arr[0] = $this->index_arr[$this->size - 1];
unset($this->data[$tmp]);
array_pop($this->index_arr);
$this->size --;
$this->siftDown(0);
return $min;
}
public function siftDown($index){
while($this->leftChild($index) < $this->size){
$c = $this->leftChild($index);
if($c+1 < $this->size && $this->data[$this->index_arr[$c]] > $this->data[$this->index_arr[$c+1]]){
$c = $this->rightChild($index);
}
if($this->data[$this->index_arr[$index]] > $this->data[$this->index_arr[$c]]){
$this->swap($index, $c);
$index = $c;
}else{
break;
}
}
}
public function heapify($data){
if(!empty($this->data)){
throw new \Exception('Heap不为空,不能进行heapify操作');
}
$this->data = $data;
$this->index_arr = array_keys($this->data);
$this->size = count($this->index_arr);
$last_not_leaf = $this->_parent($this->size - 1);
while($last_not_leaf >=0){
$this->siftDown($last_not_leaf);
$last_not_leaf --;
}
}
public function destory(){
$this->data = [];
$this->size = 0;
$this->index_arr = [];
}
public function getAll(){
$arr = [];
foreach ($this->index_arr as $val){
$arr[] = $this->data[$val];
}
return $arr;
}
public function _parent($index){
if($index == 0){
throw new \Exception('没有父元素');
}
return intdiv($index -1, 2);
}
public function leftChild($index){
return 2 * $index +1;
}
public function rightChild($index){
return 2 * $index +2;
}
}
使用上面的最小堆,出现频率前 k 高的元素
解题思路:最小堆的根节点最小,其他节点都大于等于根节点,所以求前k个频率最大的元素,就是用频率构造一个最小堆。在堆满了之后,如果有频率大于堆的根节点,就说明这个根节点不适合在堆里,需要被频率更大的替换掉。这样所有的频率都遍历一遍,完成前k大频率都在堆里面了。然后需要根据频率找到对应的元素。就ok了。
include "./MinIndexHeap.php";
class Solution {
function topKFrequent($nums, $k) {
if(empty($nums) || $k<=0){
return [];
}
$res = [];
$res2 = [];
$times_arr = array_count_values($nums);
$min_heep = new MinIndexHeap();
foreach ($times_arr as $val){
if($min_heep->size < $k){
$min_heep->add($val);
}else if($min_heep->getMin() < $val){
$min_heep->replace($val);
}
}
$low_k = $min_heep->getMin();
foreach($times_arr as $k=>$val){
if($val >= $low_k){
if(isset($res[$val])){
array_push($res[$val], $k);
}else{
$res[$val] = [$k];
}
}
}
krsort($res, SORT_NUMERIC);
foreach($res as $val){
foreach($val as $val2){
$res2[] = $val2;
}
}
return $res2;
}
}
$nums = [5,3,1,1,1,3,73,1,6,6,6,6,5,5,5];
$k=4;
$obj = new Solution();
$res = $obj->topKFrequent($nums, $k);
print_r($res);
Array
(
[0] => 5
[1] => 1
[2] => 6
[3] => 3
)
打开App,阅读手记