复旦19考研机试真题(3)--二叉树的形态

这篇博客探讨了如何计算特定数量节点能组成的不同二叉树个数问题,涉及递推公式和卡特兰数。作者指出,利用递推公式在处理大数时存在效率问题,并分享了卡特兰数的通项公式。由于涉及到大数运算的复杂性,作者选择在一定范围内解决问题,而非追求满分解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:
求N个结点能够组成的二叉树的个数(2<=N<=1000)。
例:
输入:

3

输出:

5

分析:看别人的博文说是卡特兰数;我首先想到的是找规律,用递推公式求解,因为我不知道卡特兰数; 寻找出规律之后 当然这里结果可能会超过long long的表示范围,要处理大数问题;

先分析规律,假设n个结点能够组成的二叉树数量为f(n),根节点是固定的,假设左子树有i(0<=i<=n-1)个结点,则右子树有n-1-i个结点,此时可能组成的二叉树数量为f(i) * f(n-1-i);所以有:
f(n) = f(0) * f(n-1) + f(1) * f(n-1-1) + … + f(n-1) * f(n-1-(n-1))
= f(0) * f(n-1) + f(1) * f(n-2) + … + f(n-1) * f(0)

但是计算一下复杂度,用上面的式子求f(n)的复杂度为O(n2),处理大数时时间复杂度为O(n),程序总的时间复杂度为O(n3);这个时间复杂度肯定是要超时了;此时我无奈的发现,不用卡特兰数是没办法解题了;

上面的式子进一步推导,得到递推公式(当然请原谅我并没有去推导,我觉得自己在考场上也不可能推导出来):
f(n)= f(n-1)(4n-2)/(n+1)
我们不妨再看一看卡特兰数的通项公式:
f(n)=C(n, 2n)/(n+1) = (2n)!/((n!)(n+1)!)

关于卡特兰问题的讲解我在知乎上看到一篇很不错,可以去看看,
知乎-卡特兰数.

但是!递推公式里面里面有除法,通项公式里面也有除法!难道我不会JAVA的话,要在考场上手撸一遍大数的加法,乘法,还有除法嘛?!

算了算了,我还是抛弃卡特兰数吧,毕竟我不太会写大数除法,代码量太大了;2<=n<=100这个范围内,还是可以正确的,想拿满分,等我以后进一步学习吧。

#include<iostream>   
using namespace std;
struct bigInteger {
	int digit[1010], size; 
	bigInteger() {
		memset(digit, 0, sizeof(digit));
		size = 0;
	}
	void set(string str) { 
		int L = str.length();
		for (int i = L - 1; i >= 0; i--) 
			digit[size++] = str[i]-'0';
	}
	void output() {
		for (int i = size - 1; i >= 0; i--)
			printf("%d", digit[i]);
	}
	bigInteger operator +(const bigInteger& A){
		bigInteger ret; 
		int carry = 0;
		for (int i = 0; i < A.size || i < size; i++){
			int tmp = A.digit[i] + digit[i] + carry;
			carry = tmp / 10;
			tmp %= 10;
			ret.digit[ret.size++] = tmp;
		} 
		if (carry != 0) 
			ret.digit[ret.size++] = carry; 
		return ret;
	} 
	bigInteger operator *(const bigInteger& A){
		bigInteger ret;
		ret.size = A.size + size; 
		for (int i = 0; i < A.size; i++) {
			for (int j = 0; j < size; j++) 
				ret.digit[i + j] += A.digit[i]*digit[j];
		}
		int carry = 0;
		for (int i = 0; i < ret.size; i++) {
			ret.digit[i] += carry;
			carry = ret.digit[i] / 10;
			ret.digit[i] %= 10;
		}
		while (ret.digit[ret.size - 1] == 0 && ret.size > 1)
			ret.size--;
		return ret;
	}
};
bigInteger dp[1010];
int main() { 
	int n; 
	scanf("%d", &n);
	dp[0].set("1");
	dp[1].set("1"); 
	for (int i = 2; i <= n; i++) {
		for (int j = 0; j <= i - 1; j++)
			dp[i] = dp[i] + dp[j] * dp[i - 1 - j];
	}
	dp[n].output();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值