这几天写了写了几道题,觉得这道平衡二叉树的题有点意思,就把他的c#算法写出来,不足之处请大家指点。
先看题目:
平衡二叉树
- 难 度 等 级:
题目详情
平衡二叉树的定义是递归定义的:
(1) 单个节点是平衡二叉树
(2)平衡二叉树的左右子树分别都是平衡二叉树
(3)平衡二叉树的左右子树高度差不超过1。
求n个节点有m个叶子节点的平衡二叉树个数 (0<m <= n<=20)。
例如:
n = 1,m = 1,输出:1;
n = 2,m = 1,输出:2;
n = 2,m = 2,输出:0。
分析:
1、看完这题,从n=2,m=1,有两种结果,说明,这道题要考虑左右树交换位置,即假设找到一个满足条件的平衡二叉树,左右子树交换位置,也可能得到一个答案。
2、二叉树的一般原理可以得到,n=左右子树的节点和+1
3、同理可以得到:m=左右子树的叶子数和
4、每一个层级的最少节点数=左右子树最少节点数之和(n>1)
这一点什么意思呢?
举例来说吧,一个节点,层级是1,,也只能是1,。
对于2个层级的平衡树,如果要满足条件,最少需要2个节点,1个叶子,最多3个节点,2个叶子
对于3个层级的平衡树,如果要满足条件:
最少节点=2级的最少节点数+1级的最少节点数+1
最少叶子=2级的最少叶子数+1级的最少叶子数
最多节点=2级的最多节点数*2+1
最多叶子=2级的最多叶子数*2
有了这个推理过程,我们很容易得到x级的平衡树的最少节点数、叶子数,最多节点数、叶子数
为什么要得到这个推论和结果呢?因为给我们的是节点数和叶子数,所以我们要知道这样的条件能够生成多少级别的平衡二叉树
思路:
通过上面的分析,我们就可以将问题转换为,对于n,m可以在层级为x的平衡二叉树上有多少种方案,
当x>2时,可以将x降级为左右树的可能出现情况,这样就能得到结果=left*right
1、首先构造这个最大、最小节点叶子数的字典,当然,临时求出来也行,反正规律已经分析出来的:
static Dictionary<int, List<int>> dic;
dic = new Dictionary<int, List<int>>();
for (int i = 1; i <= 6; i++)
{
List<int> list = new List<int>();
if (i == 1)
{
list.Add(1);
list.Add(1);
list.Add(1);
list.Add(1);
}
if (i == 2)
{
list.Add(2);
list.Add(1);
list.Add(3);
list.Add(2);
}
if (i > 2)
{
list.Add(dic[i - 1][0] + dic[i - 2][0]+1);
list.Add(dic[i - 1][1] + dic[i - 2][1] );
list.Add(dic[i - 1][2]*2 +1);
list.Add(dic[i - 1][3] * 2 );
}
dic.Add(i, list);
}这里自己定义list[0]表示最少节点数,依次向后表示最少叶子数,最大节点数,最大叶子数
2、构造了范围集字典,下一步创建结果集字典
因为3级的结果={左1级*右边2级+左边2级*右边1级+左边2级*右边2级)的结果和
所以,我们先将1级和2级的结果得到:
TreeResult.Add("1,1,1", 1);
TreeResult.Add("2,2,1", 2);
TreeResult.Add("2,3,2", 1);我们还是自定义数据结构,key表示:层级,节点个数,叶子个数,value表示这样的平衡树有几种情况
因此我们可以这样构造一个递归函数:
public static int cal(int n,int m,int level)
{
int result = 0;
string key = "";
key = level.ToString() + "," + n.ToString() + "," + m.ToString();
string tmpkey="";
if (TreeResult.ContainsKey(key))
{
return TreeResult[key];
}
if (level < 3)
{
TreeResult.Add(key, 0);
return 0;
}
int left = 0;
int right = 0;
int rightnode = 0;
int rightleaf = 0;
int count =0;
if (m > dic[level][3] || m < dic[level][1])
{
TreeResult.Add(key, 0);
return 0;
}
for (int i = level - 2; i <= level - 1; i++)
{
for (int j = level - 2; j <= level - 1; j++)
{
if (i != level - 1 && j != level - 1)
{
continue;
}
for (int leftnode = dic[i][0]; leftnode <= dic[i][2]; leftnode++)
{
if (leftnode > n - 1)
{
break;
}
rightnode = n - 1 - leftnode;
for (int leftleaf = dic[i][1]; leftleaf <= dic[i][3]; leftleaf++)
{
if (leftleaf > m)
{
break;
}
rightleaf = m - leftleaf;
if (rightleaf <= 0)
{
break;
}
tmpkey=i.ToString() + "," + leftnode.ToString() + "," + leftleaf.ToString();
if (!TreeResult.ContainsKey(tmpkey))
{
left = cal(leftnode, leftleaf, i);
if (!TreeResult.ContainsKey(tmpkey))
{
TreeResult.Add(tmpkey, left);
}
}
else
{
left = TreeResult[tmpkey];
}tmpkey = j.ToString() + "," + rightnode.ToString() + "," + rightleaf.ToString();
if (!TreeResult.ContainsKey(tmpkey))
{
right = cal(rightnode, rightleaf, j);
if (!TreeResult.ContainsKey(tmpkey))
{
TreeResult.Add(tmpkey, right);
}
}
else
{
right = TreeResult[tmpkey];
}
count += left * right;
}
}
}
}if (!TreeResult.ContainsKey(key))
{
TreeResult.Add(key, count );
}
result = count;
return result;
}表示对于n个节点,m个叶子,在level层级下有多少种符合条件的平衡二叉树
在level层级降级过程,还需要考虑n,m如何分配给左右子树各多少个,由于有了前面的范围集合字典,我们可以遍历范围内的节点和叶子个数,超出范围的表示不可能出现,直接返回0
ok,大功告成,当输入n,我们首先判断有n个节点的平衡二叉树可能出现在哪些层级范围内,然后调用cal函数就可以了,累加得到结果。
由于题目说了n<=20,所以在前面,我们的字典只构造了6级
提交代码,通过。