免费建立英文网站html简单的网站

张小明 2026/1/13 0:21:37
免费建立英文网站,html简单的网站,改变字体颜色的网站,seo北京网站推广目录 前言 一、归并排序——分治的经典范例 1.1 归并排序的基本思想 1.2 归并排序的算法实现 1.3 归并排序的性能分析 1.4 归并排序的稳定性与适用场景 二、逆序对问题 2.1 什么是逆序对#xff1f; 2.2 逆序对的应用意义 2.3 朴素方法的局限性 三、分治法计算逆序…目录前言一、归并排序——分治的经典范例1.1 归并排序的基本思想1.2 归并排序的算法实现1.3 归并排序的性能分析1.4 归并排序的稳定性与适用场景二、逆序对问题2.1 什么是逆序对2.2 逆序对的应用意义2.3 朴素方法的局限性三、分治法计算逆序对3.1 算法灵感来源3.2 分治算法设计3.3 算法实现3.4 算法正确性证明3.5 时间复杂度分析四、算法优化与变体4.1 原地归并的优化4.2 多路归并4.3 逆序对计算的并行化4.4 树状数组解法五、实际应用与案例分析5.1 推荐系统中的相似度计算5.2 基因重排分析5.3 算法竞赛实例六、拓展与进阶6.1 三维逆序对问题6.2 在线逆序对计算6.3 外部排序中的逆序对七、总结前言在计算机科学的世界里分治策略Divide and Conquer是一种优雅而强大的算法设计范式。它将复杂问题分解为若干个相同或相似的子问题递归地解决这些子问题最后合并子问题的解以得到原问题的解。这种分而治之的思想不仅贯穿于算法设计的各个领域更是许多高效算法背后的核心逻辑。今天我们将深入探讨分治策略的两个经典应用归并排序和逆序对计算。这两个问题看似不同却共享着相同的分治基因展现着这一策略的普适性和强大威力。一、归并排序——分治的经典范例1.1 归并排序的基本思想归并排序是分治策略最直观的体现之一。它的核心思想非常简单(1). 分解将待排序的数组从中间分成两半(2). 解决递归地对两个子数组进行排序(3). 合并将两个已排序的子数组合并成一个有序数组这种递归分解的过程会一直持续到子数组只包含一个元素此时自然是有序的然后开始逐层合并。1.2 归并排序的算法实现#include iostream #include vector using namespace std; // 合并两个已排序数组 void merge(vectorint arr, int left, int mid, int right) { int n1 mid - left 1; int n2 right - mid; // 创建临时数组 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]; // 合并临时数组回原数组 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(vectorint 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); } // 归并排序的包装函数 void mergeSort(vectorint arr) { if (arr.size() 1) return; mergeSort(arr, 0, arr.size() - 1); } // 辅助函数打印数组 void printArray(const vectorint arr) { for (int num : arr) { cout num ; } cout endl; }1.3 归并排序的性能分析归并排序的时间复杂度分析是理解分治策略效率的关键。我们可以通过递归树来分析递归深度每次将数组分成两半递归深度为 O(log n)每层工作量每层需要合并所有元素工作量为 O(n)总时间复杂度O(n log n)空间复杂度为 O(n)因为需要额外的数组来合并子数组。与快速排序的原地排序不同归并排序需要额外的存储空间这是其代价。1.4 归并排序的稳定性与适用场景归并排序是稳定排序算法即相等元素的相对顺序在排序后保持不变。这一特性在某些应用场景中非常重要比如(1). 多关键字排序先按次要关键字排序再按主要关键字排序(2). 数据库操作保持记录的相对顺序(3). 需要稳定排序的算法如某些计算几何算法此外归并排序对于链表这种数据结构特别高效因为链表的合并操作可以在 O(1) 的额外空间内完成。二、逆序对问题2.1 什么是逆序对在深入讨论逆序对的计算方法之前让我们先明确什么是逆序对。定义对于一个数组 A [a₁, a₂, ..., aₙ]如果存在索引 i j 且 A[i] A[j]则称 (i, j) 为一个逆序对inversion pair。示例数组 [2, 4, 1, 3, 5] 中的逆序对有(2, 1): 索引0的值2 索引2的值1(4, 1): 索引1的值4 索引2的值1(4, 3): 索引1的值4 索引3的值3总共3个逆序对。2.2 逆序对的应用意义逆序对计数问题看似简单却有着广泛的实际应用(1). 衡量数据混乱程度逆序对数量可以作为数组乱序程度的量化指标(2). 推荐系统比较用户偏好排序与推荐排序的一致性(3). 基因序列分析在生物信息学中衡量基因序列的相似性(4). 协作过滤评估用户评分的一致性(5). 排序算法分析某些排序算法如冒泡排序的交换次数等于逆序对数量2.3 朴素方法的局限性最直观的逆序对计算方法是检查所有可能的元素对long long countInversionsNaive(const vectorint arr) { long long count 0; int n arr.size(); for (int i 0; i n; i) { for (int j i 1; j n; j) { if (arr[i] arr[j]) { count; } } } return count; }这种方法的时间复杂度为 O(n²)对于大规模数据如 n10⁶来说完全不可行。我们需要更高效的算法。三、分治法计算逆序对3.1 算法灵感来源观察到归并排序过程中有一个重要特性当合并两个已排序的子数组时我们可以高效地计算跨越这两个子数组的逆序对数量。关键洞察在合并过程中如果左子数组的当前元素大于右子数组的当前元素那么左子数组中该元素之后的所有元素也都大于右子数组的当前元素这些都会形成逆序对。3.2 分治算法设计计算逆序对的分治算法遵循与归并排序相同的结构(1). 分解将数组分成两半(2). 解决递归计算左半部分的逆序对和右半部分的逆序对(3). 合并在合并两个已排序子数组的过程中计算跨越中点的逆序对3.3 算法实现#include iostream #include vector using namespace std; // 合并并计算逆序对 long long mergeAndCount(vectorint arr, int left, int mid, int right) { int n1 mid - left 1; int n2 right - mid; 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]; long long inversions 0; 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]; // L[i] 到 L[n1-1] 的所有元素都与 R[j] 形成逆序对 inversions (n1 - i); j; } k; } while (i n1) { arr[k] L[i]; i; k; } while (j n2) { arr[k] R[j]; j; k; } return inversions; } // 分治计算逆序对 long long countInversionsMergeSort(vectorint arr, int left, int right) { long long inversions 0; if (left right) { int mid left (right - left) / 2; // 递归计算左右两部分的逆序对 inversions countInversionsMergeSort(arr, left, mid); inversions countInversionsMergeSort(arr, mid 1, right); // 计算跨越两部分的逆序对 inversions mergeAndCount(arr, left, mid, right); } return inversions; } // 计算逆序对的包装函数 long long countInversions(vectorint arr) { // 传值以保护原数组 return countInversionsMergeSort(arr, 0, arr.size() - 1); }3.4 算法正确性证明为了理解为什么这个算法是正确的我们需要考虑三种类型的逆序对(1). 左逆序对两个元素都在左半部分(2). 右逆序对两个元素都在右半部分(3). 跨越逆序对一个元素在左半部分另一个在右半部分递归调用分别计算了左逆序对和右逆序对。在合并过程中当发现 left[i] right[j] 时我们知道 left[i] 及其之后的所有元素都大于 right[j]因此形成了 (len(left) - i) 个跨越逆序对。这样三种类型的逆序对都被正确计数。3.5 时间复杂度分析与归并排序相同该算法的时间复杂度为 O(n log n)空间复杂度为 O(n)。这比朴素方法的 O(n²) 有了巨大的改进。对于 n10⁶ 的数据朴素方法需要约 5×10¹¹ 次比较假设每秒进行10⁸次比较需要约5000秒而分治方法只需约 2×10⁷ 次比较约0.2秒。四、算法优化与变体4.1 原地归并的优化标准的归并排序需要额外的 O(n) 空间。我们可以通过一些技巧实现原地归并将空间复杂度降低到 O(1)但这通常以增加时间复杂度为代价。// 原地合并的简化示例不完全原地但减少空间使用 void mergeInPlace(vectorint arr, int left, int mid, int right) { int i left, j mid 1; while (i mid j right) { if (arr[i] arr[j]) { i; } else { int value arr[j]; int index j; // 向右移动元素 while (index i) { arr[index] arr[index - 1]; index--; } arr[i] value; i; mid; j; } } }4.2 多路归并对于某些特定场景我们可以将二路归并扩展为多路归并k-way merge这在外部排序如大数据处理中特别有用。4.3 逆序对计算的并行化逆序对计算天然适合并行化处理因为左右子数组的计算相互独立。C17 引入了并行算法支持#include execution #include algorithm // 使用C17并行排序的示例 void parallelMergeSort(vectorint arr) { std::sort(std::execution::par, arr.begin(), arr.end()); } // 注意并行化逆序对计算需要更复杂的实现 // 因为跨越逆序对的计算需要串行处理4.4 树状数组解法除了分治法逆序对问题还可以使用树状数组Fenwick Tree或线段树在 O(n log n) 时间内解决特别适用于需要频繁更新和查询的场景。#include vector #include algorithm #include unordered_map using namespace std; class FenwickTree { private: vectorint bit; int n; public: FenwickTree(int size) { n size; bit.assign(n 1, 0); } void update(int idx, int delta) { while (idx n) { bit[idx] delta; idx idx -idx; } } int query(int idx) { int sum 0; while (idx 0) { sum bit[idx]; idx - idx -idx; } return sum; } }; long long countInversionsFenwick(const vectorint arr) { // 坐标压缩 vectorint sorted_arr arr; sort(sorted_arr.begin(), sorted_arr.end()); unordered_mapint, int rank; for (int i 0; i sorted_arr.size(); i) { rank[sorted_arr[i]] i 1; // 1-based index } FenwickTree bit(arr.size()); long long inversions 0; // 从后向前遍历 for (int i arr.size() - 1; i 0; i--) { int rank_idx rank[arr[i]]; inversions bit.query(rank_idx - 1); // 比当前元素小的已出现元素数量 bit.update(rank_idx, 1); // 标记当前元素已出现 } return inversions; }五、实际应用与案例分析5.1 推荐系统中的相似度计算在电商推荐系统中用户对商品的点击序列可以看作一个排列。通过计算两个用户点击序列的逆序对差异可以衡量他们的兴趣相似度。Kendall Tau距离基于逆序对计数的相似度度量τ 1 - 2 * (逆序对数) / (n*(n-1)/2)5.2 基因重排分析在生物信息学中基因重排是进化的重要机制。通过比较两个物种的基因顺序计算将它们转换为彼此所需的最小逆序对数可以推断物种间的进化距离。5.3 算法竞赛实例逆序对计算是算法竞赛中的经典问题。以LeetCode 315题计算右侧小于当前元素的个数为例#include vector #include algorithm using namespace std; class Solution { public: vectorint countSmaller(vectorint nums) { int n nums.size(); vectorint result(n, 0); vectorpairint, int indexedNums; // (value, original index) for (int i 0; i n; i) { indexedNums.push_back({nums[i], i}); } mergeSort(indexedNums, 0, n - 1, result); return result; } private: void mergeSort(vectorpairint, int nums, int left, int right, vectorint result) { if (left right) return; int mid left (right - left) / 2; mergeSort(nums, left, mid, result); mergeSort(nums, mid 1, right, result); merge(nums, left, mid, right, result); } void merge(vectorpairint, int nums, int left, int mid, int right, vectorint result) { vectorpairint, int temp(right - left 1); int i left, j mid 1, k 0; int rightCount 0; while (i mid j right) { if (nums[i].first nums[j].first) { rightCount; temp[k] nums[j]; } else { result[nums[i].second] rightCount; temp[k] nums[i]; } } while (i mid) { result[nums[i].second] rightCount; temp[k] nums[i]; } while (j right) { temp[k] nums[j]; } for (int idx 0; idx temp.size(); idx) { nums[left idx] temp[idx]; } } };六、拓展与进阶6.1 三维逆序对问题将逆序对问题扩展到三维空间给定三维空间中的点 (xᵢ, yᵢ, zᵢ)计算满足 xᵢ xⱼ, yᵢ yⱼ, zᵢ zⱼ 的三元组 (i, j, k) 数量。这个问题可以通过分治策略结合树状数组或线段树在 O(n log² n) 时间内解决是分治法的经典高阶应用。6.2 在线逆序对计算传统逆序对计算是离线的需要全部数据。在线逆序对问题要求在数据流中实时维护逆序对数量每添加一个新元素就更新计数。这可以通过平衡二叉搜索树或树状数组实现每次插入的复杂度为 O(log n)。6.3 外部排序中的逆序对在大数据处理中当数据无法完全装入内存时需要使用外部排序。归并排序是外部排序的核心算法而逆序对计算可以帮助评估数据的预排序程度从而优化外部排序策略。七、总结分治策略是一种深刻而强大的算法设计思想归并排序和逆序对计算完美地展示了这种思想的优雅和高效。通过将复杂问题分解为可管理的子问题我们不仅能够设计出清晰的算法还能获得优异的性能表现。从归并排序的 O(n log n) 时间复杂度到逆序对计算的高效解法我们看到分治策略如何将看似不同的两个问题统一在相同的框架下。这种统一性正是计算机科学之美的一部分——简单而强大的思想能够解决广泛而多样的问题。理解和掌握分治策略不仅仅是学习几个算法更是培养一种解决问题的思维方式。在面对新问题时我们可以思考这个问题能否分解子问题是否更容易解决解能否合并这种思维模式将贯穿你的整个算法学习之旅帮助我们在复杂问题面前找到清晰而高效的解决路径。
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

静态网站说明书可口可乐营销策划方案

第一章:TensorRT推理加速的核心挑战在深度学习模型部署到生产环境的过程中,推理性能成为关键瓶颈。TensorRT作为NVIDIA推出的高性能推理优化器,能够显著提升模型运行效率,但在实际应用中仍面临多重技术挑战。硬件与算子兼容性问题…

张小明 2026/1/10 13:57:04 网站建设

简述建设一个网站的具体步骤6民治营销网站

古巴语 salsa 音乐语音教学:基于 VoxCPM-1.5-TTS-WEB-UI 的文本转语音技术实现 在哈瓦那的某个舞蹈教室里,老师反复播放一段老式录音:“¡Oye, el ritmo no miente!”——“听着,节奏从不说谎!” 学生们努力模仿着…

张小明 2026/1/10 16:20:18 网站建设

设计好网站科技特长生有哪些科目

LuCI开发终极指南:在离线环境中构建OpenWrt管理界面 【免费下载链接】luci LuCI - OpenWrt Configuration Interface 项目地址: https://gitcode.com/gh_mirrors/lu/luci 想要在没有网络的环境中开发功能强大的路由器Web界面?LuCI作为OpenWrt的官…

张小明 2026/1/10 16:20:13 网站建设

做网站销售工资怎么样wordpress有哪些网站吗

PyTorch-CUDA-v2.9镜像加速导弹轨迹预测 在高动态、强对抗的现代国防系统中,对飞行器运动状态的精准预判已成为战术决策的关键支撑。以导弹轨迹预测为例,传统基于弹道方程和空气动力学模型的方法虽具备物理可解释性,但在面对复杂气流扰动、机…

张小明 2026/1/10 16:20:13 网站建设

做网站是用myecli网站设计师的工作内容

3个关键步骤让你的Vita3K模拟器运行如飞 【免费下载链接】Vita3K Experimental PlayStation Vita emulator 项目地址: https://gitcode.com/gh_mirrors/vi/Vita3K Vita3K模拟器作为一款实验性的PlayStation Vita模拟器,已经能够在Windows、Linux、macOS和And…

张小明 2026/1/10 16:20:17 网站建设

南通营销网站制作三网一体网站建设

在当下这个数字化的商业态势里,小程序已然变成企业去衔接线上用户的重要方式,也是拓展经营渠道的关键工具,作为一种轻量级应用,它不需要进行下载安装行为,便能在微信、支付宝、与百度等超级App之内获取丰富的功能体验&…

张小明 2026/1/10 16:20:20 网站建设