递归

如何根据递归表达式求递归的时间复杂度以及分治法的简单应用

一.主要内容

通过分析归并排序,快速排序,插入排序以及一些简单递归算法的时间复杂度,介绍一些求递归表达式时间复杂度的主要方法和分治法的简单应用。

二.常见排序函数时间复杂度分析

1.归并排序的时间复杂度分析(递归树法)

我们可以把N个数据分成个数基本相同的两部分,即把N个数据的排序分成两个小的问题,每个小问题中有N/2个数据。现在,我们来分析下,将两个小问题解决的前提下,再把N个数据排序的时间复杂度:
7 14
5 12
3 4
1 2
上面两列数据,是已经分别排好序的两个大小为N/2的数组,用i,j分别表示数组的下标

int a[n];
int k=0;
int i=0,j=n/2+1;
while(i<=n/2&&j<=n)
{
   if(a[i]<=a[j])
   {
     a[k]=a[i];	//对于上面两列数据来说,就是先对1,2两个数进行比较,然后将1存入a[0]中,并且指针指向3,下一次进行3,2之间的比较,再把2存入,指针指向4
     i++;k++;
   }
   if(a[i]>a[j]{
     a[k]=a[j];
     j++;k++:
   }
}

当一列数据已经全部存入,比如上例中当7存入数组a[n]时 ,第二列数据中12,14两数还未储存,很明显直接将此列剩余的数全部存入a[n]即可,此时得到的一定是完全排好的序列。

不难看出,上面对于两列已经排好的数据的再排序,时间复杂度为O(n),在排序过程中只是遍历了n个数据。
所以,我们可以得到递归表达式:
**T(n)=2T(n/2)+O(n)
画出递归树:
在这里插入图片描述
很明显每一行相加都为O(n),那么只要求出树的深度,我们就可以得到归并排序的时间复杂度,这也是非常简单的,h=log₂n
所以,归并排序的时间复杂度为O(nlgn)级别
**在求时间复杂度时,只要得到为lg级别即可,因为不同底数直接对于相同的n,相差的倍数为常数倍,类似于n与3n这种差别,所以可以不精确的,或者不写底数的写为logn
**
2.快速排序的时间复杂度分析(递归树法)
对于快排的时间复杂度分析,要比归并排序复杂很多,原因在于快排每次分成的两个子问题,规模并不一定一样,下面我们写出快排的代码,并对几种情况进行简单分析,至于平均情况数学期望的分析,可以在我文章下面的视频链接里找。

int partition(int R[],int s,int t) //s,t是数组的下界和上界
{
  int i=s;
  int j=t;
  int tmp = R[i];
  while(i<j). //当i>j时说明一次划分已经完成
  {
  	while(j>j&&R[j]>=tmp)  //R[j]是R[i]后面的数,如果比R[i]大说明顺序正确,j--
  	 j--;
  	 R[i]=R[j]; //第一个循环结束说明R[j]更小,此时应该调换顺序,
  	while(i<j&&R[i]<tmp)
  	i++;
  	R[j]=R[i];
  }
  R[i]=tmp;
  return i;
}

void QuickSort(int R[],int s,int t)
{
   int i;
   if(s<t)
   {
     i=Partition(R,s,t);
     QuickSort(R,i+1,t);
     QuickSort(R,s,i-1);
   }
}

假设每次排序,都正好能划分成两个规模为n/2的子问题:
T(n)=2T(n/2)+O(n)
函数partition 明显只将数组遍历了一遍,所以非递归部分时间复杂度为O(n),画出递归树,我们可以得到在理想状态下时间复杂度为O(nlog₂n)
假设每次排序都是最差的情况,即问题被划分成1,n-1两个规模:
T(n)=T(1)+T(n-1)+O(n)
画出递归树,可以得到在最差情况下时间复杂度为O(n2),n的平方。
假设划分后,分成n/10和9n/10两个部分:
T(n)=T(n/10)+T(9n/10)+O(n)
画出递归树:
在这里插入图片描述
很明显9n/10规模的问题深度更大,不过我们可以不精确的将时间复杂度写为
O(nlog9/10n),很明显这个时间复杂度比实际的上界还要大一些,但仍在nlogn的范围内,由此,我们可以猜测,快速排序的平均情况应该也为O(nlgn),但仍需要证明,这里涉及到一些概率论知识以及指标随机变量,我也不懂,就不写了。
3.主方法**
T(n)=aT(n/b)+f(n)**
对于形如上面这个表达式的问题,我们可以使用主方法,其中f(n)是递归函数中非递归部分的时间复杂度,需要根据实际情况写出,下面我会给出例子。
主方法的三种情况:
在这里插入图片描述

(1)用主方法分析归并排序的时间复杂度
T(n)=2T(n/2)+O(n)

4.代换法
使用代换法可以解决大部分问题,而且更为精确,相比于代换法,主方法的局限性较大,这是代换法的优点,但代换法有一个缺点,就是必须先猜出来你要求的递归式的时间复杂度,然后才能进行证明,下面我举一个例子
Ex:T(n)=4T(n/2)+n
比较正确的猜测是,当规模变为n/2时,系数变为了4,所以应该是O(n^2),但为了更符合正常的猜测情况,也就是从错误或更大的范围开始,我们先验证它是否符合O(n ^3):
在这里插入图片描述

三.分治法与主定理,递归树的简单举例

1.分治法
请你设计一个递归算法,求x^n,要求时间复杂度为O(logn)

int f(int x,int n)
{
    if(n==1)
    {return x;}
    else if (n%2==0)
    {
        int y = f(x,n/2);
        return y*y;
    }
    else
    {
        int m=f(x,(n-1)/2);
        return m*m*x;
    }
}```


这里我们需要进行简单的分类讨论,并且用变量y,m来暂时储存f(x,n/2)的结果,如果我们不这样做的话

```cpp
return f(x,n/2)*f(x,n/2);

这两个函数会分别进行,那么递归式为:
T(n)=2T(n/2)+O(1)
时间复杂度为O(n),这样就不能得到想要的效果。

2.递归树的简单应用
T(n)=T(n/4)+T(n/2)+O(n^2)

在这里插入图片描述
很明显,用这种方法得到的结果不够精确,而且存在隐患,比如在没有证明的情况下默认数列为等比数列,所以可以用代换法再进行验证,递归树法应该也是一个在进行代换法前的猜测过程。

T(n)=4T(n/2)+n^2/lgn
因为k必须满足大于等于0,所以在这个例子中,我们没有办法使用主定理,解这个题还需要一定的对数知识:

3.主定理的简单应用
(1)T(n)=4T(n/2)+n
(2)T(n)=4T(n/2)+n^2

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值