C++归并排序进阶:从函数模板到函数对象的泛型编程实践
1. 项目概述从排序到泛型编程的实战演进排序算法是每个C开发者绕不开的基础而“归并排序”因其稳定的O(n log n)时间复杂度在处理大规模数据或要求稳定性的场景下地位举足轻重。但很多教程止步于对一个int数组的排序这在实际开发中是远远不够的。想象一下你需要排序一个double数组、一个自定义的Student对象数组或者有时需要升序有时需要降序。如果每次都重写一遍算法代码将变得冗长且难以维护。这正是我们这次要深入探讨的旅程如何将一个针对固定数据类型如int的归并排序一步步演进为一个高度复用、灵活强大的工业级组件。我们将从最基础的递归分治实现开始然后引入函数模板使其能够处理任意可比较的数据类型。最后我们将触及C泛型编程的精髓之一——函数对象通过它来实现自定义的比较逻辑如递增、递减甚至按对象某个特定成员排序让我们的排序算法真正“活”起来适应千变万化的业务需求。这个过程本身就是一次从“写代码”到“设计代码”的思维升级。2. 归并排序核心原理与基础实现2.1 算法思想拆解分而治之的典范归并排序是“分治法”的经典应用。它的核心思想非常直观如果要排序一个数组我们先把数组分成两半分别把这两半排好序然后再将这两个有序的子数组合并成一个大的有序数组。而如何把两半排好序呢答案是递归地调用同样的过程。这个过程可以分解为两个主要阶段分割递归地将当前待排序数组平均分成两个子序列直到每个子序列只包含一个元素一个元素本身自然就是有序的。合并递归地将两个已经排序的子序列合并成一个完整的排序序列。这是归并排序的灵魂所在。合并两个有序数组是一个高效的操作时间复杂度为O(n)。假设我们有两个数组A [1, 3, 5]和B [2, 4, 6]合并时我们只需要同时从两个数组的头部开始比较每次将较小的元素放入结果数组并移动相应数组的指针。这个过程就像两列已经按身高排好队的学生合并成一列你只需要依次从两列队首选出较矮的那位即可效率很高。2.2 固定数据类型的C实现让我们先实现一个针对int数组的、最朴素的归并排序。理解这个基础版本至关重要它是所有后续扩展的基石。#include iostream #include vector // 合并两个有序子数组 [left, mid] 和 [mid1, right] void merge(int arr[], int left, int mid, int right) { int n1 mid - left 1; // 左子数组长度 int n2 right - mid; // 右子数组长度 // 创建临时数组 std::vectorint L(n1), R(n2); // 拷贝数据到临时数组 for (int i 0; i n1; i) L[i] arr[left i]; for (int j 0; j n2; j) R[j] arr[mid 1 j]; // 合并临时数组回 arr[left..right] int i 0, j 0, k left; while (i n1 j n2) { // 关键比较步骤默认使用小于号实现升序排序 if (L[i] R[j]) { arr[k] L[i]; i; } else { arr[k] R[j]; j; } k; } // 拷贝剩余元素如果有 while (i n1) { arr[k] L[i]; i; k; } while (j n2) { arr[k] R[j]; j; k; } } // 递归进行归并排序 void mergeSort(int arr[], int left, int right) { if (left right) return; // 基线条件子数组只有一个或零个元素 int mid left (right - left) / 2; // 防止(leftright)溢出 mergeSort(arr, left, mid); // 排序左半部分 mergeSort(arr, mid 1, right); // 排序右半部分 merge(arr, left, mid, right); // 合并已排序的两部分 } // 辅助函数方便调用 void mergeSort(int arr[], int n) { mergeSort(arr, 0, n - 1); }关键点解析与注意事项临时数组的使用合并过程需要额外的空间这里使用std::vector动态管理比原生数组更安全方便。空间复杂度为O(n)。mid的计算使用left (right - left) / 2而非(left right) / 2是为了避免在left和right都很大时求和导致的整数溢出。这是一个经典的防溢出技巧。稳定性在merge函数的比较条件if (L[i] R[j])中我们使用了而不是。这保证了当两个元素相等时位于左子数组原数组中靠前的元素会被优先放入结果数组从而保持了排序的稳定性。这是归并排序的一个重要特性。递归深度归并排序的递归深度约为log₂n对于现代编译器和一般规模的数据是安全的但极端情况下如n极大需注意栈溢出风险。工业级实现可能会采用迭代自底向上的版本以避免此问题。实操心得在初次编写时最容易出错的地方是数组下标的处理。left、mid、right这些边界值在递归调用和合并时必须保持逻辑一致。建议在纸上画出一个包含6-8个元素的小数组手动模拟一遍递归分割和合并的过程对理解下标变化有奇效。3. 引入函数模板实现泛型排序上面的代码只能排序int数组。如果我们想排序double、string甚至自定义类型呢复制粘贴代码并修改类型这违反了DRYDon‘t Repeat Yourself原则。C的函数模板正是为此而生。它允许我们编写一个“蓝图”编译器根据调用时提供的具体类型来生成对应的代码。3.1 函数模板的基本语法与应用模板使用关键字template声明后面跟着模板参数列表用尖括号括起来。typename T或class T表示T是一个占位符类型。我们将之前的merge和mergeSort函数模板化#include iostream #include vector #include string // 用于测试string类型 // 模板化的合并函数 template typename T void merge(T arr[], int left, int mid, int right) { int n1 mid - left 1; int n2 right - mid; // 使用vectorT作为临时数组 std::vectorT L(n1), R(n2); for (int i 0; i n1; i) L[i] arr[left i]; for (int j 0; j n2; j) R[j] arr[mid 1 j]; int i 0, j 0, k left; while (i n1 j n2) { // 注意这里假设类型T支持小于等于()操作符 if (L[i] R[j]) { arr[k] L[i]; i; } else { arr[k] R[j]; j; } k; } while (i n1) { arr[k] L[i]; i; k; } while (j n2) { arr[k] R[j]; j; k; } } // 模板化的递归排序函数 template typename T void mergeSort(T arr[], int left, int right) { if (left right) return; int mid left (right - left) / 2; mergeSort(arr, left, mid); mergeSort(arr, mid 1, right); merge(arr, left, mid, right); } // 模板化的辅助函数 template typename T void mergeSort(T arr[], int n) { mergeSort(arr, 0, n - 1); }现在我们可以用同一套代码排序多种类型int main() { // 排序int数组 int intArr[] {12, 11, 13, 5, 6, 7}; int intSize sizeof(intArr) / sizeof(intArr[0]); mergeSort(intArr, intSize); // 排序double数组 double doubleArr[] {3.14, 1.59, 2.65, 3.58, 9.79}; int doubleSize sizeof(doubleArr) / sizeof(doubleArr[0]); mergeSort(doubleArr, doubleSize); // 排序std::string数组 (按字典序) std::string strArr[] {banana, apple, cherry, date}; int strSize sizeof(strArr) / sizeof(strArr[0]); mergeSort(strArr, strSize); // 输出结果... return 0; }3.2 模板实现的细节与约束隐式接口与编译时多态模板函数merge中有一行if (L[i] R[j])。这行代码定义了一个隐式接口类型T必须支持运算符。当你用std::string调用时编译器检查string有运算符于是生成string特化版本的代码。这种多态发生在编译时称为编译时多态或静态多态。类型推导在调用mergeSort(intArr, intSize)时编译器自动推导出模板参数T为int无需我们显式指定mergeSortint(...)。这大大方便了使用。注意事项模板虽然强大但错误信息可能令人困惑。如果你尝试对一个没有定义运算符的自定义类对象数组进行排序编译器会在模板实例化时报错错误信息可能会指向模板内部很深的地方如merge函数内的比较行。学习阅读模板错误信息是C进阶的必修课。一个技巧是先确保你的自定义类型定义了必要的比较运算符。4. 使用函数对象实现自定义比较逻辑模板化解决了类型问题但排序逻辑还是硬编码的升序。如果我们想要降序排序或者想按自定义规则排序例如按学生对象的成绩排序该怎么办修改模板函数内部的比较符号这同样会导致代码重复且不灵活。解决方案是将比较逻辑抽象出来作为一个可替换的组件传入排序函数。在C中有几种方式可以实现函数指针、std::function、以及这里我们要重点介绍的——函数对象。4.1 什么是函数对象函数对象也叫仿函数是重载了函数调用运算符()的类或结构体的对象。因为它是一个对象所以可以拥有状态成员变量这比普通函数指针更强大。// 一个简单的函数对象实现升序比较 struct AscendingComparator { template typename T bool operator()(const T a, const T b) const { return a b; // 如果a b则a应该排在b前面升序 } }; // 另一个函数对象实现降序比较 struct DescendingComparator { template typename T bool operator()(const T a, const T b) const { return a b; // 如果a b则a应该排在b前面降序 } };使用起来就像调用函数一样AscendingComparator ascComp; bool result ascComp(3, 5); // 返回 true因为 3 54.2 改造归并排序以接受函数对象我们需要在排序函数中增加一个模板参数Compare用来接收这个比较“策略”。在合并时不再使用固定的而是使用传入的比较器对象comp来判断两个元素的顺序。// 模板化的合并函数接受一个比较器对象 template typename T, typename Compare void merge(T arr[], int left, int mid, int right, Compare comp) { int n1 mid - left 1; int n2 right - mid; std::vectorT L(n1), R(n2); for (int i 0; i n1; i) L[i] arr[left i]; for (int j 0; j n2; j) R[j] arr[mid 1 j]; int i 0, j 0, k left; while (i n1 j n2) { // 使用传入的比较器comp来决定顺序 // 如果comp(L[i], R[j])为true则认为L[i]应该排在R[j]前面 if (comp(L[i], R[j])) { arr[k] L[i]; i; } else { arr[k] R[j]; j; } k; } while (i n1) { arr[k] L[i]; i; k; } while (j n2) { arr[k] R[j]; j; k; } } // 模板化的递归排序函数接受一个比较器对象 template typename T, typename Compare void mergeSort(T arr[], int left, int right, Compare comp) { if (left right) return; int mid left (right - left) / 2; mergeSort(arr, left, mid, comp); mergeSort(arr, mid 1, right, comp); merge(arr, left, mid, right, comp); } // 辅助函数方便调用 template typename T, typename Compare void mergeSort(T arr[], int n, Compare comp) { mergeSort(arr, 0, n - 1, comp); }4.3 实战应用多种排序需求现在我们的排序算法拥有了前所未有的灵活性。场景一基本数据类型的升序/降序int arr[] {5, 2, 9, 1, 5, 6}; int n sizeof(arr)/sizeof(arr[0]); // 升序排序 mergeSort(arr, n, AscendingComparator{}); // 此时 arr {1, 2, 5, 5, 6, 9} // 降序排序 mergeSort(arr, n, DescendingComparator{}); // 此时 arr {9, 6, 5, 5, 2, 1}场景二按自定义规则排序假设我们有一个Student结构体想按成绩降序排序成绩相同则按姓名升序排序。struct Student { std::string name; int score; }; // 自定义比较器 struct StudentComparator { bool operator()(const Student a, const Student b) const { if (a.score ! b.score) { return a.score b.score; // 成绩高的在前 } return a.name b.name; // 成绩相同姓名字典序小的在前 } }; int main() { Student students[] {{Alice, 85}, {Bob, 92}, {Charlie, 85}}; int size sizeof(students)/sizeof(students[0]); mergeSort(students, size, StudentComparator{}); for (const auto s : students) { std::cout s.name : s.score std::endl; } // 输出 // Bob: 92 // Alice: 85 // Charlie: 85 return 0; }场景三使用C标准库已有的函数对象C在functional头文件中提供了许多预定义的函数对象如std::lessT、std::greaterT等我们可以直接使用。#include functional int arr[] {5, 2, 9, 1}; int n sizeof(arr)/sizeof(arr[0]); // 使用std::lessint进行升序排序默认 mergeSort(arr, n, std::lessint{}); // 使用std::greaterint进行降序排序 mergeSort(arr, n, std::greaterint{});实操心得函数对象比函数指针的优势在于可以被编译器内联优化并且可以携带状态。例如你可以设计一个比较器它内部有一个“比较模式”标志位通过设置这个标志位同一个比较器对象可以在运行时切换升序或降序逻辑而无需创建两个不同的对象。这种灵活性是简单函数指针难以实现的。5. 性能考量、优化与边界情况处理5.1 时间复杂度与空间复杂度分析归并排序的时间复杂度在最好、最坏和平均情况下都是O(n log n)。这是因为它每次都将问题规模减半log n层并且每一层都需要进行O(n)的合并操作。这个效率非常稳定不像快速排序那样受数据初始状态影响。空间复杂度是O(n)主要来自合并时需要的临时数组。递归调用栈的空间复杂度是O(log n)通常可以忽略。这是归并排序的一个缺点即它不是原地排序算法。在内存受限的环境下需要谨慎使用。5.2 潜在优化策略小数组切换为插入排序对于很小的子数组例如长度小于16递归和函数调用的开销可能比排序本身还大。一个常见的优化是在递归到足够小的规模时改用简单的插入排序。插入排序对小规模、近乎有序的数据效率很高。template typename T, typename Compare void mergeSortOptimized(T arr[], int left, int right, Compare comp) { const int INSERTION_SORT_THRESHOLD 16; if (right - left 1 INSERTION_SORT_THRESHOLD) { insertionSort(arr, left, right, comp); // 实现一个插入排序 return; } // ... 原有的归并排序逻辑 }避免频繁内存分配我们当前的实现在每次merge调用时都会创建新的vector。频繁的内存分配/释放会影响性能。一个优化方案是在排序入口处一次性分配一个与原数组等大的临时数组然后在所有递归调用中复用这个临时空间。自底向上的迭代版本递归版本代码简洁但存在函数调用开销和栈溢出风险。迭代版本使用循环首先将数组视为n个长度为1的有序子数组然后两两合并成长度为2的有序子数组再合并成长度为4的以此类推直到整个数组有序。这种版本没有递归开销且代码通常更利于编译器优化。5.3 边界情况与异常处理空数组或单元素数组我们的基线条件if (left right) return;已经正确处理了这种情况。包含重复元素归并排序是稳定的重复元素的相对位置不会改变这通常是期望的行为。大数组与栈溢出对于极端大的n例如十亿级别递归深度log₂n大约为30这在大多数系统上是安全的。但如果实现有误如递归未收敛或系统栈空间极小则可能溢出。迭代版本是解决此问题的根本方法。自定义类型的比较确保传入的自定义比较器满足严格弱序要求。即对于所有元素a, b, ccomp(a, a)必须为 false非自反性。如果comp(a, b)为 true则comp(b, a)必须为 false非对称性。如果comp(a, b)为 true 且comp(b, c)为 true则comp(a, c)必须为 true传递性。如果!comp(a, b) !comp(b, a)则a和b是等价的。 不满足严格弱序的比较器会导致排序结果未定义甚至引发程序崩溃。6. 从函数对象到Lambda表达式现代C的简洁之道C11引入了Lambda表达式它提供了一种更简洁、更直观的方式来定义匿名函数对象。在很多场景下我们可以直接用Lambda表达式替代显式定义的函数对象类使代码更加紧凑。例如之前对Student数组的排序可以这样写Student students[] {{Alice, 85}, {Bob, 92}, {Charlie, 85}}; int size sizeof(students)/sizeof(students[0]); // 使用Lambda表达式作为比较器 mergeSort(students, size, [](const Student a, const Student b) { if (a.score ! b.score) return a.score b.score; return a.name b.name; });这行代码[](const Student a, const Student b) { ... }定义了一个Lambda表达式。编译器会自动为其生成一个独一无二的、匿名的函数对象类型。它的功能和之前显式定义的StudentComparator完全一样但写法上嵌入在调用处逻辑一目了然非常适合这种一次性使用的简单比较逻辑。Lambda与函数对象的取舍使用Lambda当比较逻辑简单、只在一处使用、且不需要复杂状态时Lambda是首选代码更清晰。使用显式函数对象类当比较逻辑复杂、需要在多处复用、或者需要携带复杂状态如配置参数时定义一个具名的函数对象类更合适有利于代码组织和维护。我们的mergeSort函数模板完美兼容这两种方式因为Lambda表达式的本质就是一个函数对象。这体现了良好抽象带来的强大扩展性。7. 与STL算法的对比与启示C标准模板库STL中的std::sort和std::stable_sort是排序的终极武器。它们高度优化通常采用内省排序快速排序堆排序等混合算法并且也支持自定义比较器。#include algorithm #include vector std::vectorint vec {5, 2, 9, 1}; // 升序排序 std::sort(vec.begin(), vec.end()); // 降序排序 std::sort(vec.begin(), vec.end(), std::greaterint()); // 使用Lambda自定义排序 std::sort(students.begin(), students.end(), [](const Student a, const Student b){ return a.score b.score; });那么我们为什么还要自己实现归并排序呢教育意义理解经典算法的原理和实现是计算机科学的基石能锻炼我们的递归思维、分治思想和编码能力。稳定性需求std::sort不保证稳定性虽然某些实现可能是而std::stable_sort保证稳定性其底层通常就是归并排序的变体。自己实现有助于理解稳定排序是如何做到的。特定场景优化在链表数据结构上归并排序是天然且最高效的排序方式因为链表节点的重链接是O(1)操作。STL的std::list::sort成员函数通常就使用归并排序。掌握泛型编程范式通过这个完整的练习我们深入实践了函数模板、函数对象、迭代器/指针接口设计等C核心泛型编程概念这是直接调用std::sort所无法获得的经验。自己动手实现一遍再对比STL的实现你会对“如何设计一个优秀的通用算法库”有更深刻的认识。例如STL的排序函数接受的是迭代器范围[first, last)而不是指针和大小这种设计更加通用和安全这是我们未来可以继续改进自己实现的方向。

相关新闻

KernelFlume:弹性核心注意力缩放优化智能体长上下文解码

KernelFlume:弹性核心注意力缩放优化智能体长上下文解码

1. 项目概述:解码长上下文的“弹性”新范式最近在折腾大语言模型推理优化时,一个绕不开的痛点就是长上下文(Long-Context)解码。模型支持128K、200K甚至更长的上下文窗口,听起来很美好,但一到实际推理&…

2026/8/21 19:55:06 阅读更多 →
MySQL迁人大金仓,预编译SQL越跑越慢?我扒了执行计划和绑定变量的底裤,附万字避坑指南

MySQL迁人大金仓,预编译SQL越跑越慢?我扒了执行计划和绑定变量的底裤,附万字避坑指南

一、那个“第6次执行就拉胯”的灵异事件 ,上个月我们组把一个核心政务系统从 MySQL 8.0 迁到人大金仓(KingbaseES V8R6,PG兼容模式)。 开发阶段一切顺利,CRUD跑得飞起。结果上压测那天,监控大屏直接红了&am…

2026/8/21 19:54:06 阅读更多 →
网站建设公司要注意什么?筛选靠谱的建站公司必看攻略

网站建设公司要注意什么?筛选靠谱的建站公司必看攻略

文章摘要:企业官网早已不是一张简单的“电子名片”,面对繁杂的市场供给,企业如何筛选出真正靠谱的建站伙伴?本文基于行业调研数据与技术标准,从硬性标准、选型策略、趋势洞察、常见问题四大维度,为企业决策…

2026/8/21 19:54:06 阅读更多 →

最新新闻

《SpringBoot 3:入门与应用实战》第 4 章 IOC 容器中的 Bean 阅读笔记 6

《SpringBoot 3:入门与应用实战》第 4 章 IOC 容器中的 Bean 阅读笔记 6

《SpringBoot 3:入门与应用实战》第 4 章 IOC 容器中的 Bean 阅读笔记 6 Spring Framework 中的 IOC 容器内部设计极其精巧,提供了强大且实用的功能和机制,通过加载解析 XML 配置文件、注解配置类实现按照配置内容工作。 4.1 BeanFactory 第2…

2026/8/21 20:40:26 阅读更多 →
整理了一套即梦AI 做视频教程资料,常用教程和示例都在里面

整理了一套即梦AI 做视频教程资料,常用教程和示例都在里面

【资源介绍】 这次整理的是《即梦AI 做视频教程》资料合集。 内容已经按主题和目录分类,下载后可以直接查看使用。 【主要内容】 01|2025 02|2026即梦Seedance2.0:5套教程合集,40案例、全套提示词、素材包&#xff0c…

2026/8/21 20:40:26 阅读更多 →
SolidWorks运动仿真实战:从齿轮啮合到铣床加工的动态验证

SolidWorks运动仿真实战:从齿轮啮合到铣床加工的动态验证

最近在整理一个老项目,发现几年前用 SolidWorks 画的齿轮箱模型还在,但当时只做了静态装配,运动关系全靠想象。正好有个新同事问起,怎么才能让这个齿轮组“动”起来,直观地看到啮合过程,甚至模拟出加工时的…

2026/8/21 20:40:26 阅读更多 →
Java面试核心知识点与备战策略全解析

Java面试核心知识点与备战策略全解析

1. 为什么需要Java面试题库? 在互联网行业,Java作为主流编程语言已有20余年历史。根据2023年Stack Overflow开发者调查,Java在全球范围内仍保持着Top 5的编程语言地位。国内一线互联网企业的后端服务中,Java技术栈占比超过60%。这…

2026/8/21 20:40:26 阅读更多 →
Go + webrpc 实战:人在外面,远程查家里 NAS 磁盘和目录

Go + webrpc 实战:人在外面,远程查家里 NAS 磁盘和目录

独立开发者笔记,非官方教程。Token 与 SDK 见 webrpc 控制台。 人不在家里时,常会碰到两个很具体的问题: 家里的 NAS / 小主机还活着吗?磁盘还剩多少空间?某个目录里有没有把备份跑完? 用 frp 可以硬把 W…

2026/8/21 20:40:26 阅读更多 →
文档切分算法详解

文档切分算法详解

一、算法概述 文档切分(Document Chunking)是将长文档分割成适合检索的小段落(chunks)的过程。这是RAG(检索增强生成)系统中最基础也最关键的环节之一。好的文档切分策略直接影响后续检索的准确性和生成的质…

2026/8/21 20:39:25 阅读更多 →

日新闻

机场边检旅客定位系统国产化白皮书:算法、硬件、底座平台全程自主

机场边检旅客定位系统国产化白皮书:算法、硬件、底座平台全程自主

前言随着国家数字基础设施信创替代、关键技术自主可控战略持续深化,口岸智慧安防、边检智能管控领域正全面进入国产化、自主化、安全可控升级周期。当前国内机场边检旅客识别与定位体系长期依赖国外商用视觉算法、进口成像硬件、闭源通用计算平台,存在核…

2026/8/21 0:00:42 阅读更多 →
别再把“数字孪生”当空间智能了!镜像视界揭开四维时空的真正面纱

别再把“数字孪生”当空间智能了!镜像视界揭开四维时空的真正面纱

别再把“数字孪生”当空间智能了!镜像视界揭开四维时空的真正面纱当下数字化建设浪潮中,很多项目将三维可视化、视频贴图叠加的数字孪生等同于空间智能。传统数字孪生更多停留在三维场景复刻,擅长把物理世界“画出来、展示出来”,…

2026/8/21 0:00:42 阅读更多 →
105、车载温度范围-40°C到85°C的影像质量一致性——ISP参数温漂补偿与产线标定策略

105、车载温度范围-40°C到85°C的影像质量一致性——ISP参数温漂补偿与产线标定策略

105、车载温度范围-40C到85C的影像质量一致性——ISP参数温漂补偿与产线标定策略 去年冬天在北方某车厂做A样评审,凌晨四点的黑河试验场,零下三十三度。客户拿了一台冷启动的车,中控屏上倒车影像全是雪花噪点,暗部细节直接糊成一片。我第一反应是sensor温度没上来,暗电流…

2026/8/21 0:00:42 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/21 3:21:33 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/21 0:02:09 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/21 6:07:56 阅读更多 →

月新闻

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

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

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

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

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

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

2026/8/20 21:46:49 阅读更多 →
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/21 0:14:22 阅读更多 →