《數據結構》嚴蔚敏版習題6.55
爲二叉鏈表的結點增加DescNum域,表示該結點的子孫數量。編寫一個算法,求二叉樹的每個結點的子孫數目並存入其DescNum域。
思路:
- 方案1,利用遞歸,從根節點開始對每個結點都調用一次函數來求每個節點的子孫數量
很明顯遞歸調用肯定很慢,因爲要不斷地壓棧彈棧。 - 方案2,與方案1的遍歷方向相反,從最下面的葉子結點開始計算每個結點的子孫數量,然後對於其雙親節點,只要將左右孩子的DescNum域加起來就可以了。那麼哪種遍歷可以滿足這種“先孩子後雙親”的遍歷順序呢?當然是後續遍歷。下面給出方案2的c++代碼
/*
求每個結點的子孫數目
*/
#include "bitree.h"
typedef struct myTNode
{
int data;
int descendant;
struct myTNode* lchild;
struct myTNode* rchild;
}myBiTree, *pmyBiTree;
void mycreat_tree(pmyBiTree &rt)
{
char ch;
ch=getchar();
if('#'==ch)
{
rt=NULL;
}
else
{
rt=(pmyBiTree)malloc(sizeof(myBiTree));
rt->data=ch;
rt->descendant = 0;
mycreat_tree(rt->lchild); //構造左子樹
mycreat_tree(rt->rchild); //構造右子樹
}
}
void DescNumber(pmyBiTree &rt)
{
if(!rt)
return;
stack<pmyBiTree> s;
pmyBiTree cur = rt;
pmyBiTree pre = NULL;
while(!s.empty() || cur != NULL)
{
while(cur)
{
s.push(cur);
cur = cur->lchild;
}
cur = s.top();
if(cur->rchild == NULL || cur->rchild == pre)
{
if(cur->lchild)
cur->descendant += cur->lchild->descendant + 1;
if(cur->rchild)
cur->descendant += cur->rchild->descendant + 1;
pre = cur;
cur = NULL;
s.pop();
}
else
{
cur = cur->rchild;
}
}
}
void PreOrderPrintDesc(pmyBiTree &rt)
{
cout << "PreOrderPrintDesc: " << endl;
if(!rt)
return;
stack<pmyBiTree> s;
s.push(rt);
while(!s.empty())
{
pmyBiTree cur = s.top();
cout << (char)(cur->data) << " Desc: " << cur->descendant << endl;
s.pop();
if(cur->rchild)
s.push(cur->rchild);
if(cur->lchild)
s.push(cur->lchild);
}
cout << '@' << endl;
}
int main(int argc, char const *argv[])
{
pmyBiTree root;
mycreat_tree(root);
DescNumber(root);
PreOrderPrintDesc(root);
return 0;
}
函數void DescNumber(pmyBiTree &rt)
計算出二叉樹rt的所有結點的DescNum域。它利用後續遍歷的思路,不同之處僅在:本來應該打印當前結點的時候,根據是否存在左右結點,計算本結點的子孫數。此題也充分說明了二叉樹的3中遍歷方法的重要性,可以讓很多問題不用遞歸,也不用很高的複雜的就能解決。此算法複雜度爲O(n)。