C++二叉樹
技術標籤:C++雜項
兩種特殊二叉樹
1.滿二叉樹:除葉子節點外的所有分支節點都含有2個非空子節點的二叉樹
2.完全二叉樹:除了最後一層,其餘層都是“滿”的,這樣的二叉樹是完全二叉樹
二叉樹定理
1)任意二叉樹度數為2節點的個數等於葉節點個數減1
當只有1個節點時,度為0。每派生出1度,就會多出1個節點。派生出的度和派生出的節點數一定相等。那麼就得出了總度數和節點總數的關係:
節點總數 = 總度數 + 1
設度數為2的節點數為X2,度數為1的節點數為X1,度數為0的節點數為X0。可以得出如下關係式:
X2 + X1 + X0 = 2X2 + X1 + 1,推出 X2 = X0 - 1
因此,度數為2的節點個數等於葉節點數減1
2)滿二叉樹定理:非空滿二叉樹的葉節點數等於其分支節點數加1
如果已知前一個結論,那麼這個定理顯然成立。下面分析如果不知道前一個結論,怎麼證明
對於只有1個節點的樹,該定理成立。從這開始思考,每產生1個分支節點(度數為2)。葉子節點數也會加1。因為要產生一個分支節點,那麼這個新的分支節點必然是原來的葉子節點,而新的分支節點又生成了2個新的葉子節點。因此葉子節點的總數先是減1然後加2,因此總數加1。因此,產生n個分支節點時,也產生了n個葉子節點,由於最初只有1個葉子節點,所以該定理成立
3)一顆非空二叉樹空子樹的數目等於其節點數目加1
考慮只有1個根節點的二叉樹:它有2個空子樹,1個節點,因此結論成立。從這裡開始考慮,每產生1個節點。空子樹便會先減1然後加2。就和上面結論中每多出1個分支節點,葉子節點的變化一樣。因此在原來結論的基礎上,由於空子樹和節點等量增長。所以結論成立
3.前序遍歷:根->左->右
4.中序遍歷:左->根->右
5.後序遍歷:左->右->根
假設樹節點的定義如下:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
遞迴版
//前序遍歷
void preorderTraversalRecursion(TreeNode *node)
{
if(!node) return;
cout << node->val << " ";//操作當前節點
preorderTraversalRecursion(node->left);
preorderTraversalRecursion(node->right);
}
//中序遍歷
void inorderTraversalRecursion(TreeNode *node)
{
if(!node) return;
inorderTraversalRecursion(node->left);
cout << node->val << " ";//操作當前節點
inorderTraversalRecursion(node->right);
}
//後序遍歷
void postorderTraversalRecursion(TreeNode *node)
{
if(!node) return;
postorderTraversalRecursion(node->left);
postorderTraversalRecursion(node->right);
cout << node->val << " ";//操作當前節點
}
迭代版
需要使用一個棧作為輔助空間
//前序遍歷
void preorderTraversalIteration(TreeNode *root)
{
stack<TreeNode*> st;
if(root)
st.push(root);
while(!st.empty()){
TreeNode *nd = st.top();
st.pop();
cout << nd->val << " ";//操作當前節點
if(nd->right)
st.push(nd->right);
if(nd->left)
st.push(nd->left);
}
}
//中序遍歷:
void inorderTraversalIteration(TreeNode *root)
{
stack<TreeNode*> st;
TreeNode *curr = root;
while(curr || !st.empty()){
if(curr){
st.push(curr);
curr = curr->left;
}
else{
curr = st.top();
st.pop();
cout << curr->val << " ";//操作當前節點
curr = curr->right;
}
}
}
//後序遍歷
void postorderTraversalIteration(TreeNode *root)
{
stack<TreeNode*> st;
TreeNode *pre;
if(root)
st.push(root);
while(!st.empty()){
TreeNode *nd = st.top();
/*
* 出棧條件:
* 對於葉子節點:直接彈出
* 對於非葉子節點:如果已經遍歷過其左子節點或右子節點,則彈出
*/
if((!nd->left && !nd->right) || (pre && (nd->left == pre || nd->right == pre))){
st.pop();
cout << nd->val <<" ";//操作當前節點
pre = nd;
}
else{//說明是一個非葉子節點,並且還未訪問其左右孩子
if(nd->right)
st.push(nd->right);
if(nd->left)
st.push(nd->left);
}
}
}
對於後序遍歷,由於其訪問序列為:左->右->根。因此還有一種方法,可以按類似前序遍歷的方式:根->右->左,然後對得到的結果反序