分治算法-简单了解

本文探讨了分治法的基本概念及其在折半搜索、递归求最大最小值、归并排序、快速排序等经典算法中的应用。同时,介绍了选择问题、矩阵乘法及平面最接近点对问题等高级应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

部分内容来自:http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741370.html  感谢原作者!


这种将整个问题分解为若干个小问题来处理的方法称为分治法。被分解出来的小问题与原问题有相同的解法,这样便于算法的计算(往往采用递归)。

     分治法的控制流程:

  A 判断是否满足最小处理条件,或者说满足了最小规模函数,然后进行处理,或者返回。【有的可能没有】

  B 如果没有满足最小规模,那么就开始对数据进行分块处理。

  C 递归调用A过程

  D 结合一下分治后的结果。


 

1、  折半搜索

一个已经排好序的数组中寻找某一个指定元素。那么便可以折半查找,即一直比较数组中间的元素和指定元素的大小关系,然后决定了下一轮查找的范围(缩小到了一半)。此处采用的是一个循环,记录住left和right,并不断的调整lefe和right下标位置。

2、  递归求最大最小值

通过分治的思想,不断的求出一半中的最大值和最小值,然后再比较左边和右边的最小值以及左边和右边的最大值。在此处获得的想法就是看递归函数的参数如何设置,比如要算出最大值和最小值,那么便可以设置两个参数,这个参数如果是引用,那么数值在每一层递归中都可以使用到。

3、  归并排序

先提到了插入排序:思路就是从第二个位置开始扫描数组,然后把刚遇到的元素插入到前面元素中去,使得前面的元素保持一定的顺序。

归并排序就是通过分治的思想把分开后有序的串拼接到一起。分为两部分,一个是MergeSort函数,主要用于分治,然后通过Merge函数把结果合并到一起。注意的是Merge函数的参数是开始,中间位置和结束位置,是普世的位置,不是从0开始的。归并排序之所以快是因为当把一些有序的数组再合并到一起的时候更加的快捷高效。

归并排序中需要留意的地方:缩小到什么规模的时候可以结束;另外在用数组的时候涉及到频繁的移位。提出了使用链接的归并排序算法。即通过之前提到的链接图来做,而不再是数组。

 

4、快速排序

       快速排序的思想是通过分治的方式,使得某元素左边的元素总是小于分割点处的元素,而分割点处元素的右面元素都大于该元素。算法也是分为两部分,quicksort,主控制函数以及partition函数(用户对指定范围内的函数进行操作,即某元素左边小于某个值;某元素右面的元素大于某个值。)

    

   排序算法   最坏复杂度   平均复杂度

   冒泡排序    O(n2)          O(n2)

   插入排序    O(n2)          O(n2)

   选择排序    O(n2)          O(n2)

   快速排序    O(n2)          nlog(n)

               归并排序    nlog(n)         nlog(n)

      

5、选择问题:在一个数组中选出第k小的元素

        最容易想到的办法就是先排序,然后选出第k小的元素;或者采用分治的思想,采用快速排序中partition的思想,找到小于某元素的所有元素和大于该元素的所有元素。这样在再找第k个的话便更加的方便和快捷。最坏情况下的复杂度为On2,平均复杂度为On.因为知道了左边小于某元素的大概多少个,大于某元素的多少个。先笼统的划分了一个范围。需要注意的就是在partselect中要不断的调整范围。

      

         上述的方法在最差的情况下时间复杂度为O(n2),怎么进一步优化呢。在调用partition函数的时候就是为了找出分界点在哪,那么怎么能快速精确的定位分界点数据呢。可以先把整个数据进行分段,然后把每一段的中间元素放到该端的最前端,这样数组的最前面就是记录每一段的中间数据了。然后再从前段这些数据中找到中间元素。以此元素作为分界值。一面发生最坏的情况。

 

6、矩阵的乘法

两个N*N矩阵的相乘时间复杂度为On3,相加的时间复杂度为On2,这个可以通过想象代码的实现过程就可以。

        于是有人想到了采用分治的思想,就是把矩阵分成一块一块的,然后再进行分块计算。虽然思想不错,但是复杂度没有变化,还是On3。此处可以学学如何推到的时间复杂度。规模降低之后的表示方式,T(n)、T(n/2)表示,以及多出来的操作时什么,复杂度如何表示等。

        分治之后的复杂度仍然是On3,于是有人想着如何降低乘法的次数,这样便可以降低复杂度,于是有人最终通过数学推倒,发现可以把原来分治之后的八次乘法降低为七次惩罚操作和18次加减法计算。目前矩阵最好的时间复杂度是On2.36。

 

7、直线上的最接近点对问题

首先想到的方法就是把这些点先排序,然后便可以通过一遍扫描知道最接近点对。通过分治的思想解决方式如下:按照中位数分为左右两部分,然后在左右两边都找最接近点对,然后再判断两边最接近点之间的距离。从而可以找出最接近点对。

    

8、平面上最接近点对问题

同样可以通过X坐标来把所有的点分成一块一块的,然后求出每一块的最近点对。不同块之间的最近点对计算的时候就要有筛选了,不是计算所有的,而是在限定好的区域内进行选取。时间复杂度为NlogN


<think>好的,用户想要了解分治算法在C语言中的应用。首先,我需要确定用户的基础,可能他有一定的编程经验,但对分治算法不太熟悉。分治算法算法设计中的重要思想,应该先解释清楚基本概念,再给出具体例子。 分治算法的三个步骤:分解、解决、合并。我需要用简单的中文描述这些步骤,避免术语过多导致理解困难。然后,用户可能需要一个具体的例子来理解如何用C语言实现,比如归并排序或者快速排序,这两个都是典型的分治算法例子。选择归并排序可能更直观,因为分解和合并的过程比较明显。 接下来,我需要编写一个C语言的示例代码。确保代码结构清晰,注释详细,方便用户理解每个步骤的作用。同时,要检查代码的正确性,比如递归终止条件是否正确,合并过程是否有逻辑错误。可能用户会复制代码进行测试,所以代码必须正确无误。 另外,需要注意用户提到的系统级指令,比如数学表达式要用$...$格式。在解释时间复杂度的时候,可能需要提到O(n log n),这时候需要用正确的LaTeX格式。同时,独立公式如主定理可能需要使用$$...$$,不过主定理可能稍微复杂,如果用户是初学者,可能不需要深入,但可以简要提及。 用户可能还想知道分治算法的优缺点和应用场景,所以在回答中需要涵盖这些内容。比如优点是可并行、简化问题,缺点是递归开销和可能的重复计算。应用场景可以提到排序、矩阵乘法、大整数乘法等,帮助用户理解其实际用途。 需要确保整个回答结构清晰,分点说明,步骤明确。先讲概念,再讲步骤,然后代码示例,最后优缺点和应用场景。这样用户能够逐步深入理解分治算法在C语言中的实现和应用。 检查是否有遗漏的部分,比如分治算法的关键点:子问题独立、结构相似。是否有必要提到递归与分治的关系?可能简要说明,避免混淆。代码中的merge函数和mergeSort函数需要详细解释,特别是合并过程,这是归并排序的核心。 最后,确保语言口语化,避免过于正式的表达,让用户更容易理解。同时,数学公式和代码格式要符合要求,使用正确的标记方式。可能还需要提醒用户,分治算法在解决问题时的适用条件,避免误用。</think>分治算法是一种将复杂问题拆解为多个相似子问题,并通过递归逐步解决的策略。其核心思想可总结为以下三步: 1. **分解**(Divide):将原问题划分为若干个规模更小的子问题 $$ \text{原问题} \rightarrow \text{子问题}_1 + \text{子问题}_2 + \dots + \text{子问题}_k $$ 2. **解决**(Conquer):递归解决子问题(若子问题足够小则直接求解) 3. **合并**(Combine):将子问题的解合并为原问题的解 以下以归并排序为例,展示C语言实现分治算法的典型结构: ```c #include <stdio.h> // 合并两个有序数组 void merge(int arr[], int left, int mid, int right) { int temp[right - left + 1]; // 临时存储合并结果 int i = left, j = mid + 1, k = 0; // 比较两个子数组元素,依次插入临时数组 while (i <= mid && j <= right) { if (arr[i] <= arr[j]) temp[k++] = arr[i++]; else temp[k++] = arr[j++]; } // 处理剩余元素 while (i <= mid) temp[k++] = arr[i++]; while (j <= right) temp[k++] = arr[j++]; // 将合并结果复制回原数组 for (int m = 0; m < k; m++) { arr[left + m] = temp[m]; } } // 归并排序主函数 void mergeSort(int arr[], int left, int right) { if (left < right) { int mid = left + (right - left) / 2; // 计算中间点 mergeSort(arr, left, mid); // 递归处理左半部分 mergeSort(arr, mid + 1, right); // 递归处理右半部分 merge(arr, left, mid, right); // 合并两个有序子数组 } } int main() { int arr[] = {9,5,2,7,3,8,1}; int n = sizeof(arr)/sizeof(arr[0]); mergeSort(arr, 0, n-1); printf("排序结果: "); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } return 0; } ``` **关键点解析**: 1. **时间复杂度**:归并排序的时间复杂度为$O(n\log n)$,符合分治算法典型特征 2. **递归终止条件**:当`left >= right`时停止分解(子数组长度为1时已有序) 3. **合并操作**:merge函数实现了两个有序子数组合并的核心逻辑 **分治算法的优势**: - 天然适合并行计算 - 通过递归简化复杂问题 - 时间复杂度通常优于暴力解法(例如$O(n^2) \rightarrow O(n\log n)$) **适用场景**: - 排序算法(归并排序、快速排序) - 大规模计算(矩阵乘法、快速傅里叶变换) - 复杂问题分解(汉诺塔问题、最近点对问题) **注意事项**: - 需要确保子问题与原问题具有相同结构 - 递归深度过大会导致栈溢出 - 合并操作的实现质量直接影响最终效率 实际开发中可根据具体问题选择递归实现或迭代实现。对于C语言,需特别注意指针操作和内存管理,避免在分解/合并过程中产生越界访问等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值