摘要:欢迎又来到leetcode每日一题系列,今天我们为大家讲解的是有关于leetcode347.前k个高频元素的讲解(又名topk),大家在看完我的讲解之后也可以点开链接自己做一下。
给你一个整数数组 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 是数组大小。
这道题主要有两个思路:
解决这个问题的关键在于两个步骤:
以下是实现上述思路的C语言代码:
#include
#include
typedef struct {
int value;
int cnt;
} Vat;
int compFunc(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
int compVatFunc(const void *a, const void *b) {
Vat *node1 = (Vat *)a;
Vat *node2 = (Vat *)b;
return node2->cnt - node1->cnt;
}
int* topKFrequent(int* nums, int numsSize, int k, int* returnSize) {
if (k <= 0 || numsSize <= 0) return NULL;
qsort(nums, numsSize, sizeof(int), compFunc);
Vat *nums_info = (Vat *)malloc(numsSize * sizeof(Vat));
int index = 0;
for (int i = 0; i < numsSize; ++i) {
if (i == 0 || nums[i] != nums[i - 1]) {
nums_info[index].value = nums[i];
nums_info[index].cnt = 1;
} else {
nums_info[index].cnt++;
}
index++;
}
qsort(nums_info, index, sizeof(Vat), compVatFunc);
*returnSize = k;
int *result = (int *)malloc(k * sizeof(int));
for (int i = 0; i < k; ++i) {
result[i] = nums_info[i].value;
}
free(nums_info);
return result;
}
int main() {
int nums[] = {1,1,1,2,2,3};
int k = 2;
int returnSize;
int* result = topKFrequent(nums, sizeof(nums)/sizeof(nums[0]), k, &returnSize);
for (int i = 0; i < returnSize; ++i) {
printf("%d ", result[i]);
}
free(result);
return 0;
}
大家如果不熟悉qsort的逻辑,我曾经写过一篇有关于qsort的模拟实现,链接放在这里大家可以点击查看。库函数的模拟实现。
解决这个问题的关键在于两个步骤:统计元素出现次数和根据出现次数排序。这里,我们采用哈希表来统计次数,使用堆来找出前K大的值。
#include
#include
// 定义最小堆中的元素节点
typedef struct {
int key; // 数字
int count; // 出现次数
} HeapNode;
// 定义最小堆结构
typedef struct {
HeapNode *nodes; // 堆中元素数组
int capacity; // 堆的容量
int size; // 堆中当前元素数量
} MinHeap;
// 创建最小堆
MinHeap* createMinHeap(int capacity) {
MinHeap *hp = (MinHeap *)malloc(sizeof(MinHeap));
hp->nodes = (HeapNode *)malloc(sizeof(HeapNode) * capacity);
hp->capacity = capacity;
hp->size = 0;
return hp;
}
// 交换两个堆节点
void swapNodes(HeapNode *a, HeapNode *b) {
HeapNode temp = *a;
*a = *b;
*b = temp;
}
// 维护最小堆性质,从索引idx开始向下调整
void minHeapify(MinHeap *hp, int idx) {
int smallest = idx;
int left = 2 * idx + 1;
int right = 2 * idx + 2;
if (left < hp->size && hp->nodes[left].count < hp->nodes[smallest].count) {
smallest = left;
}
if (right < hp->size && hp->nodes[right].count < hp->nodes[smallest].count) {
smallest = right;
}
if (smallest != idx) {
swapNodes(&hp->nodes[smallest], &hp->nodes[idx]);
minHeapify(hp, smallest);
}
}
// 向最小堆中插入新节点
void insertMinHeap(MinHeap *hp, HeapNode node) {
if (hp->size == hp->capacity) {
// 如果堆已满,且新节点的count大于堆顶元素的count,则替换堆顶
if (hp->nodes[0].count < node.count) {
hp->nodes[0] = node;
minHeapify(hp, 0);
}
return;
}
hp->size++;
hp->nodes[hp->size - 1] = node;
// 从底向上调整堆,维护最小堆性质
int i = hp->size - 1;
while (i && hp->nodes[(i - 1) / 2].count > hp->nodes[i].count) {
swapNodes(&hp->nodes[(i - 1) / 2], &hp->nodes[i]);
i = (i - 1) / 2;
}
}
// 从最小堆中提取最小元素
void extractMin(MinHeap *hp) {
if (hp->size > 0) {
hp->size--;
hp->nodes[0] = hp->nodes[hp->size];
minHeapify(hp, 0);
}
}
// 定义哈希表节点
typedef struct Node {
int key;
int value;
struct Node *next;
} Node;
// 定义哈希表结构
typedef struct {
Node **buckets;
int capacity;
} HashMap;
// 创建哈希表
HashMap* createHashMap(int capacity) {
HashMap *hm = (HashMap *)malloc(sizeof(HashMap));
hm->buckets = (Node **)malloc(sizeof(Node *) * capacity);
for (int i = 0; i < capacity; i++) {
hm->buckets[i] = NULL;
}
hm->capacity = capacity;
return hm;
}
// 哈希表插入或更新操作
void put(HashMap *hm, int key, int value) {
int index = abs(key) % hm->capacity;
Node *node = hm->buckets[index];
if (node == NULL) {
hm->buckets[index] = (Node *)malloc(sizeof(Node));
hm->buckets[index]->key = key;
hm->buckets[index]->value = value;
hm->buckets[index]->next = NULL;
} else {
while (node->next != NULL && node->key != key) {
node = node->next;
}
if (node->key == key) {
node->value = value;
} else {
node->next = (Node *)malloc(sizeof(Node));
node->next->key = key;
node->next->value = value;
node->next->next = NULL;
}
}
}
// 哈希表查询操作
int get(HashMap *hm, int key) {
int index = abs(key) % hm->capacity;
Node *node = hm->buckets[index];
while (node != NULL) {
if (node->key == key) {
return node->value;
}
node = node->next;
}
return -1;
}
// 查找数组中前K个最频繁出现的元素
int* topKFrequent(int* nums, int numsSize, int k, int* returnSize) {
// 创建哈希表,统计每个数字出现的次数
HashMap *hm = createHashMap(769); // 使用质数作为哈希表的大小
for (int i = 0; i < numsSize; i++) {
int count = get(hm, nums[i]);
if (count == -1) {
put(hm, nums[i], 1);
} else {
put(hm, nums[i], count + 1);
}
}
// 创建最小堆,用于存储出现次数最多的K个元素
MinHeap *hp = createMinHeap(k);
// 遍历哈希表,构建最小堆
for (int i = 0; i < hm->capacity; i++) {
Node *node = hm->buckets[i];
while (node != NULL) {
HeapNode heapNode = {node->key, node->value};
insertMinHeap(hp, heapNode);
node = node->next;
}
}
// 从最小堆中提取前K个最频繁出现的元素
*returnSize = hp->size;
int *result = (int *)malloc(sizeof(int) * hp->size);
for (int i = 0; i < hp->size; i++) {
result[i] = hp->nodes[i].key;
extractMin(hp); // 提取最小元素后,堆大小减1
}
// 释放哈希表和最小堆占用的内存
for (int i = 0; i < hm->capacity; i++) {
Node *node = hm->buckets[i];
while (node != NULL) {
Node *temp = node;
node = node->next;
free(temp);
}
}
free(hm->buckets);
free(hm);
free(hp->nodes);
free(hp);
return result;
}
int main() {
int nums[] = {1,1,1,2,2,3};
int k = 2;
int returnSize;
int* result = topKFrequent(nums, sizeof(nums)/sizeof(nums[0]), k, &returnSize);
for (int i = 0; i < returnSize; ++i) {
printf("%d ", result[i]);
}
free(result);
return 0;
}
关于代码中的堆的内容,在堆的实现的这篇文中有详细的讲解。大家对于题目还有什么不理解的可以评论留言。