递归与分治[数据结构与算法]

往期文章
1 绪论-数据结构的基本概念
2 绪论-算法
3 线性表-顺序表和链式表概念及其代码实现
4 查找-顺序+折半+索引+哈希
5 插入排序-希尔排序-选择排序-冒泡排序-快速排序-基数排序-外部排序-归并排序

1 递归的定义

  • 子程序(或函数)直接调用自己或通过一系列调用语句 间接调用自己,称为递归。
  • 递归是一种描述问题和解决问题的基本方法。

直接递归调用

void A()
{
    
    
 ...
	A();
 ...
}

间接调用递归

void B()
{
    
    
...
C();
...
}

void C()
{
    
    
 ...
	B();
 ...
}

2 Fibonacci数列递归求解和非递归求解

尾递归都能转换成非递归的写法
尾递归是指递归只是出现在return的时候

在这里插入图片描述
递归求解

int f(int n)
{
    
    
	if(n<=1)
		return n;//结束条件
	else
		return(f(n-1)+f(n-2));//递归方程
} 

非递归求解

int f(int n)
{
    
    
	int pre,now,next,j;
	if(n<=1)return(n);
	else
	{
    
    
		pre=0;now=1;
		for(j=2;j<=n;j++)
		{
    
    
			next=pre+now;
			pre=now;
			now=next
		}
		return(next);
  	}

}

3 回文串检测的递归求解和非递归求解

    如果一个字符串从左向右读和从右向左读完全相同(不区分大小写),则这个字符串称为回文串(palindrome),例如“noon”、“madam”等都是回文串。

递归求解

bool palindrome(string &s,unsigned h,unsigned t)
{
    
    
	if (h>=t) return 1;
	if(tolower(s[h])==tolower(s[t]))
		return palindrome(s,h+1,t-1);
	else
		return 0;
}

非递归求解

bool palindrome(string &s)
{
    
    
	int h=0,t=strlen(s)-1;
	while(h<=t)
	{
    
    
		if(s[h]!=s[t])return 0;
		h++;t--;
	}
	return 1;
}

4 分治递归

4.1 分治递归思想

  • 对这k个子问题分别求解。如果子问题的规模仍然不够 小,则再划分为k个子问题,如此递归的进行下去,直 到问题规模足够小,很容易求出其解为止。
  • 将求出的小规模的问题的解合并为一个更大规模的问题的解,自底向上逐步求出原来问题的解。

4.2 分治法的适用条件

  • 该问题的规模缩小到一定的程度就可以容易地解决;
  • 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质(前提
  • 利用该问题分解出的子问题的解可以合并为该问题的解;(如果具备前两条不具备第三条可以考虑使用贪婪算法和动态规划
  • 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。(这条特征设计分治法的效率

4.3 分治法的基本步骤

注意看注释

{
    
    
	if ( | P | <= n0) adhoc(P); //解决小规模的问题
	divide P into smaller subinstances P1,P2,...,Pk;//分解问题
		for (i=1,i<=k,i++)
			yi=divide-and-conquer(Pi); //递归的解各子问题
		return merge(y1,...,yk); //将各子问题的解合并为原问题的解
}

4.4 递归复杂度计算的三种方式求解

4.4.1 代换法

前提说明(忽略细节 如果后期发现细节很重要再加入进来)
T(n) 由两部分组成 一个是 每个小部分的计算时间之和 + 分解时间和解和并的时间f(n)

在这里插入图片描述
主要思想

  1. 猜测解的形式.
  2. 通过数学归纳法找出使解真正有 效的常数

在这里插入图片描述

在这里插入图片描述

4.4.2 迭代法-直接展开

在这里插入图片描述

4.4.3 主方法

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

5 实例应用

    假设有n个数据a[n],要把这n个数据的全排列都打印出来,考虑递归方法PrintPermutation。

  1. 第一个位置的元素可能是这n个元素的任意一个,可以采用swap,将第一个位置与其他位置的交换,就可以得到第一个位置元素不同的情况,而余下的递归调用打印全排列函数PrintPermutation完成。但要注意还应该交换回去,便于后面的正确处理。

  2. 递归函数内部,又会当前的第一个位置(第2个位置)的元素与余下n-2个位置元素交换;里面又会当前的第一个位置(第3个位置)的元素与余下n-3个位置元素交换;……;最后第n个位置的元素,就表示前面的n-1个位置已经处理了,就可以打印了。所以递归打印条件是位置k=n,这个也是递归出口

a[] 存放数据 k表示第一个交换的位置,一般为1,n便是数据的个数。

void PrintPermutation(int a[], int k, int n){
    
    
	if (k == n){
    
    
		for (int i = 1; i < n + 1; ++i){
    
    
			cout << a[i] << " ";
		}
		cout << endl;
		return;
	}
	else{
    
    
		for (int i = k; i < n+1; ++i){
    
    
			Swap(a[k], a[i]);
			PrintPermutation(a, k + 1, n);
			Swap(a[k], a[i]);
		}
	}
}


int main()
{
    
    
	int *a, len;
	cout << "len:" << endl;
	cin >> len;
	a = new int[len+1];
	if (!a)return -1;
	for (int i = 1; i < len + 1; ++i)
		a[i] = i;
	PrintPermutation(a, 1, len);
}

如果有遗漏和错误的地方,欢迎指出。

猜你喜欢

转载自blog.csdn.net/weixin_44972997/article/details/114443152
今日推荐