接下来我谈谈我对分治算法的理解。
分治:就是把一个任务分为和原任务形式相同但规模更小的几个任务(通常是两个任务)分别完成,或只需要完成其中一部分。然后再处理这几部分的结果来实现整个任务的完成。其实它大部分最好和递归一起使用。
概念很好理解,但是关键还是老话,思想很重要。比如我们常见的称硬币问题,16枚硬币有一枚假的,假的比真的轻,有一个天平问最少称几次可以找到假币。当然我们可以用穷举的思想,两个两个的称,但是也可以用分治的思想,平均一分8个8个称,哪边轻那假币肯定就在那8个里,就变成了在8个硬币找假币,然后再一分……,这看起来有些像二分查找,本来很多算法都有相似之处,这是一个简单的例子,接下来看一个分治的经典问题“归并排序”。
归并排序是我们学过的排序算法里面第一个突破时间复杂度为O(n^2)的排序算法,像其他的冒泡排序,选择排序,直接排序等时间复杂度都是n的平方,而归并排序的时间复杂度为N*logN。
归并排序就是我们想把一个无序序列分为两部分,分别吧这两部分排好序然后归并到一起成为一个有序序列,这听起来好像没有改善多少,但是我们分为的两部分可以递归继续分直到只有一个元素,这样就可以大大降低时间复杂度。直接上代码:
#include<iostream>
using namespace std;
int a[100];
int b[100];
void Merge(int a[], int s, int m, int e, int tmp[])
{
int pb = 0;
int p1 = s;
int p2 = m+1;
while ((p1 <= m) && (p2 <= e))
{
if (a[p1] > a[p2])
tmp[pb++] = a[p2++];
else
tmp[pb++] = a[p1++];
}
while (p1 <= m)
{
tmp[pb++] = a[p1++];
}
while (p2 <= e)
{
tmp[pb++] = a[p2++];
}
for (int i = 0; i < e - s + 1; i++)
{
a[s+i] = tmp[i];
}
}
void MergeSort(int a[], int s, int e, int tmp[])
{
if(s < e)
{
int m = s + (e - s) / 2;
MergeSort(a, s, m, tmp);
MergeSort(a, m + 1, e, tmp);
Merge(a, s, m, e, tmp);
}
}
int main()
{
int N;
cin >> N;
for(int i = 0;i<N;i++)
cin >> a[i];
MergeSort(a,0,N-1,b);
for (int i = 0; i < N; i++)
{
cout << a[i]<<" ";
}
return 0;
}
至于为什么时间复杂度为NlogN,看下图:
还有就是快速排序也用了分治的思想。
快速排序就是在一个待排序的数组中,使右半部分的所有数都大于左边的所有数(也不一定一定是对半分的),我们一般都是以首元素为分界线,比首元素大的都在右边,比首元素小的都在左边,然后再用递归继续对两边的进行相同操作,最后只剩一个元素时就已排好序。具体的怎么进行分类呢?我们可以这样:
在数组中选择一个基准点(一般为首元素)
分别从数组的两端扫描数组,设两个指示标志
从后半部分开始,如果发现有元素比该基准点的值小,就交换位置
然后从前半部分开始扫描,发现有元素大于基准点的值,继续交换位置
如此往复循环,然后把基准点的值放到和end位置,排序完成
以后采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组就自然有序了。
同理这个排序的时间复杂度和归并排序一样,都是NlogN。所以归并的思想还是很重要的。
上代码:
#include<iostream>
using namespace std;
int a[100];
void Swap(int& a, int& b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
}
void QuickSort(int a[], int s, int e)
{
if (s >= e) return;
int k = a[s];
int i = s, j = e;
while (i != j)
{
while ((i < j) && (k <= a[j]))
j--;
Swap(a[i], a[j]);
while ((i < j) && (k >= a[i]))
i++;
Swap(a[i], a[j]);
}
QuickSort(a, s, i - 1);
QuickSort(a,i + 1,e);
}
int main()
{
int N;
cin >> N;
for (int i = 0; i < N; i++)
cin >> a[i];
QuickSort(a, 0, N - 1);
for (int i = 0; i < N; i++)
cout << a[i]<<" ";
return 0;
}
像C++中STL中自带的sort()排序函数内部也是用的快速排序,而且这个排序是很多学校和公司都比较爱考的。
这就是我对分治的一些小小的理解,有大佬有更好的见解也可以一起讨论啊!!!