某天得知寒假还有编程作业,便很无奈地写着第一套题,发现分治算法,这种基础算法,初一初二学的,现在完全不记得了23333
于是嘛,就又重新学了一下分治以及归并排序。
一、啥是分治
分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
(以上来自百度百科quq)
二、适用条件
采用分治法解决的问题一般具有的特征如下:
- 问题的规模缩小到一定的规模就可以较容易地解决;
- 问题可以分解为若干个规模较小的模式相同的子问题,即该问题具有最优子结构性质;
- 合并问题分解出的子问题的解可以得到问题的解;
- 问题所分解出的各个子问题之间是独立的,即子问题之间不存在公共的子问题。
(还是来自百度百科,因为觉得说的很有道理就直接复制了)
三、从归并排序入手
假设我们要排序这样一个序列:
14,13,52,26,12,2,44,42
首先是要利用分治将这列数的排序变成小数字的排序(从上往下):
PS:主要是二分法,这个最常用。
然后是把每个小问题排序,合并成最终答案(从下往上):
(是不是很像一颗树?!)
当把子问题合并时,我们只需要用一个tmp数组存顺序,最后复制回原数组即可(以最后一步为例):
我们通过俩指针i和j比较从front到mid和mid+1到end的大小,依次放进tmp中,再复制回原数组即可(如下)。
具体见程序咯!
时间复杂度:O(nlogn)
C++ Code:
#include <bits/stdc++.h>
using namespace std;
int a[100];
void MergeSort(int* a,int f,int mid,int e){
int* tmp = new int[e-f+2];//利用tmp询问一个新的内存空间用来定义tmp数组
int i = f,j = mid+1,k = 1;//定义指针和tmp数组初始位置
/*比较并存入tmp过程*/
while (i <= mid && j <= e){
if (a[i] < a[j]) tmp[k++] = a[i++];
else tmp[k++] = a[j++];
}
while (i <= mid) tmp[k++] = a[i++];
while (j <= e) tmp[k++] = a[j++];
/*复制过程*/
k = 1;
for (int i = f;i <= e;i++) a[i] = tmp[k++];
delete [] tmp;//养成好习惯,有new必有delete!
}
void Merge(int* a,int f,int e){
int mid = (f+e)/2;
if (f < e){
Merge(a,f,mid);
Merge(a,mid+1,e);
MergeSort(a,f,mid,e);
}
}
int main(){
int n;
cin >> n;
for (int i = 1;i <= n;i++)
cin >> a[i];
Merge(a,1,n);
for (int i = 1;i <= n;i++) cout << a[i] << " ";
cout << endl;
}
四、写在后面
分治貌似就是和二分差不多,写了分治求最近点对和分治求逆序数,大概懂了些皮毛了。。。
也祝大家新年快乐!!!!!!!!