图解堆操作:从完全二叉树到高效排序与优先队列
1. 项目概述为什么我们需要图解堆操作如果你写过排序算法或者刷过一些关于“Top K”问题的算法题大概率会碰到“堆”这个数据结构。它听起来有点抽象代码实现起来指针上下飞舞稍不留神就容易写错。我自己在初学数据结构时对着书本上那一大段描述父节点、子节点下标的公式推导也是云里雾里直到我亲手画了几次图才真正理解了它的精妙之处。所以今天我们不谈枯燥的理论推导就用最直观的“图解”方式把堆特别是小根堆的创建、插入、删除和排序这几个核心操作像拆解乐高积木一样一步步画给你看。堆的本质是一个完全二叉树并且满足堆序性质对于小根堆任何一个节点的值都小于或等于其子节点的值。这个简单的规则却衍生出了高效获取极值、动态维护数据集合的能力。无论是操作系统的进程调度优先队列还是实时推荐系统里快速找出最热门的商品堆都扮演着关键角色。这篇文章适合所有对数据结构感兴趣特别是觉得“堆”有点难啃的朋友。我会假设你了解数组和二叉树的基本概念但即使你忘了也没关系看图说话我们从头来过。我的目标是看完这篇你不仅能清晰地在脑中构建出堆的每一步变化还能自信地写出无bug的堆操作代码。2. 核心基石完全二叉树与数组的映射在开始画图之前我们必须统一“语言”。堆虽然逻辑上是一棵树但在计算机内存中几乎总是用一个一维数组来存储。这种存储方式高效且节省空间其映射规则是整个堆操作的基石。2.1 逻辑结构与物理存储想象一棵二叉树它从上到下、从左到右地被“填满”只有最后一层可能不满并且所有节点都向左靠齐。这就是“完全二叉树”。现在我们把这棵树的节点按照层序遍历即先第一层根节点然后第二层从左到右接着第三层……的顺序依次放入一个数组中。图解映射关系假设我们有一个小根堆其逻辑树结构如下数字代表节点值1 (层0) / \ 3 5 (层1) / \ / 4 8 7 (层2)它的层序遍历结果是[1, 3, 5, 4, 8, 7]。这个数组就是堆的物理存储。下标计算公式务必记住对于一个存储在数组heap中、下标从0开始的堆这是大多数编程语言如Java、Python的常见实现方式父节点下标对于任意节点i其父节点下标为(i - 1) // 2整数除法。左孩子下标2 * i 1右孩子下标2 * i 2注意有些教材或C语言实现可能下标从1开始公式会略有不同父节点i/2左孩子2*i。本文统一采用“下标0起始”的约定因为这与主流编程实践一致。在阅读其他资料时务必先确认其下标起始点。2.2 为什么是数组用数组存储有两大无可比拟的优势空间效率高不需要像链表那样存储额外的指针left, right节省内存。随机访问快通过上述公式我们可以在O(1)时间内找到任何节点的父节点或子节点这是堆能高效进行“上浮”和“下沉”操作的前提。实操心得在纸上或白板上画堆时我习惯先画一棵树然后在旁边对应地写出数组。这个“树-数组”对照的过程能极大地加深你对堆物理结构的理解。试着对上面例子中的节点4数组下标3套用公式它的父节点是(3-1)//2 1即数组中的3它的左孩子是2*317已超出数组范围说明它是叶子节点。多练几次直到你能条件反射般地进行换算。3. 堆的核心操作图解与实现理解了存储结构我们就可以深入核心操作了。所有操作都围绕着维护“堆序性质”这一核心目标展开。3.1 操作一插入节点与“上浮”当我们向堆中插入一个新元素时为了保持完全二叉树的结构我们首先把它放到数组的末尾也就是树最后一层最右边的下一个位置。但这肯定会破坏堆序。因此需要将这个新节点向上调整直到它找到合适的位置。这个过程叫“上浮”或“堆化向上”。图解步骤假设现有小根堆[2, 5, 10, 14, 7, 18]对应树结构如下我们要插入新元素4。2 / \ 5 10 / \ / 14 7 18放入末尾数组变为[2, 5, 10, 14, 7, 18, 4]。树结构多了一个右孩子节点4作为节点10的右孩子。2 / \ 5 10 / \ / \ 14 7 18 4 [新节点]开始上浮比较新节点4与其父节点10。4 10违反小根堆性质需要交换。第一次交换交换节点4和节点10。数组变为[2, 5, 4, 14, 7, 18, 10]。2 / \ 5 4 [原节点10] / \ / \ 14 7 18 10 [原节点4]继续上浮节点4的新父节点是2。比较4 2满足堆序上浮停止。代码实现关键点def heap_insert(heap, val): heap.append(val) # 1. 放入末尾 idx len(heap) - 1 # 2. 上浮过程 while idx 0: parent_idx (idx - 1) // 2 if heap[idx] heap[parent_idx]: # 小根堆当前比父小才交换 heap[idx], heap[parent_idx] heap[parent_idx], heap[idx] idx parent_idx # 当前节点索引更新为父节点 else: break # 已满足堆序停止上浮注意事项上浮的循环条件是idx 0因为根节点下标0没有父节点。交换时一定要同步更新当前节点的索引idx为父节点索引才能继续向上比较。3.2 操作二删除堆顶与“下沉”删除操作通常指的是删除堆顶元素即最小值。我们不能简单地将数组第一个元素移除因为那样会破坏完全二叉树的结构。标准的做法是用数组最后一个元素覆盖堆顶元素。移除最后一个元素现在它已经在堆顶了。从新的堆顶开始向下调整使其满足堆序。这个过程叫“下沉”或“堆化向下”。图解步骤接上例删除堆顶元素2。 初始堆[2, 5, 4, 14, 7, 18, 10]2 / \ 5 4 / \ / \ 14 7 18 10覆盖与移除用最后一个元素10覆盖堆顶2然后移除最后一个位置。数组变为[10, 5, 4, 14, 7, 18]。10 [原末尾节点] / \ 5 4 / \ / 14 7 18开始下沉从根节点10开始我们需要在它的左右孩子5和4中找出较小者。4 5且4 10违反堆序所以节点10需要与节点4交换。第一次交换交换节点10和节点4。数组变为[4, 5, 10, 14, 7, 18]。4 / \ 5 10 [原节点4] / \ / 14 7 18继续下沉节点10的新位置其左右孩子是18左和空右。只需比较节点10和节点18。10 18满足堆序下沉停止。代码实现关键点def heap_pop(heap): if not heap: return None top_val heap[0] # 保存要返回的堆顶值 # 1. 末尾覆盖堆顶 heap[0] heap[-1] heap.pop() # 移除末尾元素 n len(heap) idx 0 # 2. 下沉过程 while True: smallest idx left 2 * idx 1 right 2 * idx 2 # 找出当前节点、左孩子、右孩子三者中的最小值索引 if left n and heap[left] heap[smallest]: smallest left if right n and heap[right] heap[smallest]: smallest right # 如果最小值不是当前节点则需要交换并继续下沉 if smallest ! idx: heap[idx], heap[smallest] heap[smallest], heap[idx] idx smallest # 当前节点索引更新为交换的子节点索引 else: break # 当前节点已比两个孩子都小或没有孩子停止下沉 return top_val实操心得下沉时必须同时比较左右两个孩子并与其中更小的那个交换这样才能保证交换后新的父节点同时小于两个子节点维持堆序。这是新手最容易出错的地方只比较一边是不行的。3.3 操作三从无序数组“创建”堆给定一个无序数组如何将其调整成一个合法的堆一个直观的方法是从左到右遍历数组对每个元素执行“上浮”操作。这种方法的时间复杂度是O(n log n)。但存在一种更高效、时间复杂度为O(n)的“自底向上建堆”法。高效建堆法Floyd算法图解核心思想从最后一个非叶子节点开始从右到左、从下到上对每个节点执行一次“下沉”操作。 为什么是最后一个非叶子节点因为叶子节点没有孩子本身已经满足堆序或者说下沉操作对叶子节点无效。步骤对无序数组[9, 3, 7, 6, 5, 1, 10, 2]建立小根堆。找到起点数组长度n8。最后一个非叶子节点的下标是(n//2) - 1 (8//2)-1 3。即节点6数组下标3。第一轮下沉节点6节点6的左右孩子是2左和不存在右。6 2交换。数组变为[9, 3, 7, 2, 5, 1, 10, 6]。第二轮下沉节点7下标2节点7的左右孩子是1左和10右。最小孩子是1。7 1交换。数组变为[9, 3, 1, 2, 5, 7, 10, 6]。第三轮下沉节点3下标1节点3的左右孩子是2左和5右。最小孩子是2。3 2交换。数组变为[9, 2, 1, 3, 5, 7, 10, 6]。交换后节点3在新位置下标3需要继续下沉吗它的孩子是63 6停止。第四轮下沉节点9下标0节点9的左右孩子是2左和1右。最小孩子是1。9 1交换。数组变为[1, 2, 9, 3, 5, 7, 10, 6]。交换后节点9在新位置下标2需要继续下沉。它的孩子是7左和10右最小孩子是7。9 7交换。数组变为[1, 2, 7, 3, 5, 9, 10, 6]。节点9在新位置下标5已是叶子节点停止。最终得到小根堆[1, 2, 7, 3, 5, 9, 10, 6]。代码实现def heapify(arr): n len(arr) # 从最后一个非叶子节点开始向前遍历 for i in range(n // 2 - 1, -1, -1): _sift_down(arr, i, n) # 调用下沉函数n表示当前考虑的堆大小 def _sift_down(arr, i, n): 在arr中对下标为i的节点进行下沉n是当前堆的逻辑大小 while True: smallest i left 2 * i 1 right 2 * i 2 if left n and arr[left] arr[smallest]: smallest left if right n and arr[right] arr[smallest]: smallest right if smallest ! i: arr[i], arr[smallest] arr[smallest], arr[i] i smallest # 更新当前节点索引继续下沉 else: break为什么是O(n)直观上感觉有n/2个节点要下沉每个下沉O(log n)似乎是O(n log n)。但精确计算需要考虑节点高度。大部分节点都在底层高度小下沉代价低。数学推导证明其摊还复杂度为O(n)。记住结论自底向上建堆比逐个插入更高效。4. 堆排序一种不稳定的选择排序堆排序是堆数据结构的一个经典应用。它利用大根堆或小根堆的特性实现了一种原地、时间复杂度为O(n log n)的排序算法。这里我们以升序排序为例通常使用大根堆更为直观堆顶最大但用小根堆也能实现只是步骤稍显绕。我们讲解更标准的大根堆版本。堆排序三部曲建堆将待排序的无序数组调整成一个大根堆。交换与缩小将堆顶元素当前最大值与堆的最后一个元素交换。此时最大值已位于数组末尾的正确位置。调整将交换后的新堆顶元素进行“下沉”调整以恢复大根堆的性质注意此时堆的大小减1末尾已排序的部分不再参与堆调整。重复步骤2和3直到堆的大小变为1。图解过程大根堆升序排序对数组[4, 10, 3, 5, 1]进行升序排序。初始建堆大根堆应用自底向上建堆法下沉操作比较时用“”。无序数组:[4, 10, 3, 5, 1]建堆后:[10, 5, 3, 4, 1](树表示10是根左右孩子是5和35的孩子是4和1)第一轮交换堆顶10与最后一个元素1交换。数组[1, 5, 3, 4, 10]。此时10已在最终位置。调整对新的堆顶1进行下沉堆大小现在为4。1与孩子(5,3)中的最大者5交换 -[5, 1, 3, 4, 10]。1继续与孩子4交换 -[5, 4, 3, 1, 10]。调整后堆为[5, 4, 3, 1]。第二轮交换堆顶5与当前最后一个元素1交换。数组[1, 4, 3, 5, 10]。5在最终位置。调整对堆顶1下沉堆大小3。1与孩子(4,3)中的最大者4交换 -[4, 1, 3, 5, 10]。1是叶子节点停止。调整后堆为[4, 1, 3]。第三轮交换堆顶4与当前最后一个元素3交换。数组[3, 1, 4, 5, 10]。4在最终位置。调整对堆顶3下沉堆大小2。3与孩子1比较31停止。调整后堆为[3, 1]。第四轮交换堆顶3与最后一个元素1交换。数组[1, 3, 4, 5, 10]。3在最终位置。堆大小变为1排序结束。最终升序数组为[1, 3, 4, 5, 10]。代码实现def heap_sort(arr): n len(arr) # 1. 构建大根堆 for i in range(n // 2 - 1, -1, -1): _sift_down_max(arr, i, n) # 2. 逐个提取元素 for i in range(n - 1, 0, -1): # 将当前堆顶最大值交换到末尾i处 arr[0], arr[i] arr[i], arr[0] # 对新的堆顶进行下沉恢复大根堆堆大小变为i _sift_down_max(arr, 0, i) def _sift_down_max(arr, i, n): 大根堆下沉 while True: largest i left 2 * i 1 right 2 * i 2 if left n and arr[left] arr[largest]: largest left if right n and arr[right] arr[largest]: largest right if largest ! i: arr[i], arr[largest] arr[largest], arr[i] i largest else: break注意事项与特性不稳定排序堆排序是不稳定的。例如对[5a, (5, b), 3]排序两个5的相对顺序可能在建堆和交换过程中被打乱。原地排序只需要常数级别的额外空间。时间复杂度建堆O(n) (n-1)次下沉O(n log n)总体为O(n log n)。实际效率虽然时间复杂度与快速排序、归并排序同阶但由于其数据访问方式跳跃式访问父子节点对CPU缓存不友好在实际应用中通常比快速排序慢一些。但其最坏情况下的O(n log n)性能是稳定的。5. 常见问题与排查技巧实录即使理解了原理亲手实现时还是会遇到各种坑。下面是我在学习和教学过程中总结的几个典型问题。5.1 下标越界魔鬼在细节里这是实现“下沉”操作时最高频的错误。# 错误示范 def sift_down_wrong(arr, i): while (2*i 1) len(arr): # 只检查了左孩子存在 child 2*i 1 if child 1 len(arr) and arr[child1] arr[child]: # 这里才检查右孩子 child 1 if arr[child] arr[i]: swap(arr, i, child) i child else: break问题循环条件while (2*i 1) len(arr)只保证了左孩子存在。如果节点只有左孩子没有右孩子这没问题。但循环内部的if child 1 len(arr)...逻辑是正确的。然而更清晰的写法是像前面示例那样在循环体内分别计算左右孩子下标并先检查是否越界再比较。正确做法在循环开始时将smallest/largest初始化为当前节点i然后分别判断左右孩子索引是否在堆大小范围内再进行比较。这样可以清晰地处理“只有左孩子”或“无孩子”的情况。5.2 堆序比较符号弄反小根堆和大根堆的实现就差一个比较符号但很容易写懵尤其是在堆排序中既需要建大根堆又需要从小根堆里弹最小值。小根堆heap[child] heap[parent]时上浮heap[child] heap[current]时下沉找更小的孩子。大根堆heap[child] heap[parent]时上浮heap[child] heap[current]时下沉找更大的孩子。排查技巧写完后用一组简单数据手动模拟一遍。例如对小根堆插入[3, 1, 2]看最终堆顶是不是1。对于大根堆排序输入[2,1,3]看输出是否为[1,2,3]。5.3 建堆起点的计算错误“自底向上建堆”时起始索引是n // 2 - 1下标0起始。很多人会记成n // 2或(n-1) // 2。记忆方法最后一个节点的下标是n-1。它的父节点下标是((n-1) - 1) // 2 (n-2) // 2。由于整数除法的性质当n为偶数时(n-2)//2等于n//2 - 1当n为奇数时也等于n//2 - 1例如n7, n//2-13, (7-2)//22等等这里需要仔细验证。更稳妥的方法是直接记住结论对于下标0开始的数组建堆从n//2 - 1开始到0结束。可以代入n1空堆或单元素无需建堆1//2-1 -1循环不执行、n22//2-10对根节点下沉、n33//2-10等简单情况验证。5.4 堆排序后顺序不符合预期如果想用小根堆实现升序排序过程会有点别扭建小根堆后堆顶是最小值但你不能直接把它放到数组开头然后调整因为这会破坏后面元素的相对位置。通常的做法是建小根堆后反复取出堆顶删除操作取出的元素依次放入一个新数组得到的就是升序序列。但这需要额外O(n)空间。标准且原地的堆排序如前面所述使用大根堆进行升序排序是更直接和高效的做法。如果你写出的堆排序结果不对首先检查我建的是大根堆还是小根堆我的比较符号在排序的调整阶段对吗5.5 性能问题何时用逐个插入何时用自底向上建堆逐个插入上浮适用于数据流式输入的场景即你不知道所有数据来一个插入一个动态维护一个堆。时间复杂度为O(n log n)。自底向上建堆Floyd算法适用于你已经拥有全部数据的数组想一次性将其构建成堆。时间复杂度为O(n)更优。选择建议在解决“给定数组构建堆”这类问题时无脑选择自底向上建堆法。只有在实现优先队列Priority Queue需要支持持续插入操作时才使用插入上浮法。画图是理解堆操作最强大的工具。我建议你在学习时准备纸笔对于每一个插入、删除、建堆、排序的步骤都在纸上画出树形结构和数组的变化。这个过程看似慢却是将抽象逻辑内化为直觉的最快路径。当你能够不假思索地画出堆操作的全过程时写代码就只是把这些步骤翻译成语言而已再也难不倒你了。

相关新闻

飞腾E2000Q处理器IO BANK硬件设计:从信号完整性到PCB布线的工程实践

飞腾E2000Q处理器IO BANK硬件设计:从信号完整性到PCB布线的工程实践

1. 先搞清楚E2000Q的IO BANK设计到底要解决什么问题 如果你正在接触飞腾E2000Q的板级硬件设计,尤其是第一次处理它的IO接口,最应该关心的不是某个引脚的具体功能,而是 如何正确地把CPU的物理信号引出来,并确保它们能在你的目标板…

2026/8/2 12:55:26 阅读更多 →
树莓派5+Hailo-8多流AI推理基准测试与优化实战

树莓派5+Hailo-8多流AI推理基准测试与优化实战

1. 项目概述:边缘AI推理的新标杆最近在折腾树莓派5,特别是搭配了Hailo-8 AI加速模块后,性能表现确实让人眼前一亮。但很多朋友拿到这套组合后,可能只是跑跑官方的Demo,或者用单张图片测试一下YOLO的帧率,总…

2026/8/2 12:55:26 阅读更多 →
BetterNCM安装器深度解析:从零打造网易云音乐插件生态

BetterNCM安装器深度解析:从零打造网易云音乐插件生态

BetterNCM安装器深度解析:从零打造网易云音乐插件生态 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer 你是否曾经想过,为什么网易云音乐客户端的功能总是固定的…

2026/8/2 12:55:26 阅读更多 →

最新新闻

具身智能:从顶会趋势到落地挑战,从业者如何跨越理想与现实鸿沟

具身智能:从顶会趋势到落地挑战,从业者如何跨越理想与现实鸿沟

1. 从顶会热潮到落地冷思考:具身智能的十字路口 刚结束的ICRA和CVPR,无疑是全球机器人学和计算机视觉领域的两场年度盛宴。朋友圈里刷屏的论文接收喜报、会场里人头攒动的海报展,还有那些让人眼前一亮的Demo视频,无不散发着一种技…

2026/8/2 17:10:00 阅读更多 →
C++运行时机制二进制剖析:虚函数表、多重继承与异常处理

C++运行时机制二进制剖析:虚函数表、多重继承与异常处理

1. 项目概述:从二进制视角重新理解C运行时如果你写过几年C,用过虚函数、处理过多重继承的指针转换,或者调试过让人头疼的异常崩溃,那你可能已经对它们的“行为”很熟悉了。但你是否想过,当你写下virtual void foo()时&…

2026/8/2 17:10:00 阅读更多 →
scrcpy投屏快捷键

scrcpy投屏快捷键

2026/8/2 17:10:00 阅读更多 →
基于YOLO与Web技术的危险物品检测系统实战指南

基于YOLO与Web技术的危险物品检测系统实战指南

1. 项目概述:从需求到实现的完整闭环 最近在做一个挺有意思的项目,一个基于深度学习的危险物品检测系统,而且带网页界面。这玩意儿听起来挺高大上,但其实核心逻辑很清晰:就是让电脑能像人一样,从摄像头或者…

2026/8/2 17:10:00 阅读更多 →
433MHz射频模块入门:从ASK调制到Arduino无线通信实战

433MHz射频模块入门:从ASK调制到Arduino无线通信实战

1. 项目概述:为什么选择433MHz简单射频链路?如果你玩过Arduino或者树莓派,想给项目加个无线遥控或者数据传输功能,肯定纠结过选什么方案。Wi-Fi、蓝牙模块现在遍地都是,但有时候你会发现它们有点“杀鸡用牛刀”——功耗…

2026/8/2 17:09:00 阅读更多 →
20260801-01-行业热点-数字孪生赛道融资热潮

20260801-01-行业热点-数字孪生赛道融资热潮

数字孪生赛道融资热潮:飞渡科技、51视界、漂视网络的资本博弈 前言 2026年数字孪生赛道迎来新一轮融资热潮,头部企业纷纷获得资本青睐。本文将从融资动态、估值变化、投资逻辑三个维度,深入分析这场资本博弈背后的行业趋势。 一、融资热潮&am…

2026/8/2 17:09:00 阅读更多 →

日新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/2 0:00:38 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/2 0:00:38 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:38 阅读更多 →

周新闻

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

最大流算法详解:从水管网络到Ford-Fulkerson与Dinic实战

1. 从水管网络到最大流:一个核心问题的诞生想象一下,你是一个城市供水系统的总工程师。你的城市有多个水源(水库),需要通过一个复杂的地下管道网络,将水输送到各个居民区。每条管道都有其最大通水能力&…

2026/8/2 0:00:38 阅读更多 →
基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

基于Springboot的企业门户网站(源码+LW+调试文档+讲解)

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/8/2 0:00:38 阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:38 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/2 6:34:16 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/2 2:47:48 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/2 0:23:22 阅读更多 →