From e4ba690005bcf3207d93ad71bf530afcab8debb0 Mon Sep 17 00:00:00 2001 From: Reanon <793584285@qq.com> Date: Sun, 25 Jun 2023 20:51:31 +0800 Subject: [PATCH] feat(go): support new features with go code (#565) * feat(go): support hash map chaining * feat(go): support hash map open address * feat(go): support simple hash * feat(go): support top k heap * feat(go): support subset sum I * feat(go): support subset sum native * feat(go): support subset sum II * fix(go): fix some problem --- codes/go/chapter_backtracking/subset_sum_i.go | 45 ++++++ .../subset_sum_i_naive.go | 40 +++++ .../go/chapter_backtracking/subset_sum_ii.go | 50 ++++++ .../chapter_backtracking/subset_sum_test.go | 56 +++++++ codes/go/chapter_hashing/hash_map_chaining.go | 134 +++++++++++++++++ .../hash_map_open_addressing.go | 142 ++++++++++++++++++ codes/go/chapter_hashing/hash_map_test.go | 97 ++++++++++-- codes/go/chapter_hashing/simple_hash.go | 55 +++++++ codes/go/chapter_heap/heap_test.go | 11 ++ codes/go/chapter_heap/top_k.go | 49 ++++++ 10 files changed, 666 insertions(+), 13 deletions(-) create mode 100644 codes/go/chapter_backtracking/subset_sum_i.go create mode 100644 codes/go/chapter_backtracking/subset_sum_i_naive.go create mode 100644 codes/go/chapter_backtracking/subset_sum_ii.go create mode 100644 codes/go/chapter_backtracking/subset_sum_test.go create mode 100644 codes/go/chapter_hashing/hash_map_chaining.go create mode 100644 codes/go/chapter_hashing/hash_map_open_addressing.go create mode 100644 codes/go/chapter_hashing/simple_hash.go create mode 100644 codes/go/chapter_heap/top_k.go diff --git a/codes/go/chapter_backtracking/subset_sum_i.go b/codes/go/chapter_backtracking/subset_sum_i.go new file mode 100644 index 000000000..7e8b157ce --- /dev/null +++ b/codes/go/chapter_backtracking/subset_sum_i.go @@ -0,0 +1,45 @@ +// File: subset_sum_i.go +// Created Time: 2023-06-24 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import "sort" + +type subsetI struct{} + +/* 回溯算法:子集和 I */ +func (s subsetI) backtrack(start, target int, state, choices *[]int, res *[][]int) { + // 子集和等于 target 时,记录解 + if target == 0 { + newState := append([]int{}, *state...) + *res = append(*res, newState) + return + } + // 遍历所有选择 + // 剪枝二:从 start 开始遍历,避免生成重复子集 + for i := start; i < len(*choices); i++ { + // 剪枝一:若子集和超过 target ,则直接结束循环 + // 这是因为数组已排序,后边元素更大,子集和一定超过 target + if target-(*choices)[i] < 0 { + break + } + // 尝试:做出选择,更新 target, start + *state = append(*state, (*choices)[i]) + // 进行下一轮选择 + s.backtrack(i, target-(*choices)[i], state, choices, res) + // 回退:撤销选择,恢复到之前的状态 + *state = (*state)[:len(*state)-1] + } +} + +/* 求解子集和 I */ +func subsetSumI(nums []int, target int) [][]int { + s := subsetI{} + state := make([]int, 0) // 状态(子集) + sort.Ints(nums) // 对 nums 进行排序 + start := 0 // 遍历起始点 + res := make([][]int, 0) // 结果列表(子集列表) + s.backtrack(start, target, &state, &nums, &res) + return res +} diff --git a/codes/go/chapter_backtracking/subset_sum_i_naive.go b/codes/go/chapter_backtracking/subset_sum_i_naive.go new file mode 100644 index 000000000..31b6552c6 --- /dev/null +++ b/codes/go/chapter_backtracking/subset_sum_i_naive.go @@ -0,0 +1,40 @@ +// File: subset_sum_i_naive.go +// Created Time: 2023-06-24 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +type subset struct{} + +/* 回溯算法:子集和 I */ +func (s subset) backtrack(total, target int, state, choices *[]int, res *[][]int) { + // 子集和等于 target 时,记录解 + if target == total { + newState := append([]int{}, *state...) + *res = append(*res, newState) + return + } + // 遍历所有选择 + for i := 0; i < len(*choices); i++ { + // 剪枝:若子集和超过 target ,则跳过该选择 + if total+(*choices)[i] > target { + continue + } + // 尝试:做出选择,更新元素和 total + *state = append(*state, (*choices)[i]) + // 进行下一轮选择 + s.backtrack(total+(*choices)[i], target, state, choices, res) + // 回退:撤销选择,恢复到之前的状态 + *state = (*state)[:len(*state)-1] + } +} + +/* 求解子集和 I(包含重复子集) */ +func subsetSumINaive(nums []int, target int) [][]int { + s := subset{} + state := make([]int, 0) // 状态(子集) + total := 0 // 子集和 + res := make([][]int, 0) // 结果列表(子集列表) + s.backtrack(total, target, &state, &nums, &res) + return res +} diff --git a/codes/go/chapter_backtracking/subset_sum_ii.go b/codes/go/chapter_backtracking/subset_sum_ii.go new file mode 100644 index 000000000..3e5d2d98c --- /dev/null +++ b/codes/go/chapter_backtracking/subset_sum_ii.go @@ -0,0 +1,50 @@ +// File: subset_sum_ii.go +// Created Time: 2023-06-24 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import "sort" + +type subsetII struct{} + +/* 回溯算法:子集和 II */ +func (s subsetII) backtrack(start, target int, state, choices *[]int, res *[][]int) { + // 子集和等于 target 时,记录解 + if target == 0 { + newState := append([]int{}, *state...) + *res = append(*res, newState) + return + } + // 遍历所有选择 + // 剪枝二:从 start 开始遍历,避免生成重复子集 + // 剪枝三:从 start 开始遍历,避免重复选择同一元素 + for i := start; i < len(*choices); i++ { + // 剪枝一:若子集和超过 target ,则直接结束循环 + // 这是因为数组已排序,后边元素更大,子集和一定超过 target + if target-(*choices)[i] < 0 { + break + } + // 剪枝四:如果该元素与左边元素相等,说明该搜索分支重复,直接跳过 + if i > start && (*choices)[i] == (*choices)[i-1] { + continue + } + // 尝试:做出选择,更新 target, start + *state = append(*state, (*choices)[i]) + // 进行下一轮选择 + s.backtrack(i+1, target-(*choices)[i], state, choices, res) + // 回退:撤销选择,恢复到之前的状态 + *state = (*state)[:len(*state)-1] + } +} + +/* 求解子集和 II */ +func subsetSumII(nums []int, target int) [][]int { + s := subsetII{} + state := make([]int, 0) // 状态(子集) + sort.Ints(nums) // 对 nums 进行排序 + start := 0 // 遍历起始点 + res := make([][]int, 0) // 结果列表(子集列表) + s.backtrack(start, target, &state, &nums, &res) + return res +} diff --git a/codes/go/chapter_backtracking/subset_sum_test.go b/codes/go/chapter_backtracking/subset_sum_test.go new file mode 100644 index 000000000..9cd681051 --- /dev/null +++ b/codes/go/chapter_backtracking/subset_sum_test.go @@ -0,0 +1,56 @@ +// File: subset_sum_test.go +// Created Time: 2023-06-24 +// Author: Reanon (793584285@qq.com) + +package chapter_backtracking + +import ( + "fmt" + "strconv" + "testing" + + . "github.com/krahets/hello-algo/pkg" +) + +func TestSubsetSumINaive(t *testing.T) { + nums := []int{3, 4, 5} + target := 9 + res := subsetSumINaive(nums, target) + + fmt.Printf("target = " + strconv.Itoa(target) + ", 输入数组 nums = ") + PrintSlice(nums) + + fmt.Println("所有和等于 " + strconv.Itoa(target) + " 的子集 res = ") + for i := range res { + PrintSlice(res[i]) + } + fmt.Println("请注意,该方法输出的结果包含重复集合") +} + +func TestSubsetSumI(t *testing.T) { + nums := []int{3, 4, 5} + target := 9 + res := subsetSumI(nums, target) + + fmt.Printf("target = " + strconv.Itoa(target) + ", 输入数组 nums = ") + PrintSlice(nums) + + fmt.Println("所有和等于 " + strconv.Itoa(target) + " 的子集 res = ") + for i := range res { + PrintSlice(res[i]) + } +} + +func TestSubsetSumII(t *testing.T) { + nums := []int{4, 4, 5} + target := 9 + res := subsetSumII(nums, target) + + fmt.Printf("target = " + strconv.Itoa(target) + ", 输入数组 nums = ") + PrintSlice(nums) + + fmt.Println("所有和等于 " + strconv.Itoa(target) + " 的子集 res = ") + for i := range res { + PrintSlice(res[i]) + } +} diff --git a/codes/go/chapter_hashing/hash_map_chaining.go b/codes/go/chapter_hashing/hash_map_chaining.go new file mode 100644 index 000000000..002f1a778 --- /dev/null +++ b/codes/go/chapter_hashing/hash_map_chaining.go @@ -0,0 +1,134 @@ +// File: hash_map_chaining.go +// Created Time: 2023-06-23 +// Author: Reanon (793584285@qq.com) + +package chapter_hashing + +import ( + "fmt" + "strconv" + "strings" +) + +/* 链式地址哈希表 */ +type hashMapChaining struct { + size int // 键值对数量 + capacity int // 哈希表容量 + loadThres float64 // 触发扩容的负载因子阈值 + extendRatio int // 扩容倍数 + buckets [][]pair // 桶数组 +} + +/* 构造方法 */ +func newHashMapChaining() *hashMapChaining { + buckets := make([][]pair, 4) + for i := 0; i < 4; i++ { + buckets[i] = make([]pair, 0) + } + return &hashMapChaining{ + size: 0, + capacity: 4, + loadThres: 2 / 3.0, + extendRatio: 2, + buckets: buckets, + } +} + +/* 哈希函数 */ +func (m *hashMapChaining) hashFunc(key int) int { + return key % m.capacity +} + +/* 负载因子 */ +func (m *hashMapChaining) loadFactor() float64 { + return float64(m.size / m.capacity) +} + +/* 查询操作 */ +func (m *hashMapChaining) get(key int) string { + idx := m.hashFunc(key) + bucket := m.buckets[idx] + // 遍历桶,若找到 key 则返回对应 val + for _, p := range bucket { + if p.key == key { + return p.val + } + } + // 若未找到 key 则返回空字符串 + return "" +} + +/* 添加操作 */ +func (m *hashMapChaining) put(key int, val string) { + // 当负载因子超过阈值时,执行扩容 + if m.loadFactor() > m.loadThres { + m.extend() + } + idx := m.hashFunc(key) + // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + for _, p := range m.buckets[idx] { + if p.key == key { + p.val = val + return + } + } + // 若无该 key ,则将键值对添加至尾部 + p := pair{ + key: key, + val: val, + } + m.buckets[idx] = append(m.buckets[idx], p) + m.size += 1 +} + +/* 删除操作 */ +func (m *hashMapChaining) remove(key int) { + idx := m.hashFunc(key) + // 遍历桶,从中删除键值对 + for i, p := range m.buckets[idx] { + if p.key == key { + // 切片删除 + m.buckets[idx] = append(m.buckets[idx][:i], m.buckets[idx][i+1:]...) + break + } + } + m.size -= 1 +} + +/* 扩容哈希表 */ +func (m *hashMapChaining) extend() { + // 暂存原哈希表 + tmpBuckets := make([][]pair, len(m.buckets)) + for i := 0; i < len(m.buckets); i++ { + tmpBuckets[i] = make([]pair, len(m.buckets[i])) + copy(tmpBuckets[i], m.buckets[i]) + } + // 初始化扩容后的新哈希表 + m.capacity *= m.extendRatio + m.buckets = make([][]pair, m.capacity) + for i := 0; i < m.capacity; i++ { + m.buckets[i] = make([]pair, 0) + } + m.size = 0 + // 将键值对从原哈希表搬运至新哈希表 + for _, bucket := range tmpBuckets { + for _, p := range bucket { + m.put(p.key, p.val) + } + } +} + +/* 打印哈希表 */ +func (m *hashMapChaining) print() { + var builder strings.Builder + + for _, bucket := range m.buckets { + builder.WriteString("[") + for _, p := range bucket { + builder.WriteString(strconv.Itoa(p.key) + " -> " + p.val + " ") + } + builder.WriteString("]") + fmt.Println(builder.String()) + builder.Reset() + } +} diff --git a/codes/go/chapter_hashing/hash_map_open_addressing.go b/codes/go/chapter_hashing/hash_map_open_addressing.go new file mode 100644 index 000000000..e851e30b9 --- /dev/null +++ b/codes/go/chapter_hashing/hash_map_open_addressing.go @@ -0,0 +1,142 @@ +// File: hash_map_open_addressing.go +// Created Time: 2023-06-23 +// Author: Reanon (793584285@qq.com) + +package chapter_hashing + +import ( + "fmt" + "strconv" +) + +/* 链式地址哈希表 */ +type hashMapOpenAddressing struct { + size int // 键值对数量 + capacity int // 哈希表容量 + loadThres float64 // 触发扩容的负载因子阈值 + extendRatio int // 扩容倍数 + buckets []pair // 桶数组 + removed pair // 删除标记 +} + +/* 构造方法 */ +func newHashMapOpenAddressing() *hashMapOpenAddressing { + buckets := make([]pair, 4) + return &hashMapOpenAddressing{ + size: 0, + capacity: 4, + loadThres: 2 / 3.0, + extendRatio: 2, + buckets: buckets, + removed: pair{ + key: -1, + val: "-1", + }, + } +} + +/* 哈希函数 */ +func (m *hashMapOpenAddressing) hashFunc(key int) int { + return key % m.capacity +} + +/* 负载因子 */ +func (m *hashMapOpenAddressing) loadFactor() float64 { + return float64(m.size) / float64(m.capacity) +} + +/* 查询操作 */ +func (m *hashMapOpenAddressing) get(key int) string { + idx := m.hashFunc(key) + // 线性探测,从 index 开始向后遍历 + for i := 0; i < m.capacity; i++ { + // 计算桶索引,越过尾部返回头部 + j := (idx + 1) % m.capacity + // 若遇到空桶,说明无此 key ,则返回 null + if m.buckets[j] == (pair{}) { + return "" + } + // 若遇到指定 key ,则返回对应 val + if m.buckets[j].key == key && m.buckets[j] != m.removed { + return m.buckets[j].val + } + } + // 若未找到 key 则返回空字符串 + return "" +} + +/* 添加操作 */ +func (m *hashMapOpenAddressing) put(key int, val string) { + // 当负载因子超过阈值时,执行扩容 + if m.loadFactor() > m.loadThres { + m.extend() + } + idx := m.hashFunc(key) + // 线性探测,从 index 开始向后遍历 + for i := 0; i < m.capacity; i++ { + // 计算桶索引,越过尾部返回头部 + j := (idx + i) % m.capacity + // 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶 + if m.buckets[j] == (pair{}) || m.buckets[j] == m.removed { + m.buckets[j] = pair{ + key: key, + val: val, + } + m.size += 1 + return + } + // 若遇到指定 key ,则更新对应 val + if m.buckets[j].key == key { + m.buckets[j].val = val + } + } +} + +/* 删除操作 */ +func (m *hashMapOpenAddressing) remove(key int) { + idx := m.hashFunc(key) + // 遍历桶,从中删除键值对 + // 线性探测,从 index 开始向后遍历 + for i := 0; i < m.capacity; i++ { + // 计算桶索引,越过尾部返回头部 + j := (idx + 1) % m.capacity + // 若遇到空桶,说明无此 key ,则直接返回 + if m.buckets[j] == (pair{}) { + return + } + // 若遇到指定 key ,则标记删除并返回 + if m.buckets[j].key == key { + m.buckets[j] = m.removed + m.size -= 1 + } + } +} + +/* 扩容哈希表 */ +func (m *hashMapOpenAddressing) extend() { + // 暂存原哈希表 + tmpBuckets := make([]pair, len(m.buckets)) + copy(tmpBuckets, m.buckets) + + // 初始化扩容后的新哈希表 + m.capacity *= m.extendRatio + m.buckets = make([]pair, m.capacity) + m.size = 0 + // 将键值对从原哈希表搬运至新哈希表 + for _, p := range tmpBuckets { + if p != (pair{}) && p != m.removed { + m.put(p.key, p.val) + } + } +} + +/* 打印哈希表 */ +func (m *hashMapOpenAddressing) print() { + for _, p := range m.buckets { + if p != (pair{}) { + fmt.Println(strconv.Itoa(p.key) + " -> " + p.val) + } else { + fmt.Println("nil") + } + } +} diff --git a/codes/go/chapter_hashing/hash_map_test.go b/codes/go/chapter_hashing/hash_map_test.go index c53d3991c..69e7195a8 100644 --- a/codes/go/chapter_hashing/hash_map_test.go +++ b/codes/go/chapter_hashing/hash_map_test.go @@ -6,6 +6,7 @@ package chapter_hashing import ( "fmt" + "strconv" "testing" . "github.com/krahets/hello-algo/pkg" @@ -13,43 +14,113 @@ import ( func TestHashmap(t *testing.T) { /* 初始化哈希表 */ - mapp := make(map[int]string) + hmap := make(map[int]string) /* 添加操作 */ // 在哈希表中添加键值对 (key, value) - mapp[12836] = "小哈" - mapp[15937] = "小啰" - mapp[16750] = "小算" - mapp[13276] = "小法" - mapp[10583] = "小鸭" + hmap[12836] = "小哈" + hmap[15937] = "小啰" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鸭" fmt.Println("\n添加完成后,哈希表为\nKey -> Value") - PrintMap(mapp) + PrintMap(hmap) /* 查询操作 */ // 向哈希表输入键 key ,得到值 value - name := mapp[15937] + name := hmap[15937] fmt.Println("\n输入学号 15937 ,查询到姓名 ", name) /* 删除操作 */ // 在哈希表中删除键值对 (key, value) - delete(mapp, 10583) + delete(hmap, 10583) fmt.Println("\n删除 10583 后,哈希表为\nKey -> Value") - PrintMap(mapp) + PrintMap(hmap) /* 遍历哈希表 */ // 遍历键值对 key->value fmt.Println("\n遍历键值对 Key->Value") - for key, value := range mapp { + for key, value := range hmap { fmt.Println(key, "->", value) } // 单独遍历键 key fmt.Println("\n单独遍历键 Key") - for key := range mapp { + for key := range hmap { fmt.Println(key) } // 单独遍历值 value fmt.Println("\n单独遍历值 Value") - for _, value := range mapp { + for _, value := range hmap { fmt.Println(value) } } + +func TestHashMapChaining(t *testing.T) { + /* 初始化哈希表 */ + hmap := newHashMapChaining() + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + hmap.put(12836, "小哈") + hmap.put(15937, "小啰") + hmap.put(16750, "小算") + hmap.put(13276, "小法") + hmap.put(10583, "小鸭") + fmt.Println("\n添加完成后,哈希表为\nKey -> Value") + hmap.print() + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + name := hmap.get(15937) + fmt.Println("\n输入学号 15937 ,查询到姓名 ", name) + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + hmap.remove(12836) + fmt.Println("\n删除 12836 后,哈希表为\nKey -> Value") + hmap.print() +} + +func TestHashMapOpenAddressing(t *testing.T) { + /* 初始化哈希表 */ + hmap := newHashMapOpenAddressing() + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + hmap.put(12836, "小哈") + hmap.put(15937, "小啰") + hmap.put(16750, "小算") + hmap.put(13276, "小法") + hmap.put(10583, "小鸭") + fmt.Println("\n添加完成后,哈希表为\nKey -> Value") + hmap.print() + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + name := hmap.get(13276) + fmt.Println("\n输入学号 13276 ,查询到姓名 ", name) + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + hmap.remove(16750) + fmt.Println("\n删除 16750 后,哈希表为\nKey -> Value") + hmap.print() +} + +func TestSimpleHash(t *testing.T) { + var hash int + + key := "Hello 算法" + + hash = addHash(key) + fmt.Println("加法哈希值为 " + strconv.Itoa(hash)) + + hash = mulHash(key) + fmt.Println("乘法哈希值为 " + strconv.Itoa(hash)) + + hash = xorHash(key) + fmt.Println("异或哈希值为 " + strconv.Itoa(hash)) + + hash = rotHash(key) + fmt.Println("旋转哈希值为 " + strconv.Itoa(hash)) +} diff --git a/codes/go/chapter_hashing/simple_hash.go b/codes/go/chapter_hashing/simple_hash.go new file mode 100644 index 000000000..a843abaa0 --- /dev/null +++ b/codes/go/chapter_hashing/simple_hash.go @@ -0,0 +1,55 @@ +// File: simple_hash.go +// Created Time: 2023-06-23 +// Author: Reanon (793584285@qq.com) + +package chapter_hashing + +import "fmt" + +/* 加法哈希 */ +func addHash(key string) int { + var hash int64 + var modulus int64 + + modulus = 1000000007 + for _, b := range []byte(key) { + hash = (hash + int64(b)) % modulus + } + return int(hash) +} + +/* 乘法哈希 */ +func mulHash(key string) int { + var hash int64 + var modulus int64 + + modulus = 1000000007 + for _, b := range []byte(key) { + hash = (31*hash + int64(b)) % modulus + } + return int(hash) +} + +/* 异或哈希 */ +func xorHash(key string) int { + hash := 0 + modulus := 1000000007 + for _, b := range []byte(key) { + fmt.Println(int(b)) + hash ^= int(b) + hash = (31*hash + int(b)) % modulus + } + return hash & modulus +} + +/* 旋转哈希 */ +func rotHash(key string) int { + var hash int64 + var modulus int64 + + modulus = 1000000007 + for _, b := range []byte(key) { + hash = ((hash << 4) ^ (hash >> 28) ^ int64(b)) % modulus + } + return int(hash) +} diff --git a/codes/go/chapter_heap/heap_test.go b/codes/go/chapter_heap/heap_test.go index 6c9e45162..4b84a05e5 100644 --- a/codes/go/chapter_heap/heap_test.go +++ b/codes/go/chapter_heap/heap_test.go @@ -7,6 +7,7 @@ package chapter_heap import ( "container/heap" "fmt" + "strconv" "testing" . "github.com/krahets/hello-algo/pkg" @@ -88,3 +89,13 @@ func TestMyHeap(t *testing.T) { isEmpty := maxHeap.isEmpty() fmt.Printf("\n堆是否为空 %t\n", isEmpty) } + +func TestTopKHeap(t *testing.T) { + /* 初始化堆 */ + // 初始化大顶堆 + nums := []int{1, 7, 6, 3, 2} + k := 3 + res := topKHeap(nums, k) + fmt.Printf("最大的 " + strconv.Itoa(k) + " 个元素为") + PrintHeap(*res) +} diff --git a/codes/go/chapter_heap/top_k.go b/codes/go/chapter_heap/top_k.go new file mode 100644 index 000000000..a446544a9 --- /dev/null +++ b/codes/go/chapter_heap/top_k.go @@ -0,0 +1,49 @@ +// File: top_k.go +// Created Time: 2023-06-24 +// Author: Reanon (793584285@qq.com) + +package chapter_heap + +import "container/heap" + +type minHeap []any + +func (h *minHeap) Len() int { return len(*h) } +func (h *minHeap) Less(i, j int) bool { return (*h)[i].(int) < (*h)[j].(int) } +func (h *minHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } + +// Push heap.Interface 的方法,实现推入元素到堆 +func (h *minHeap) Push(x any) { + *h = append(*h, x.(int)) +} + +// Pop heap.Interface 的方法,实现弹出堆顶元素 +func (h *minHeap) Pop() any { + // 待出堆元素存放在最后 + last := (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] + return last +} + +// Top 获取堆顶元素 +func (h *minHeap) Top() any { + return (*h)[0] +} + +func topKHeap(nums []int, k int) *minHeap { + h := &minHeap{} + heap.Init(h) + // 将数组的前 k 个元素入堆 + for i := 0; i < k; i++ { + heap.Push(h, nums[i]) + } + // 从第 k+1 个元素开始,保持堆的长度为 k + for i := k; i < len(nums); i++ { + // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆 + if nums[i] > h.Top().(int) { + heap.Pop(h) + heap.Push(h, nums[i]) + } + } + return h +}