From 7822bf9cd4816577b1de02baad51db40642ce893 Mon Sep 17 00:00:00 2001 From: Yudong Jin Date: Thu, 26 Oct 2023 02:54:19 +0800 Subject: [PATCH] feat: add top_k.c and refactor top_k.js (#889) * Add top_k.c based on my_heap.c * Improve the implementation of top_k.js * Add a comment to top_k --- codes/c/chapter_heap/CMakeLists.txt | 4 +- codes/c/chapter_heap/my_heap.c | 40 ++----------- codes/c/chapter_heap/my_heap_test.c | 41 +++++++++++++ codes/c/chapter_heap/top_k.c | 73 ++++++++++++++++++++++++ codes/cpp/chapter_heap/top_k.cpp | 1 + codes/csharp/chapter_heap/top_k.cs | 1 + codes/dart/chapter_heap/top_k.dart | 2 +- codes/go/chapter_heap/top_k.go | 1 + codes/java/chapter_heap/top_k.java | 1 + codes/javascript/chapter_heap/my_heap.js | 56 +++++++++--------- codes/javascript/chapter_heap/top_k.js | 50 +++++++++++----- codes/python/chapter_heap/top_k.py | 1 + codes/rust/chapter_heap/top_k.rs | 2 +- 13 files changed, 195 insertions(+), 78 deletions(-) create mode 100644 codes/c/chapter_heap/my_heap_test.c create mode 100644 codes/c/chapter_heap/top_k.c diff --git a/codes/c/chapter_heap/CMakeLists.txt b/codes/c/chapter_heap/CMakeLists.txt index c94ec9c16..357ec702c 100644 --- a/codes/c/chapter_heap/CMakeLists.txt +++ b/codes/c/chapter_heap/CMakeLists.txt @@ -1,2 +1,2 @@ -add_executable(my_heap my_heap.c) - +add_executable(my_heap_test my_heap_test.c) +add_executable(top_k top_k.c) diff --git a/codes/c/chapter_heap/my_heap.c b/codes/c/chapter_heap/my_heap.c index 4a12e4876..076a99749 100644 --- a/codes/c/chapter_heap/my_heap.c +++ b/codes/c/chapter_heap/my_heap.c @@ -34,6 +34,12 @@ MaxHeap *newMaxHeap(int nums[], int size) { return h; } +/* 析构函数 */ +void freeMaxHeap(MaxHeap *h) { + // 释放内存 + free(h); +} + /* 获取左子节点索引 */ int left(MaxHeap *h, int i) { return 2 * i + 1; @@ -144,37 +150,3 @@ void siftUp(MaxHeap *h, int i) { i = p; } } - -/* Driver Code */ -int main() { - /* 初始化堆 */ - // 初始化大顶堆 - int nums[] = {9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}; - MaxHeap *heap = newMaxHeap(nums, sizeof(nums) / sizeof(int)); - printf("输入数组并建堆后\n"); - printHeap(heap->data, heap->size); - - /* 获取堆顶元素 */ - printf("\n堆顶元素为 %d\n", peek(heap)); - - /* 元素入堆 */ - push(heap, 7); - printf("\n元素 7 入堆后\n"); - printHeap(heap->data, heap->size); - - /* 堆顶元素出堆 */ - int top = pop(heap); - printf("\n堆顶元素 %d 出堆后\n", top); - printHeap(heap->data, heap->size); - - /* 获取堆大小 */ - printf("\n堆元素数量为 %d\n", size(heap)); - - /* 判断堆是否为空 */ - printf("\n堆是否为空 %d\n", isEmpty(heap)); - - // 释放内存 - free(heap); - - return 0; -} diff --git a/codes/c/chapter_heap/my_heap_test.c b/codes/c/chapter_heap/my_heap_test.c new file mode 100644 index 000000000..46676d3d0 --- /dev/null +++ b/codes/c/chapter_heap/my_heap_test.c @@ -0,0 +1,41 @@ +/** + * File: my_heap_test.c + * Created Time: 2023-01-15 + * Author: Reanon (793584285@qq.com) + */ + +#include "my_heap.c" + +/* Driver Code */ +int main() { + /* 初始化堆 */ + // 初始化大顶堆 + int nums[] = {9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2}; + MaxHeap *heap = newMaxHeap(nums, sizeof(nums) / sizeof(int)); + printf("输入数组并建堆后\n"); + printHeap(heap->data, heap->size); + + /* 获取堆顶元素 */ + printf("\n堆顶元素为 %d\n", peek(heap)); + + /* 元素入堆 */ + push(heap, 7); + printf("\n元素 7 入堆后\n"); + printHeap(heap->data, heap->size); + + /* 堆顶元素出堆 */ + int top = pop(heap); + printf("\n堆顶元素 %d 出堆后\n", top); + printHeap(heap->data, heap->size); + + /* 获取堆大小 */ + printf("\n堆元素数量为 %d\n", size(heap)); + + /* 判断堆是否为空 */ + printf("\n堆是否为空 %d\n", isEmpty(heap)); + + // 释放内存 + freeMaxHeap(heap); + + return 0; +} diff --git a/codes/c/chapter_heap/top_k.c b/codes/c/chapter_heap/top_k.c new file mode 100644 index 000000000..34bf02bb5 --- /dev/null +++ b/codes/c/chapter_heap/top_k.c @@ -0,0 +1,73 @@ +/** + * File: top_k.c + * Created Time: 2023-10-26 + * Author: Krahets (krahets163.com) + */ + +#include "my_heap.c" + +/* 元素入堆 */ +void pushMinHeap(MaxHeap *maxHeap, int val) { + // 元素取反 + push(maxHeap, -val); +} + +/* 元素出堆 */ +int popMinHeap(MaxHeap *maxHeap) { + // 元素取反 + return -pop(maxHeap); +} + +/* 访问堆顶元素 */ +int peekMinHeap(MaxHeap *maxHeap) { + // 元素取反 + return -peek(maxHeap); +} + +/* 取出堆中元素 */ +int *getMinHeap(MaxHeap *maxHeap) { + // 将堆中所有元素取反并存入 res 数组 + int *res = (int *)malloc(maxHeap->size * sizeof(int)); + for (int i = 0; i < maxHeap->size; i++) { + res[i] = -maxHeap->data[i]; + } + return res; +} + +// 基于堆查找数组中最大的 k 个元素的函数 +int *topKHeap(int *nums, int sizeNums, int k) { + // 初始化小顶堆 + // 请注意:我们将堆中所有元素取反,从而用大顶堆来模拟小顶堆 + int empty[0]; + MaxHeap *maxHeap = newMaxHeap(empty, 0); + // 将数组的前 k 个元素入堆 + for (int i = 0; i < k; i++) { + pushMinHeap(maxHeap, nums[i]); + } + // 从第 k+1 个元素开始,保持堆的长度为 k + for (int i = k; i < sizeNums; i++) { + // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 + if (nums[i] > peekMinHeap(maxHeap)) { + popMinHeap(maxHeap); + pushMinHeap(maxHeap, nums[i]); + } + } + int *res = getMinHeap(maxHeap); + // 释放内存 + freeMaxHeap(maxHeap); + return res; +} + +/* Driver Code */ +int main() { + int nums[] = {1, 7, 6, 3, 2}; + int k = 3; + int sizeNums = sizeof(nums) / sizeof(nums[0]); + + int *res = topKHeap(nums, sizeNums, k); + printf("最大的 %d 个元素为: ", k); + printArray(res, k); + + free(res); + return 0; +} diff --git a/codes/cpp/chapter_heap/top_k.cpp b/codes/cpp/chapter_heap/top_k.cpp index bd1727a76..0b5fc4174 100644 --- a/codes/cpp/chapter_heap/top_k.cpp +++ b/codes/cpp/chapter_heap/top_k.cpp @@ -8,6 +8,7 @@ /* 基于堆查找数组中最大的 k 个元素 */ priority_queue, greater> topKHeap(vector &nums, int k) { + // 初始化小顶堆 priority_queue, greater> heap; // 将数组的前 k 个元素入堆 for (int i = 0; i < k; i++) { diff --git a/codes/csharp/chapter_heap/top_k.cs b/codes/csharp/chapter_heap/top_k.cs index e3962eeb8..90460e1f8 100644 --- a/codes/csharp/chapter_heap/top_k.cs +++ b/codes/csharp/chapter_heap/top_k.cs @@ -9,6 +9,7 @@ namespace hello_algo.chapter_heap; public class top_k { /* 基于堆查找数组中最大的 k 个元素 */ public static PriorityQueue TopKHeap(int[] nums, int k) { + // 初始化小顶堆 PriorityQueue heap = new(); // 将数组的前 k 个元素入堆 for (int i = 0; i < k; i++) { diff --git a/codes/dart/chapter_heap/top_k.dart b/codes/dart/chapter_heap/top_k.dart index 72cb81669..d66df86c1 100644 --- a/codes/dart/chapter_heap/top_k.dart +++ b/codes/dart/chapter_heap/top_k.dart @@ -8,7 +8,7 @@ import '../utils/print_util.dart'; /* 基于堆查找数组中最大的 k 个元素 */ MinHeap topKHeap(List nums, int k) { - // 将数组的前 k 个元素入堆 + // 初始化小顶堆,将数组的前 k 个元素入堆 MinHeap heap = MinHeap(nums.sublist(0, k)); // 从第 k+1 个元素开始,保持堆的长度为 k for (int i = k; i < nums.length; i++) { diff --git a/codes/go/chapter_heap/top_k.go b/codes/go/chapter_heap/top_k.go index ae8c7fafd..4874c3fb1 100644 --- a/codes/go/chapter_heap/top_k.go +++ b/codes/go/chapter_heap/top_k.go @@ -32,6 +32,7 @@ func (h *minHeap) Top() any { /* 基于堆查找数组中最大的 k 个元素 */ func topKHeap(nums []int, k int) *minHeap { + // 初始化小顶堆 h := &minHeap{} heap.Init(h) // 将数组的前 k 个元素入堆 diff --git a/codes/java/chapter_heap/top_k.java b/codes/java/chapter_heap/top_k.java index 8d0213956..760caec59 100644 --- a/codes/java/chapter_heap/top_k.java +++ b/codes/java/chapter_heap/top_k.java @@ -12,6 +12,7 @@ import java.util.*; public class top_k { /* 基于堆查找数组中最大的 k 个元素 */ static Queue topKHeap(int[] nums, int k) { + // 初始化小顶堆 Queue heap = new PriorityQueue(); // 将数组的前 k 个元素入堆 for (int i = 0; i < k; i++) { diff --git a/codes/javascript/chapter_heap/my_heap.js b/codes/javascript/chapter_heap/my_heap.js index 37e089a81..62699ffbd 100644 --- a/codes/javascript/chapter_heap/my_heap.js +++ b/codes/javascript/chapter_heap/my_heap.js @@ -123,33 +123,35 @@ class MaxHeap { } /* Driver Code */ -/* 初始化大顶堆 */ -const maxHeap = new MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); -console.log('\n输入列表并建堆后'); -maxHeap.print(); - -/* 获取堆顶元素 */ -let peek = maxHeap.peek(); -console.log(`\n堆顶元素为 ${peek}`); - -/* 元素入堆 */ -let val = 7; -maxHeap.push(val); -console.log(`\n元素 ${val} 入堆后`); -maxHeap.print(); - -/* 堆顶元素出堆 */ -peek = maxHeap.pop(); -console.log(`\n堆顶元素 ${peek} 出堆后`); -maxHeap.print(); - -/* 获取堆大小 */ -let size = maxHeap.size(); -console.log(`\n堆元素数量为 ${size}`); - -/* 判断堆是否为空 */ -let isEmpty = maxHeap.isEmpty(); -console.log(`\n堆是否为空 ${isEmpty}`); +if (require.main === module) { + /* 初始化大顶堆 */ + const maxHeap = new MaxHeap([9, 8, 6, 6, 7, 5, 2, 1, 4, 3, 6, 2]); + console.log('\n输入列表并建堆后'); + maxHeap.print(); + + /* 获取堆顶元素 */ + let peek = maxHeap.peek(); + console.log(`\n堆顶元素为 ${peek}`); + + /* 元素入堆 */ + let val = 7; + maxHeap.push(val); + console.log(`\n元素 ${val} 入堆后`); + maxHeap.print(); + + /* 堆顶元素出堆 */ + peek = maxHeap.pop(); + console.log(`\n堆顶元素 ${peek} 出堆后`); + maxHeap.print(); + + /* 获取堆大小 */ + let size = maxHeap.size(); + console.log(`\n堆元素数量为 ${size}`); + + /* 判断堆是否为空 */ + let isEmpty = maxHeap.isEmpty(); + console.log(`\n堆是否为空 ${isEmpty}`); +} module.exports = { MaxHeap, diff --git a/codes/javascript/chapter_heap/top_k.js b/codes/javascript/chapter_heap/top_k.js index 27c65af70..9e1408200 100644 --- a/codes/javascript/chapter_heap/top_k.js +++ b/codes/javascript/chapter_heap/top_k.js @@ -6,25 +6,49 @@ const { MaxHeap } = require('./my_heap'); +/* 元素入堆 */ +function pushMinHeap(maxHeap, val) { + // 元素取反 + maxHeap.push(-val); +} + +/* 元素出堆 */ +function popMinHeap(maxHeap) { + // 元素取反 + return -maxHeap.pop(); +} + +/* 访问堆顶元素 */ +function peekMinHeap(maxHeap) { + // 元素取反 + return -maxHeap.peek(); +} + +/* 取出堆中元素 */ +function getMinHeap(maxHeap) { + // 元素取反 + return maxHeap.getMaxHeap().map((num) => -num); +} + /* 基于堆查找数组中最大的 k 个元素 */ function topKHeap(nums, k) { - // 使用大顶堆 MaxHeap ,对数组 nums 取相反数 - const invertedNums = nums.map((num) => -num); + // 初始化小顶堆 + // 请注意:我们将堆中所有元素取反,从而用大顶堆来模拟小顶堆 + const maxHeap = new MaxHeap([]); // 将数组的前 k 个元素入堆 - const heap = new MaxHeap(invertedNums.slice(0, k)); + for (let i = 0; i < k; i++) { + pushMinHeap(maxHeap, nums[i]); + } // 从第 k+1 个元素开始,保持堆的长度为 k - for (let i = k; i < invertedNums.length; i++) { - // 若当前元素小于堆顶元素,则将堆顶元素出堆、当前元素入堆 - if (invertedNums[i] < heap.peek()) { - heap.pop(); - heap.push(invertedNums[i]); + for (let i = k; i < nums.length; i++) { + // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 + if (nums[i] > peekMinHeap(maxHeap)) { + popMinHeap(maxHeap); + pushMinHeap(maxHeap, nums[i]); } } - // 取出堆中元素 - const maxHeap = heap.getMaxHeap(); - // 对堆中元素取相反数 - const invertedMaxHeap = maxHeap.map((num) => -num); - return invertedMaxHeap; + // 返回堆中元素 + return getMinHeap(maxHeap); } /* Driver Code */ diff --git a/codes/python/chapter_heap/top_k.py b/codes/python/chapter_heap/top_k.py index fea1c8791..f613574a8 100644 --- a/codes/python/chapter_heap/top_k.py +++ b/codes/python/chapter_heap/top_k.py @@ -15,6 +15,7 @@ import heapq def top_k_heap(nums: list[int], k: int) -> list[int]: """基于堆查找数组中最大的 k 个元素""" + # 初始化小顶堆 heap = [] # 将数组的前 k 个元素入堆 for i in range(k): diff --git a/codes/rust/chapter_heap/top_k.rs b/codes/rust/chapter_heap/top_k.rs index 34579d2be..f731a97de 100644 --- a/codes/rust/chapter_heap/top_k.rs +++ b/codes/rust/chapter_heap/top_k.rs @@ -11,7 +11,7 @@ use std::collections::BinaryHeap; /* 基于堆查找数组中最大的 k 个元素 */ fn top_k_heap(nums: Vec, k: usize) -> BinaryHeap> { - // Rust 的 BinaryHeap 是大顶堆,使用 Reverse 将元素大小反转 + // BinaryHeap 是大顶堆,使用 Reverse 将元素取反,从而实现小顶堆 let mut heap = BinaryHeap::>::new(); // 将数组的前 k 个元素入堆 for &num in nums.iter().take(k) {