递归,首先它的目的是把问题缩小为同类问题的子问题,通过不断地递归调用自身,最终到达某次调用能结束返回。
如果f()是一个递归函数,调用是这样的:
条件:递归到一定程度必须可以终止,不能无限地递归,换句话说,就是递归函数一定是可以结束的。
分治,对于一个规模为N的问题,若该问题可以容易解决,则直接解决,否则将其分解为M个规模较小的子问题,这些问题相互独立(这点很重要),并且和原问题形式相同,递归解决这些子问题,然后各子问题的解得到原问题的解。其运用的手段是递归思想。
条件:可以分解成几个相互独立的子问题。
几种运用递归和分治思想的算法:
1.快排
思路:选取一个参照数(一般为a[0]),将数组中比该参照数大的丢到参照数右边,比参照数小的丢到参照数左边,然后将参照数左边和右边分别重复(这里用到分治和递归),
void QuickSort(int a[],int left,int right)
{
分割数组;
QuickSort(a,left,参照数);
QuickSort(a,参照数数右侧,right);
}
这个算法重点是分割数组,刚才谈到分割数组的方法“选取一个参照数(一般为a[0]),将数组中比该参照数大的丢到参照数右边,比参照数小的丢到参照数左边”,重点要理解这个“丢”,按照模拟现实的情景,第一个作为参照,遍历后面的数,遇到比其小的,丢到参照数左,剩下的就是比参照数大的了,但仔细想想,这里的“丢”,只能将参照数和遍历到的数交换,这一交换问题就来了,以前遍历过的比其大的数说不定又跑到参照数左边了。
到这里,我们梳理一下我们的目标:把一半数都换到右边来,把一半数都换到左边去,且左边数都小于右边数。
那要保证一点,当遍历到每个数时,该在右边的,本在右边则不换,不在右边必须要换到右边来(要求参照数得在右边),该在左边的,本在左边则不换,不在左边必须要换到左边来(参照数得在左边)。
最后思路:参照数在左边—-》遍历右边比参照数小的数,遍历到了换到右边来
参照数在右边—-》遍历左边比参照数大的数,遍历到了换到左边来
int Division(int a[],int left,int right)
{
int base=a[left];
int l=left,r=right;
if(l<r) //递归结束条件
{
while(left<right) //左右交替
{
while(left<right && a[left]<base)
left++;
a[right]=a[left]; //遍历左边,遇到比参照数大的了,交换后,参照数移到左边来
a[left]=base;
while(left<right && a[right]>base)
right–;
a[left]=a[right]; //遍历左边,遇到比参照数大的了,交换后,参照数移到右边来
a[right]=base;
}
Division(a,l,right-1); //左分治
Division(a,left+1,r); //右分治
}
}
缺点:输入量大时,递归栈大量开销