本文已参与「新人创作礼」活动,一起开启掘金创作之路。
【C语言】分治法的设计与实现
一、目的
掌握分治法的设计思想、具体实现和时间复杂度分析。
二、实验内容
先用伪代码或流程图描述利用分治法解决的算法解决方案,再用程序实现,计算时间复杂度,记录最终测试数据和测试结果。
2.1实验内容:
2.1.1 求a的n次方或者求n个自然数的和或者计算n的阶乘
2.1.2 求某数列的最大值\最小值
2.1.3 归并排序
2.1.4一维最近对问题
2.2.5 快速排序
三、设计和编码
3.1算法设计
3.1.1计算n的阶乘伪代码
输入:整数n
输出:n的阶乘
- 如果n等于1,则返回1,;
- 如果n不等于1,则返回n*f(n-1)的值;
3.1.2求某数列的最大值\最小值伪代码
输入:无序数列a[],所需判断序列区间的首位n1与末位n2
输出:数列a[]中的最大值与最小值
- 定义求最值的函数
1.1如果n2-n1+1等于1,则取指针m为int[2],返回getmaxmin(a[n2], a[n2])
1.2如果n2-n1+1等于2,则返回return getmaxmin(a[n1], a[n2])
1.3其他情况下
1.3.1定义数组lm与rm
1.3.2如果lm[0] < rm[0],lm[0]为最小值
1.3.3如果lm[1] < rm[1],rm[1]为最大值
3.1.3归并排序伪代码
Begin
1.1int main()
{
1.2 int a[];
1.3 MergeSort()处理a;
1.4 putnum()处理a;
}
2.1 void Merge(int r[],int r1[],int s,int m,int t)
{
2.2 int i=s,j=m+1,k=s;
2.3 while (i<=m&&j<=t)
{
2.4 如果(r[i]<=r[j]) r1[k++]=r[i++];
2.5 则 r1[k++]=r[j++];
}
2.6 while (i<=m)
r1[k++]=r[i++];
2.7 while (j<=t)
r1[k++]=r[j++];
}
3.1 void MergeSort(int r[],int s,int t)
{
3.2 int m,r1[1000];
3.3 如果(s==t)return;
3.4 则
{
m=(s+t)/2;
MergeSort(r,s,m);
MergeSort(r,m+1,t);
Merge(r,r1,s,m,t);
for(int i=s;i<=t;i++)
r[i]=r1[i];
}
}
4.1 void putnum(int r[],int n)
{
4.2 int i;
4.3 for(i=0;i<n;i++)
4.4 输出r[];
}
End
3.1.4一维最近对问题伪代码
输入:按x坐标升序排列的n(n>=2)个点的集合S={(x1,y1),(x2,y2),…,(xn,yn)}
输出:最近点对的距离
- 如果n等于2,则返回(x1,y1)和(x2,y2)之间的距离,算法结束;
- 划分:m=S中各点x坐标的中位数;
- d1=计算{(x1,y1),…,(xm,ym)}的最近对距离;
- d2=计算{(xm,ym),…,(xn,yn)}的最近对距离;
- d=min{d1,d2};
- 依次考察集合S中的点p(x,y),如果(x<=xm并且x>=xm-d),则将点p放入集合P1中;如果(x>xm并且x<=xm+d),则将点p放入集合p2中;
- 将集合P1和P2按y坐标升序排列;
- 对集合P1和P2中的每个点p(x,y),在y坐标区间[y,y+d]内最对取出8个候选点,计算与点p的最近距离d3;
- 返回min{d,d3};
3.1.5快速排序
先找一个基准元素(比如待排序数组的第一个元素),进行一趟快速排序,使得该基准元素左边的所有数据都它小,而右边的所有数据都它大,然后再按此方法,对左右两边的数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数组变成有序序列。
3.2程序代码
3.2.1.计算n的阶乘
#include<stdio.h>
int f(int n)
{
if(n==1)
return 1;
return n*f(n-1);
}
int main()
{
int n;
printf("请输入一个数:\n");
scanf("%d",&n);
printf("这个数的阶乘为:%d\n",f(n));
return 0;
}
3.2.2.求某序列的最大值/最小值
#include<cstdio>
#include<cstdlib>
#include<cmath>
using namespace std;
int* getmaxmin(int a, int b){
int* m = new int[2];
if(a < b){
m[0] = a;
m[1] = b;
}else{
m[0] = b;
m[1] = a;
}
return m;
}
//m[0]存放最小值,m[1]存放最大值
int* maxmin(int a[], int n1, int n2){
if((n2 - n1 + 1)== 1){
int* m = new int[2];
m[0] = -1;
m[1] = a[n2];
return getmaxmin(a[n2], a[n2]);
}else if((n2 - n1 + 1) == 2){
return getmaxmin(a[n1], a[n2]);
}else{
int k = n1 + (n2 - n1) / 2;
int* lm = maxmin(a, n1, k);
int* rm = maxmin(a, k+1, n2);
int* result = new int[2];
if(lm[0] < rm[0]){
result[0] = lm[0];
}else{
result[0] = rm[0];
}
if(lm[1] < rm[1]){
result[1] = rm[1];
}else{
result[1] = lm[1];
}
return result;
}
}
int main(){
printf("输入数组:6 10 32 8 19 20 2 14\n");
int a[8] = {6,10,32,8,19,20,2,14};
int* result = maxmin(a, 0, 7);
printf("最大值为:%d,最小值为:%d", result[1], result[0]);
}
3.2.3.归并排序
# include<stdio.h>
void Merge(int r[],int r1[],int s,int m,int t);
void MergeSort(int r[],int s,int t);
void putnum(int r[],int n);
int main()
{
int a[5]={4,8,2,5,3};
printf("输入序列:4 8 2 5 3\n");
MergeSort(a,0,4);
putnum(a,5);
}
void Merge(int r[],int r1[],int s,int m,int t)
{
int i=s,j=m+1,k=s;
while (i<=m&&j<=t)
{
if(r[i]<=r[j]) r1[k++]=r[i++];
else r1[k++]=r[j++];
}
while (i<=m)
r1[k++]=r[i++];
while (j<=t)
r1[k++]=r[j++];
}
void MergeSort(int r[],int s,int t)
{
int m,r1[1000];
if(s==t)return;
else
{
m=(s+t)/2;
MergeSort(r,s,m);
MergeSort(r,m+1,t);
Merge(r,r1,s,m,t);
for(int i=s;i<=t;i++)
r[i]=r1[i];
}
}
void putnum(int r[],int n)
{
int i;
printf("输出序列:");
for(i=0;i<n;i++)
printf("%d ",r[i]);
}
3.2.4.一维最近对问题
#include <bits/stdc++.h>
using namespace std;
struct point{
int x,y;
};
double Closest(point S[],int low,int high);
double Distance(point a,point b);
int main() {
int n;
cout<<"一维坐标点数:";
cin>>n;
double *p = new double[n];
cout<<"一维坐标点具体值:";
for(int i = 0; i < n; i++) {
cin>>p[i];
}
sort(p, p + n);
cout <<"最近值为 = "<<nearest1(p, p[0], p[n - 1], n) << endl;
delete [] p;
return 0;
}
double Closest(point S[],int low,int high)
{
double d1,d2,d3,d;
int mid,i,j,index;
point p[20];
if(high-low==1)
return Distance(S[low],S[high]);
if(high-low==2) {
d1=Distance(S[low],S[low+1]);
d2=Distance(S[low+1],S[high]);
d3=Distance(S[low],S[high]);
if((d1<d2)&&(d1<d3))
return d1;
else if (d2<d3) return d2;
else return d3;
}
mid=(low+high)/2;
d1=Closest(S,low,mid);
d2=Closest(S,mid+1,high);
if(d1<=d2) d=d1;
else d=d2;
index=0;
for(i=mid;(i>=low)&&(S[mid].x-S[i].x<d);i--)
p[index++]=S[i];
for(i=mid+1;(i<=high)&&(S[i].x-S[mid].x<d);i--)
p[index++]=S[i];
sort(p,p+index-1);
for(i=0;i<index;j++){
for(j=i+1;j<index;j++){
if(p[j].y-p[i].y>=d)
break;
else{
d3=Distance(p[i],p[j]);
if(d3<d) d=d3;
}
}
}
return d;
}
double Distance(point a,point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
3.2.5 快速排序
# include<stdio.h>
int Partition(int r[],int first,int end);
void QuickSort(int r[],int first,int end);
void putnum(int r[],int n);
int main()
{
int a[5]={5,4,8,7,3};
printf("输入序列:5 4 8 7 3\n");
Partition(a,0,4);
QuickSort(a,0,4);
putnum(a,5);
}
int Partition(int r[],int first,int end)
{
int i=first,j=end;
while(i<j)
{
while(i<j&&r[i]<=r[j]) j--;
if(i<j)
{
int temp=r[i];r[i]=r[j];r[j]=temp;
i++;
}
while (i<j&&r[i]<=r[j]) i++;
if(i<j)
{
int temp=r[i];r[i]=r[j];r[j]=temp;
j--;
}
}
return i;
}
void QuickSort(int r[],int first,int end)
{
int pivot;
if(first<end)
{
pivot=Partition(r,first,end);
QuickSort(r,first,pivot-1);
QuickSort(r,pivot+1,end);
}
}
void putnum(int r[],int n)
{
int i;
printf("输出序列:");
for(i=0;i<n;i++)
printf("%d ",r[i]);
}
四、运行结果及分析
4.1运行结果
4.1.1计算n的阶乘运算结果
时间复杂度:O(n)
4.1.2.求某序列的最大值/最小值
时间复杂度:O(n)
4.1.3归并排序
时间复杂度:nlog2n
4.1.4快速排序
时间复杂度:nlog2n
4.1.5一维最近对问题
时间复杂度:O(n^2)
4.2分析
分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
五、实验小结
我们分析并解决了所有必做任务,及选做任务中的快速排序,并且对选做任务中的多个算法进行了简易的分析。同时,计算出了所做任务程序的时间复杂度。
正如古文所述,分治者,分而治之也。分治法将一个难以解决的大问题划分成为一个规模较小的子问题,分别求解各个子问题,再合并子问题的解。一般来说分治法求解过程为通过划分、求解子问题、合并三个阶段把各子问题的解合并起来,子问题的合并因情况不同会产生差异,分治算法的效率很大程度上依赖于合并的实现。
在实验过程中,由于对分治法的理解不够透彻,在一些算法程序的设计过程中对算法不够熟悉,未能快速的做出解析。于是便多次查阅书本,对书本所述算法进行进一步的学习理解,并通过互联网工具参透其他学者思想,对互联网中的一些算法程序进行复现等,进一步理解算法。
并且通过网络工具获取一些快速排序问题、0/1背包问题等问题的程序代码进行分析。将互联网中知识与课本相结合,希望通过此方式能够对分治法的求解方式进行的更深入的理解,希望能够更好的掌握该算法,以为后期其他算法的学习做基础准备。