這幾天寫了寫了幾道題,覺得這道平衡二叉樹的題有點意思,就把他的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級
提交代碼,通過。