zoukankan      html  css  js  c++  java
  • 347、前 K 个高频元素 | 堆-JS

    给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

    示例 1:

    输入: nums = [1,1,1,2,2,3], k = 2
    输出: [1,2]
    

    示例 2:

    输入: nums = [1], k = 1
    输出: [1]

    提示:

    • 1 <= nums.length <= 105
    • k 的取值范围是 [1, 数组中不相同的元素的个数]
    • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的

    进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。

     1 class MinHeap {
     2   constructor(){
     3     this.heap = [];
     4   }
     5   swap(i1, i2){
     6     const temp = this.heap[i1];
     7     this.heap[i1] = this.heap[i2];
     8     this.heap[i2] = temp;
     9   }
    10   getParentIndex(i){  //获取父节点的值
    11     return (i-1) >> 1;  //二进制右移相当于除以2
    12   }
    13   getLeftIndex(i) {  //获取左结点
    14     return i * 2 + 1;
    15   }
    16   getRightIndex(i) {  //获取右结点
    17     return i * 2 + 2;
    18   }
    19 
    20   shiftUp(index){   //需要让父节点不断小于它的子节点
    21     if(index == 0){ return; }  //如果已经是根结点了就不用找了
    22     const parentIndex = this.getParentIndex(index);
    23     if(this.heap[parentIndex] && this.heap[parentIndex].value > this.heap[index].value){
    24       this.swap(parentIndex, index);  //如果父节点的值大于子节点则进行交换
    25       this.shiftUp(parentIndex)
    26     }
    27   }
    28   insert(value){  //插入,添加的方法
    29     this.heap.push(value);
    30     this.shiftUp(this.heap.length - 1);  //shiftUp就是上移操作,接收参数是上移时的下标
    31   }
    32   shiftDown(index){  //下移节点,直到子节点都大于当前节点的值
    33     // 需要获取它的左右子节点
    34     const leftIndex = this.getLeftIndex(index);
    35     const rightIndex = this.getRightIndex(index);
    36     if(this.heap[leftIndex] && this.heap[leftIndex].value < this.heap[index].value){  //小顶堆,父节点小于子节点
    37       this.swap(leftIndex, index);
    38       this.shiftDown(leftIndex);  //迭代,直到找到合适的位置
    39     }
    40     if(this.heap[rightIndex] && this.heap[rightIndex].value < this.heap[index].value){  //小顶堆,父节点小于子节点
    41       this.swap(rightIndex, index);
    42       this.shiftDown(rightIndex);  //迭代,直到找到合适的位置
    43     }
    44   }
    45 
    46   pop(){   //下移方法
    47     this.heap[0] = this.heap.pop();  // 把数组的最后一位转移到头部,相当于变相删除堆顶
    48     this.shiftDown(0);  //传什么下标,就对那个进行下移操作
    49   }
    50   peek(){ //获取堆顶,返回数组的头部
    51     return this.heap[0];
    52   }
    53   size(){  // 获取堆的大小
    54     return this.heap.length;
    55   }
    56 }
    57 /**
    58  * @param {number[]} nums
    59  * @param {number} k
    60  * @return {number[]}
    61  */
    62 var topKFrequent = function(nums, k) {
    63     const map = new Map();
    64     nums.forEach(n=>{
    65         map.set(n, map.has(n)?map.get(n)+1:1);
    66     })
    67     const h = new MinHeap();
    68     map.forEach((value, key) => {
    69         h.insert({ value,key });
    70         if(h.size() > k){  //超出堆的大小之后,就弹出来
    71             h.pop()  //把堆顶的删除,最小的元素在堆顶
    72         }
    73     });
    74     return h.heap.map( a => a.key)
    75 };

     小顶堆的数据结构:

    class MinHeap {
      constructor(){
        this.heap = [];
      }
      swap(i1, i2){
        const temp = this.heap[i1];
        this.heap[i1] = this.heap[i2];
        this.heap[i2] = temp;
      }
      getParentIndex(i){  //获取父节点的值
        return (i-1) >> 1;  //二进制右移相当于除以2
      }
      getLeftIndex(i) {  //获取左结点
        return i * 2 + 1;
      }
      getRightIndex(i) {  //获取右结点
        return i * 2 + 2;
      }
    
      shiftUp(index){   //需要让父节点不断小于它的子节点
        if(index == 0){ return; }  //如果已经是根结点了就不用找了
        const parentIndex = this.getParentIndex(index);
        if(this.heap[parentIndex] > this.heap[index]){
          this.swap(parentIndex, index);  //如果父节点的值大于子节点则进行交换
          this.shiftUp(parentIndex)
        }
      }
      insert(value){  //插入,添加的方法
        this.heap.push(value);
        this.shiftUp(this.heap.length - 1);  //shiftUp就是上移操作,接收参数是上移时的下标
      }
      shiftDown(index){  //下移节点,直到子节点都大于当前节点的值
        // 需要获取它的左右子节点
        const leftIndex = this.getLeftIndex(index);
        const rightIndex = this.getRightIndex(index);
        if(this.heap[leftIndex] < this.heap[index]){  //小顶堆,父节点小于子节点
          this.swap(leftIndex, index);
          this.shiftDown(leftIndex);  //迭代,直到找到合适的位置
        }
        if(this.heap[rightIndex] < this.heap[index]){  //小顶堆,父节点小于子节点
          this.swap(rightIndex, index);
          this.shiftDown(rightIndex);  //迭代,直到找到合适的位置
        }
      }
    
      pop(){   //下移方法
        this.heap[0] = this.heap.pop();  // 把数组的最后一位转移到头部,相当于变相删除堆顶
        this.shiftDown(0);  //传什么下标,就对那个进行下移操作
      }
      peek(){ //获取堆顶,返回数组的头部
        return this.heap[0];
      }
      size(){  // 获取堆的大小
        return this.heap.length;
      }
    }
  • 相关阅读:
    代码规范圣战
    代码复审结果
    个人工程总结
    第一周个人博客作业
    软工课程总结
    软件工程课程的建议
    大泥球你好~
    第一次会议记录
    软件工程的瀑布, 大泥球, 教堂,集市,和银弹
    vs2013——单元测试&& 性能图
  • 原文地址:https://www.cnblogs.com/oaoa/p/15028210.html
Copyright © 2011-2022 走看看