算法详解之分治法具体实现
分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。
分治法解题的一般步骤:
(1)分解,将要解决的问题划分成若干规模较小的同类问题;
(2)求解,当子问题划分得足够小时,用较简单的方法解决;
(3)合并,按原问题的要求,将子问题的解逐层合并构成原问题的解。
一言以蔽之:分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
在认识分治之前很有必要先了解一下递归,当然,递归也是最基本的编程问题,一般接触过编程的人都会对递归有一些认识.为什么要先了解递归呢?你看,根据上面所说的,我们就要将一个问题分成若干个小问题,然后一一求解并且最后合并,这就是一个递归的问题,递归的去分解自身,递归的去解决每一个小问题,然后合并…
关于递归,这里举一个最简单的例子,求N!;
我们只需要定义函数
int calculate(int n)
{
if(n==1)
return 1;
else
return n*calculate(n-1); //调用自身…
}
好了,有了递归的铺垫,我们下来来看一看一个分治算法的问题,归并排序问题…
基本思想:
将待排序元素分成大小大致相同的2个子集合(递归直到最小的排序单元),分别对2个子集合进行排序,最终将排好序的子集合合并成为所要求的排好序的集合。
下面我们用一张图来展示整个流程,最下面的(姑且叫他第一层)是原始数组分成了8个最小排序问题,各自只有一个元素,故不需要排序,大家可以看到,我们通过分而治之的思想把对最初数组的排序分为了若干个只有一个元素的小数组的排序,然后第二层,我们进行了合并,将每两个最小排序结果合并为有两个元素的数组,然后逐层往上进行合并,就有了最后的结果…
下面我们来看一下这个算法的具体实现,下面的MERGE-SORT (A, p, r)表示对数组A[p->r]的排序过程.其中p->r代表从p到r.
MERGE-SORT (A, p, r)
1. IF p < r // 进行A[p->r]的排序过程自然需要p<r的前提条件
2. THEN q = [(p + r)/2] // 将当前的排序问题一分为二,分别进行处理
3. MERGE-SORT (A, p, q) //继续递归看能不能将问题继续一分为二,处理A[p->q]的排序
4. MERGE-SORT (A, q + 1, r) // 继续递归看能不能将问题继续一分为二处理A[q+1->r]的排序
5. MERGE (A, p, q, r) // 合并当前结果
到这里,分治算法的精髓已经出来了,我们通过递归将问题进行分解到足够小…继而进行结果计算…然后再将结果合并.
下面来处理一下边角料的工作,呵呵,让大家看到一个完整的归并排序的例子,整个算法总结系列我都没有很好的使用伪代码,而是使用我认为广泛使用的C语言代码来进行代码诠释.实际上,描述算法最好还是使用伪代码比较好,这里我对我前面的四篇文章没有使用伪代码而小小的鄙视一下自己,太不专业了..呵呵
以下算法MERGE (A, p, q, r )表示合并A[p->q]和A[q+1->r]这两个已经排序好的数组
MERGE (A, p, q, r )
1. n1 ← q − p + 1 //计算A[p->q]的长度
2. n2 ← r − q //计算A[q+1->r]的长度
3. Create arrays L[1 . . n1 + 1] and R[1 . . n2 + 1] //创建两个数组
4. FOR i ← 1 TO n1
5. DO L[i] ← A[p + i − 1]
6. FOR j ← 1 TO n2
7. DO R[j] ← A[q + j ] //4-7行是将原数组中A[p->r]的元素取出到新创建的数组,我们的操作是基于临时数组的操作
8. L[n1 + 1] ← ∞
9. R[n2 + 1] ← ∞ //8-9行设置界限..
10. i ← 1
11. j ← 1
12. FOR k ← p TO r
13. DO IF L[i ] ≤ R[ j]
14. THEN A[k] ← L[i]
15. i ← i + 1
16. ELSE A[k] ← R[j]
17. j ← j + 1 //12-17行进行排序合
这里我还是提供一个具体的实现,请见下面的代码
C语言代码
关于代码注释,请见博客上面的伪代码注释..
#include<stdio.h>
int L[100],R[100];
void merge(int numbers[],int left, int mid, int right)
{
int n1=mid-left+1;
int n2=right-mid;
int i,j,k;
for(i=1;i<=n1;i++)
L[i]=numbers[left+i-1];
for( j=1;j<=n2;j++)
R[j]=numbers[mid+j];
L[n1+1]=99999;
R[n2+1]=99999;
i=1;
j=1;
for(k=left;k<=right;k++)
if(L[i]<=R[j])
{
numbers[k]=L[i];
i++;
}
else
{
numbers[k]=R[j];
j++;
}
}
void mergeSort(int numbers[],int left, int right)
{
if(left<right)
{
int mid;
mid = (right + left) / 2;
mergeSort(numbers, left, mid);
mergeSort(numbers, mid+1, right);
merge(numbers,left, mid, right);
}
}
int main()
{
int numbers[]={5,2,4,6,1,3,2,6};
mergeSort(numbers,0,7);
for(int i=0;i<8;i++)
printf("%d",numbers[i]);
}
归并排序算法的时间复杂度是O(nlogn),对于冒泡排序的O(n*n),效率还有有比较好的提高..
其实本人原来在学习的时候好长一段时间不理解为什么时间复杂度会是O(nlogn),像冒泡排序就比较好理解,有两个for循环,问题的规模随着n变大而变大,算法时间复杂度自然就是O(n*n),后面花了一些时间来阅读一些资料才明白其原理,这里我已经将资料地址放到了本文最后,有兴趣的也可以去看看.简单的描述一下为什么会是O(nlogn)
大家看看,我们的例子,解一个8个元素的数组,我们用到了几层?是四层,假设我们这里有n个元素,我们会用到多少层?根据一定的归纳总结,我们知道我们会用到(lgn)+1层..(lgn)+1层需要用到lgn层次的合并算法.现在再看看MERGE (A, p, q, r )的复杂度是多少,毫无疑问O(n),故其归并排序的算法时间复杂度是O(nlogn).当然这个结果还可以通过其他的方法计算出来,我这里是口语话最简洁的一种..
下面来一张算法时间复杂度的与n规模的关系图..
上一篇:Linux下用Valgrind做检查(防止内存泄露)
栏 目:C语言
下一篇:关闭显示器软件代码分享
本文标题:算法详解之分治法具体实现
本文地址:https://www.xiuzhanwang.com/a1/Cyuyan/3803.html
您可能感兴趣的文章
- 01-10求子数组最大和的解决方法详解
- 01-10深入二叉树两个结点的最低共同父结点的详解
- 01-10数据结构课程设计- 解析最少换车次数的问题详解
- 01-10数据结构课程设计-用栈实现表达式求值的方法详解
- 01-10HDOJ 1443 约瑟夫环的最新应用分析详解
- 01-10使用C++实现全排列算法的方法详解
- 01-10如何查看进程实际的内存占用情况详解
- 01-10深入Main函数中的参数argc,argv的使用详解
- 01-10APUE笔记之:进程环境详解
- 01-10深入第K大数问题以及算法概要的详解
阅读排行
本栏相关
- 04-02c语言函数调用后清空内存 c语言调用
- 04-02func函数+在C语言 func函数在c语言中
- 04-02c语言的正则匹配函数 c语言正则表达
- 04-02c语言用函数写分段 用c语言表示分段
- 04-02c语言中对数函数的表达式 c语言中对
- 04-02c语言编写函数冒泡排序 c语言冒泡排
- 04-02c语言没有round函数 round c语言
- 04-02c语言分段函数怎么求 用c语言求分段
- 04-02C语言中怎么打出三角函数 c语言中怎
- 04-02c语言调用函数求fibo C语言调用函数求
随机阅读
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 01-10delphi制作wav文件的方法
- 01-10C#中split用法实例总结
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 01-10SublimeText编译C开发环境设置
- 04-02jquery与jsp,用jquery
- 01-11ajax实现页面的局部加载