二、递归(来自郭炜老师的算法)

本文深入探讨了递归的基本思想和特点,通过汉诺塔、八皇后等经典问题阐述递归的解决策略。同时,展示了全排列、2的次幂方表示、布尔表达式计算及简单整数划分问题的递归算法实现,帮助读者理解递归在解决问题中的强大能力。代码示例清晰易懂,适合进阶学习。
摘要由CSDN通过智能技术生成

递归

1.基本思想:一个过程或函数在其定义或说明中直接或间接调用自身的一种方法,通常把一个大型的复杂的问题转化为一个与原问题相似的规模较小的问题来求解。
2.算法特点:思路简洁但是运行效率比较低。
3.比较经典的有汉诺塔问题和八皇后问题,这两个问题都能在网上找到详细的讲解(推荐一下郭炜老师讲的八皇后问题,讲得真是特别好)。
4.课后测验:
a.全排列
在这里插入图片描述
思路:有点类似深度优先遍历
用一个flag数组标记字符数组中的字符是否被填写到结果数组,如果没有填写,则将其加入并且递归填写结果数组的下一个位置,因为要输出所有全排列的结果,所以递归结束后要将flag数组置为未填写,以便进行下一轮的递归。
代码如下:

# include<iostream>
# include<cstring>
using namespace std;

int len,flag[10];//flag用于标记同下标的字符是否被访问过
char c[10];

void f(char a[],int k)
{
	if (k == len)
	{
		for (int i = 0; i < k; i++)
			cout << a[i];
		cout << endl;
	}
	for (int i = 0; i < len; i++)
	{
		if (!flag[i])
		{
			flag[i] = 1;
			a[k] = c[i];
			f(a, k + 1);
			flag[i] = 0;
		}
	}

}

int main()
{
	cin >> c;
	len = strlen(c);
	char a[10];
	f(a,0);
	return 0;
}

b.2的次幂方表示
在这里插入图片描述
思路:用string类做这题比较好,直接看看代码吧,应该比较好理解。

# include<iostream>
# include<string>
using namespace std;

void f(string &s, int n)
{
	if (n == 0)
		return;
	if (n == 1)
		s += "2(0)";
	else if (n == 2)
		s += "2";
	else
	{
		int v = 1, i = -1;
		while (v <= n)
		{
			v = 1;
			i++;
			v = v << i;
		}
		v = v >> 1;
		i--;
		if (i == 1)
			s += "2";
		else
		{
			s += "2(";
			f(s, i);
			s += ")";
		}
		if (n - v != 0)
		{
			s += "+";
			f(s, n - v);
		}
	}
}

int main()
{
	string s;
	int n;
	cin >> n;
	f(s, n);
	cout << s;
	return 0;
}

注意到24行加了一个判断是否i为1,如果不加这个判断,结果就会出现2(2(0))这样的现象。

c:Boolean Expressions
在这里插入图片描述
思路:和四则运算表达式求值同样的思路。表达式由因子和因子的&&或||组成,因子由“V”、“F”或“(”+“表达式”+“)”组成,或者由他们的!组成,这就出现了递归定义,照着定义套代码就可以了,注意一些细节的处理。

# include<iostream>
using namespace std;

bool factor();
bool expression()
{
	bool result = factor();
	while (1)
	{
		char c = cin.peek();
		if (c == ' ')
			cin.get();
		else if (c == '|' || c == '&')
		{
			cin.get();
			bool value = factor();
			if (c == '|')
			{
				result = result || value;
			}
			else
			{
				result = result && value;
			}
		}
		else
			break;
	}
	return result;
}

bool factor()
{
	bool result = true;
	while (1)
	{
		char  c = cin.peek();
		if (c == ' ')
		{
			cin.get();
		}
		else if (c == 'V')
		{
			result = true;
			cin.get();
		}
		else if (c == 'F')
		{
			result = false;
			cin.get();
		}
		else if (c == '!')
		{
			cin.get();
			bool value = factor();
			result = !value;
		}
		else if (c == '(')
		{
			cin.get();
			result = expression();
			cin.get();
		}
		else
			break;
	}
	return result;
}

int main()
{
	int count = 1;
	while (cin.peek() == '(' || cin.peek() == 'V' || cin.peek() == 'F' || cin.peek() == '&' || cin.peek() == '|' || cin.peek() == '!')
	{
		bool result = expression();
		if (result)
		{
			cout << "Expression " << count++ << ": " << "V"<<endl;
		}
		else
		{
			cout << "Expression " << count++ << ": " << "F"<<endl;
		}
		cin.get();
	}
	return 0;
}

值得注意的有cin.get()和cin.peek()的用法,我打算在其他文章中详细的说一说。

d.简单的整数划分问题
在这里插入图片描述
思路:这题就是要找到递推公式,找到公式后就容易许多了。这里定义两个变量:n,m(n表示要划分的数字,m表示划分中的最大数字,最大数字不一定存在划分中,小于等于m都可以)。
分四种情况讨论。
第一种:n或m等于1,n等于1时,只能划分为1,m=1时只能划分为n个1的和的形式,都只有一种划分方法;
第二种:n等于m时,一种划分是只有一个m这种情况,另一种就是最大数字为m-1的划分的情况;
第三种:n小于m,此时只有最大值为n时的划分,回到了第二种情况,因为不能出现负数的划分;
第四种:n大于m,一种划分情况是划分数字为n-m并且最大数字为m的情况,另一种是划分数字为n并且最大数字为m-1的情况(即至少包含一个m和不包含m的情况)。

得到递推公式:
1;(n=1或m=1)
1+f(n,m-1);(n=m)
f(n,n);(n<m)
f(n-m,m)+f(n,m-1);(n>m)

代码如下:

# include<iostream>
using namespace std;

int f(int n, int m)
{
	if (n == 1 || m == 1)
		return 1;
	else if (n == m)
	{
		return (1 + f(n,m - 1));
	}
	else if (n < m)
	{
		return f(n, n);
	}
	else
	{
		return f(n - m, m) + f(n, m - 1);
	}
}

int main()
{
	int n;
	while(cin >> n)
		cout << f(n,n)<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值