347. Top K Frequent Elements
題意
給定一個非空的整數陣列,返回其中出現頻率最高的 k 個元素。
題目理解
這道題目要求找出陣列中出現頻率最高的 k 個元素。換句話說,我們需要對陣列中每個元素的出現次數進行統計,然後根據這些次數找出出現次數最多的前 k 個元素。
限制條件
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
k
的範圍是[1, 陣列中不同元素的數量]
- 題目保證答案是唯一的,也就是說前 k 個頻率最高的元素的出現次數不會相同
解題思路
解法一:最大堆積(Max Heap)
- 統計頻率:使用一個雜湊表(Hash Map)來統計陣列中每個元素出現的次數。
- 建立最大堆積:將每個元素及其頻率放入一個最大堆積中,堆積按照頻率從高到低排序。
- 收集結果:從堆積中取出前 k 個元素,即為所需結果。
解題說明
- 計算頻率:首先,我們用一個雜湊表
counter
來計算每個元素在陣列中出現的次數。 - 使用最大堆積:接下來,使用一個優先佇列(Priority Queue)作為最大堆積,將每個元素及其頻率放入其中。優先佇列的比較依據是元素的出現次數。
- 收集結果:最後,從優先佇列中取出前 k 個元素,放入結果陣列中。
複雜度分析
- 時間複雜度:O(n log n)
- 計算頻率:遍歷陣列一次,時間複雜度為 O(n)。
- 建立堆積:將所有元素插入堆積中,時間複雜度為 O(n log n)。
- 收集結果:從堆積中取出 k 個元素,時間複雜度為 O(k log n)。
- 空間複雜度:O(n)
- 雜湊表:儲存每個元素的出現次數,最多有 n 個不同的元素,空間複雜度為 O(n)。
- 堆積:最多儲存 n 個元素,空間複雜度為 O(n)。
程式碼
C++ 實作
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
const int n = nums.size();
unordered_map<int, int> counter;
priority_queue<pair<int, int>> pq;
for (const int num : nums) {
counter[num] += 1;
}
for (const auto [num, count] : counter) {
pq.push({ count, num });
}
vector<int> ans;
while (!pq.empty() && k--) {
ans.push_back(pq.top().second);
pq.pop();
}
return ans;
}
};
Java 實作
class Solution {
public int[] topKFrequent(int[] nums, int k) {
HashMap<Integer, Integer> counter = new HashMap<>();
for (int num : nums) {
counter.put(num, counter.getOrDefault(num, 0) + 1);
}
PriorityQueue<Map.Entry<Integer, Integer>> maxHeap =
new PriorityQueue<>((a, b) -> (b.getValue() - a.getValue()));
for (Map.Entry<Integer, Integer> entry : counter.entrySet()) {
maxHeap.add(entry);
}
int[] ans = new int[k];
for (int i = 0; i < k; i += 1) {
ans[i] = maxHeap.poll().getKey();
}
return ans;
}
}
解法二:桶排序(Bucket Sort)
- 統計頻率:使用一個雜湊表來統計陣列中每個元素出現的次數。
- 建立桶:創建一個桶陣列
bucket
,索引代表頻率,值為具有該頻率的元素列表。 - 收集結果:從高頻到低頻遍歷桶,收集元素直到達到 k 個。
解題說明
- 計算頻率:使用雜湊表
count
記錄每個元素的出現次數。 - 建立桶:創建一個大小為
n + 1
的桶陣列,因為元素的最大頻率不會超過陣列的長度n
。桶的索引代表頻率,值為具有該頻率的元素列表。 - 收集結果:從索引最大的桶(最高頻率)開始,依次往下遍歷,收集元素直到收集到 k 個。
複雜度分析
- 時間複雜度:O(n)
- 計算頻率:遍歷陣列一次,時間複雜度為 O(n)。
- 建立桶:遍歷雜湊表一次,時間複雜度為 O(n)。
- 收集結果:最壞情況下,需要遍歷所有桶,時間複雜度為 O(n)。
- 空間複雜度:O(n)
- 雜湊表:儲存元素的頻率,空間複雜度為 O(n)。
- 桶陣列:大小為
n + 1
,空間複雜度為 O(n)。
程式碼
C++ 實作
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
const int n = nums.size();
unordered_map<int, int> count;
vector<vector<int>> bucket(n + 1, vector<int>());
for (const int num : nums) {
count[num] += 1;
}
for (auto [num, freq] : count) {
bucket[freq].push_back(num);
}
vector<int> ans;
for (int i = n; i > 0; i -= 1) {
for (const int num : bucket[i]) {
if (!k) {
return ans;
}
ans.push_back(num);
k -= 1;
}
}
return ans;
}
};
Java 實作
class Solution {
public int[] topKFrequent(int[] nums, int k) {
HashMap<Integer, Integer> counter = new HashMap<>();
for (int num : nums) {
counter.put(num, counter.getOrDefault(num, 0) + 1);
}
List<Integer>[] bucket = new List[nums.length + 1];
for (int num : counter.keySet()) {
int freq = counter.get(num);
if (bucket[freq] == null) {
bucket[freq] = new LinkedList<>();
}
bucket[freq].add(num);
}
int[] ans = new int[k];
for (int i = bucket.length - 1, p = 0; i > 0 && k > 0; i -= 1) {
if (bucket[i] != null) {
List<Integer> list = bucket[i];
for (int num : list) {
if (k == 0) {
return ans;
}
ans[p++] = num;
k -= 1;
}
}
}
return ans;
}
}
Comments ()