分治算法:分而治之。也就是将原问题划分为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;
}
分治算法求解的主要思路: 分块,相乘&&相加,拼接。
虽然没有写分治进行分两个矩阵的乘积的程序,但是细数一下,其实分块之后,时间复杂度并没有降低,而且由于其中的分块合块,时间复杂度反而提高了。