【C语言】分治法的设计与实现

247 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

【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的阶乘

  1. 如果n等于1,则返回1,;
  2. 如果n不等于1,则返回n*f(n-1)的值;

3.1.2求某数列的最大值\最小值伪代码
输入:无序数列a[],所需判断序列区间的首位n1与末位n2
输出:数列a[]中的最大值与最小值

  1. 定义求最值的函数
    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)}
输出:最近点对的距离

  1. 如果n等于2,则返回(x1,y1)和(x2,y2)之间的距离,算法结束;
  2. 划分:m=S中各点x坐标的中位数;
  3. d1=计算{(x1,y1),…,(xm,ym)}的最近对距离;
  4. d2=计算{(xm,ym),…,(xn,yn)}的最近对距离;
  5. d=min{d1,d2};
  6. 依次考察集合S中的点p(x,y),如果(x<=xm并且x>=xm-d),则将点p放入集合P1中;如果(x>xm并且x<=xm+d),则将点p放入集合p2中;
  7. 将集合P1和P2按y坐标升序排列;
  8. 对集合P1和P2中的每个点p(x,y),在y坐标区间[y,y+d]内最对取出8个候选点,计算与点p的最近距离d3;
  9. 返回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;

intgetmaxmin(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]存放最大值 
intmaxmin(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, 07);
    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的阶乘运算结果

image.png

时间复杂度:O(n)

4.1.2.求某序列的最大值/最小值

image.png

时间复杂度:O(n)

4.1.3归并排序

image.png

时间复杂度:nlog2n

4.1.4快速排序

image.png

时间复杂度:nlog2n

4.1.5一维最近对问题

image.png

时间复杂度:O(n^2)

4.2分析

分治法的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。

五、实验小结

我们分析并解决了所有必做任务,及选做任务中的快速排序,并且对选做任务中的多个算法进行了简易的分析。同时,计算出了所做任务程序的时间复杂度。
正如古文所述,分治者,分而治之也。分治法将一个难以解决的大问题划分成为一个规模较小的子问题,分别求解各个子问题,再合并子问题的解。一般来说分治法求解过程为通过划分、求解子问题、合并三个阶段把各子问题的解合并起来,子问题的合并因情况不同会产生差异,分治算法的效率很大程度上依赖于合并的实现。
在实验过程中,由于对分治法的理解不够透彻,在一些算法程序的设计过程中对算法不够熟悉,未能快速的做出解析。于是便多次查阅书本,对书本所述算法进行进一步的学习理解,并通过互联网工具参透其他学者思想,对互联网中的一些算法程序进行复现等,进一步理解算法。
并且通过网络工具获取一些快速排序问题、0/1背包问题等问题的程序代码进行分析。将互联网中知识与课本相结合,希望通过此方式能够对分治法的求解方式进行的更深入的理解,希望能够更好的掌握该算法,以为后期其他算法的学习做基础准备。