分治算法——求逆序对、n个点中求最近的两个点的距离、快速求解两个矩阵的乘积

分治算法:分而治之。也就是将原问题划分为n个规模较小的子问题,并且结构与原问题相似,递归地解决这些子问题,然后再合并其结果,就得到原问题的解。

关于递归是否可以换成循环处理,我在处理实际问题的时候,试过,可行。所以还得要具体问题具体分析。

一、求逆序对

#include<iostream>
using namespace std;

static int num = 0;		// 全局变量,记录逆序对数
void merge(int *a, int p, int q, int r);
void mergeSortCounting(int *a, int p, int r);

int count(int *a, int n)
{
	num = 0;
	mergeSortCounting(a, 0, n - 1);		// 通过归并,计算继续对数
	return num;
}
// inparam: 数组,起始,终止
void mergeSortCounting(int *a, int p, int r)
{
	if(p >= r)
		return;
	int q = (p + r) / 2;
	mergeSortCounting(a, p, q);
	mergeSortCounting(a, q + 1, r);
	merge(a, p, q, r);
}
// 合并
void merge(int *a, int p, int q, int r)
{
	int i = p, j = q + 1, k = 0;
	int *temp = new int[r-p+1];	// a的所有的数
	while(i <= q && j <= r)		// i j小于自己的终止范围
	{
		if(a[i] <= a[j])	// 正序
			temp[k++] = a[i++];
		else
		{
			num += (q - i + 1);	// 逆序
			temp[k++] = a[j++];
		}
	}
	while(i <= q)		// 前半段剩余,直接赋值
	{
		temp[k++] = a[i++];
	}
	while(j <= r)		// 后半段剩余,直接赋值
	{
		temp[k++] = a[j++];
	}
	for(i = 0; i <= r-p; ++i)	// 对于temp里面排序好的数,直接复制回到a数组中
	{
		a[p + i] = temp[i];
	}
}

void print(int *a, int len)
{
	for(int i = 0; i < len; ++i)
        {
                cout << a[i] << ", ";
        }
	cout << endl;
}
// 此函数只求解,不排序。时间复杂度为n^2
int loop(int *a, int len)
{
	int n = 0;
	for(int i = 0 ; i < len - 1; ++i)
	{
		for(int j = i; j < len - 1; ++j)
		{
			if(a[i] > a[j + 1])
			{
				++n;
			}
		}	
	}
	return n;
}

int main()
{
	int arr[9] = {4,6,2,7,1,8,5,9,3};
	cout << "数据如下,求其逆序对数:" << endl;
	print(arr, 9);
	cout << "采用分治思想,调用归并算法,求的逆序对数为: " << endl;
	count(arr, 9);
	cout << num << endl;
	cout << "排序之后:" << endl;
	print(arr, 9);
	cout << endl;
	int brr[9] = {4,6,2,7,1,8,5,9,3};
	cout << "采用循环,求的逆序对数为: " << endl;
	cout << loop(brr, 9) << endl;
	return 0;
}

二、二维平面上有n个点,快速计算出两个距离最近的点对

用循环来做,时间复杂度为O(n^2),采用分治,降低复杂度。

#include<iostream>
#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;

const double INF = 1e20;
const int N = 65535;	// 用于申请数组长度为N

struct Point
{
	int x;
	int y;
}point[N];

int n;
int tmpt[N];	// 用于保存距离 mid 为d的长度中的点

// 按照x坐标从小到大排序Point数组
bool cmpxy(const Point &a, const Point &b)
{
	if(a.x != b.x)
		return a.x < b.x;
	return a.y < b.y;
}

// 按照y坐标从小到大排序Point数组
bool cmpy(const int &a, const int &b)
{
	return point[a].y < point[b].y;
}

// 找到小值输出
double min(double a, double b)
{
	return a < b ? a : b;
}

// 求下标为 i和 j的两点的Point,的距离
double dis(int i, int j)
{
	return sqrt((point[i].x - point[j].x) * (point[i].x - point[j].x)
		+ (point[i].y - point[j].y) * (point[i].y - point[j].y));
}

//最近点对距离,递归求解
double Closest_Pair(int left, int right)
{
	double d = INF;
	if(left == right)	// 最后剩下一个点
		return d;
	if(left + 1 == right)	// 最后剩下两个点
		return dis(left, right);
	int mid = (left + right) >> 1;		// /2
	double d1 = Closest_Pair(left, mid);
	double d2 = Closest_Pair(mid + 1, right);
	d = min(d1, d2);
	int i, j, k = 0;
	for(i = left; i <= right; ++i)
	{
		if(abs(point[mid].x - point[i].x) <= d)
			tmpt[k++] = i;
	}
	sort(tmpt, tmpt + k, cmpy);
	for(i = 0; i < k; ++i)
	{
		for(j = i + 1; j < k && point[tmpt[j]].y - point[tmpt[i]].y < d; ++j)
		{
			double d3 = dis(tmpt[i], tmpt[j]);
			if(d > d3)
				d = d3;
		}
	}
	return d;
}

int main()
{
	cout << "请输入数组对数,并依次输入数组对 : " << endl;
	int n;
	while(1)
	{
		cin >> n;
		if(n == 0)
			break;
		for(int i = 0; i < n; i++)
		{
			cin >> point[i].x >> point[i].y;
		}
		sort(point, point + n, cmpxy);
		cout << "最近的距离为: "<< Closest_Pair(0, (n - 1)/2) << endl;
		cout << endl;
		cout << "如果继续,请输入需要的数组对数,并输入数组对。否则输入 0,退出程序。" << endl;
	}
	cout << "程序结束!" << endl;
	return 0;
}

三、有两个n*n的矩阵 A B,快速求解两个矩阵的乘积 C = A*B

先呈献一般方法的代码,时间复杂度为O(n^3);

#include<iostream>
#include<stdio.h>
#include<cstdlib>

using namespace std;

// 计算二维矩阵的乘积,时间复杂度O(n^3)
void matrix_product(int **matx1, int **matx2, int **matx3, int len)
{
	int i,j,k;
	for(i = 0; i < len; ++i)		// i 行
	{
		for(j = 0; j < len; ++j)	// j 列
		{
			matx3[i][j] = 0;
			for(k = 0; k < len; ++k)	// k 列
			{
				matx3[i][j] += matx1[i][k] * matx2[k][j];
			}
		}
	}
}

// 打印输出二维数组
void print(int **matx, int len)
{
	for(int i = 0; i < len; ++i)
	{
		for(int j = 0; j < len; ++j)
		{
			cout << matx[i][j] << "  ";
		}
		cout << endl;
	}
	cout << endl;
}

// 初始化数组
void init(int **matx, int len)
{
	for(int i = 0; i < len; ++i)
        {
                for(int j = 0; j < len; ++j)
                {
                        matx[i][j] = (rand()%30 + 1);
                }
        }
}

// 删除二维矩阵
void Delete(int **matx, int len)
{
	for(int i = 0; i < len; ++i)
        {
                delete [] matx[i];
        }
}

int main()
{
	const int m = 6;
	
	int **matrix1 = new int* [m];
	int **matrix2 = new int* [m];
	int **matrix3 = new int* [m];
	for(int i = 0; i < m; ++i)
	{
		matrix1[i] = new int[m]();
		matrix2[i] = new int[m]();
		matrix3[i] = new int[m]();
	}
	
	init(matrix1, m);
	init(matrix2, m);
	
	cout << "矩阵1:" << endl;
	print(matrix1, m);
	cout << "矩阵2:" << endl;
	print(matrix2, m);
	
	matrix_product(matrix1, matrix2, matrix3, m);
	cout << "矩阵乘积结果: " << endl;
	print(matrix3, m);

	
	Delete(matrix1, m);
	Delete(matrix2, m);
	Delete(matrix3, m);
	return 0;
}

分治算法求解的主要思路: 分块,相乘&&相加,拼接。

虽然没有写分治进行分两个矩阵的乘积的程序,但是细数一下,其实分块之后,时间复杂度并没有降低,而且由于其中的分块合块,时间复杂度反而提高了。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值