读取vector第i个元素_一日一力扣第62天:前 K 个高频元素

点击上方蓝字关注加小星星✨

涛哥给你我所有~

今天是 Kevin 的算法之路的第 62 天。为大家讲解 LeetCode 第 347 题,是一道中等难度且很经典的题目。

每日一笑

我洗了个头,掉了不少头发,但我不伤心,因为我还有个头,而我的头却啥也没有。

题目描述

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

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

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

提示:

你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。你可以按任意顺序返回答案。

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/top-k-frequent-elements 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

哈希表-优先队列

这道常考题的经典做法,必须掌握!

  • 先用 哈希表 来建立数字和其出现次数的映射,遍历一遍数组统计元素的频率
  • 维护一个元素数目为 K优先队列

这里要前K个高频元素,也就是按从大到小排序,所以使用最小堆。(同理,若要从小到大排序,则使用最大堆)

每次都将新的元素与堆顶元素(堆中频率最小的元素)进行比较;

如果新的元素的频率比堆顶端的元素大,则弹出堆顶端的元素,将新的元素添加进堆中;

所以最终,堆中的 k 个元素即为前 k 个高频元素。

代码实现

//go
// go中没有自带的优先队列,需要自己实现小根堆
func topKFrequent(nums []int, k int) []int {
 if k == 0 || len(nums) == 0 {
  return make([]int, 0)
 }
 // 1. 初始化map
 m := make(map[int]int)
 for _, v := range nums {
  m[v] = m[v] + 1
 }

 // 2. 放到小根堆里面
 h := &NodeHeap{}
 topK := min(k, len(m))
 size := 0
 for k, v := range m {
  if size    heap.Push(h, &Node{
    val:   k,
    times: v,
   })
   size++
  } else {
   if v > (*h)[0].times {
    heap.Pop(h)
    heap.Push(h, &Node{
     val:   k,
     times: v,
    })
   }
  }

 }

 // 3.收集答案
 res := make([]int, 0, topK)
 for i := 0; i   res = append(res, heap.Pop(h).(*Node).val)
 }
 return res
}

type Node struct {
 val   int
 times int
}

type NodeHeap []*Node

func (h NodeHeap) Len() int { return len(h) }

// 小根堆
func (h NodeHeap) Less(i, j int) bool { return h[i].times 
func (h NodeHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }

func (h *NodeHeap) Push(x interface{}) {
 *h = append(*h, x.(*Node))
}

func (h *NodeHeap) Pop() interface{} {
 old := *h
 n := len(old)
 x := old[n-1]
 *h = old[0 : n-1]
 return x
}

func min(a, b int) int {
 if a   return a
 }
 return b
}
//go 
// 偷懒不想手写堆的话,可以用下面这种简便的解法

func topKFrequent(nums []int, k int) []int {
    // map记录num和出现频率
    m := make(map[int]int)
    // slice记录出现过的num
    s := make([]int,0)
    for _, num := range nums {
        if _, ok := m[num]; ok {
            m[num] += 1
        }else {
            m[num] = 1
            s = append(s, num)
        }
    }

    // 降序排序
    sort.Slice(s, func(i, j int) bool {
        return m[s[i]] > m[s[j]]
    })

    return s[:k]

}
//java
//java有自带的优先队列,可以直接使用
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        ArrayList res = new ArrayList<>();
        HashMap map = new HashMap<>();for (int num : nums) {if (!map.containsKey(num)) {
                map.put(num, 1);
            } else {
                map.put(num, map.get(num) + 1);
            }
        }// 最小堆
        PriorityQueue pq = new PriorityQueue<>(new Comparator() {@Overridepublic int compare(Integer o1, Integer o2) {return map.get(o1) - map.get(o2);
            }
        });for (Integer key : map.keySet()) {if (pq.size()                 pq.add(key);
            } else if (map.get(key) > map.get(pq.peek())) {
                pq.remove();
                pq.add(key);
            }
        }while (!pq.isEmpty()) {
            res.add(pq.remove());
        }// List 转 Arrayreturn res.stream().mapToInt(Integer::valueOf).toArray();
    }
}

郑重声明:

所展示代码已通过 LeetCode 运行通过,请放心食用~

在唠唠嗑

很多人都想养成好习惯,但大多数人却是三分钟热度。为了我们能一起坚持下去,决定制定如下计划(福利)

一起学算法,打卡领红包!

参与方式:

关注我的微信公众号「Kevin的学堂」

还可「加群」与众多小伙伴

一起坚持,一起学习,一起更优秀!

打卡规则为:

「留言」“打卡XXX天” ➕「分享」到朋友圈

奖励:

打卡 21 天,联系本人获取 6.6 元红包一个!

打卡 52 天,联系本人获取 16.6 元红包一个!

打卡 100 天,联系本人获取 66.6 元红包一个!

aa3fa89953770acf62e201b996e9f18a.png
长按扫码,一起进步
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值