二叉樹基礎

一:前言

  本文主要講解如下二叉樹的4個部分:
  (1)構造二叉樹;
  (2)前,中,後序遍歷(遞歸與非遞歸)和層次遍歷;
  (3)求節點數;
  (4)求葉子數。
  在此先約定下二叉樹的節點結構和類的結構:php

struct Node 
{
    char data;
    Node *left, *right;
};

class BiTree
{
public:
    Node *root;  //根節點
    BiTree();
    Node* create();
    void pre_order_rec(Node *node);   //前序遍歷遞歸版
    void pre_order_non_rec();         //前序遍歷非遞歸版
    void in_order_rec(Node *node);    //中序
    void in_order_non_rec();          //
    void post_order_rec(Node *node);  //後序
    void post_order_non_rec();        //
    void level_order();               //層次遍歷
    int count_node(Node *node);       //求節點數
    int count_leaf(Node *node);       //求葉子數
};

二:具體實現與代碼分析

2.1 構造二叉樹

BiTree::BiTree()
{
    root = create();
}

Node * BiTree::create()
{
    Node *p = nullptr;
    char ch;
    cin >> ch;
    if (ch == '.')  //結束輸入
        p = nullptr;
    else
    {
        p = new Node;
        p->data = ch;
        p->left = create();
        p->right = create();
    }
    return p;
}

2.2 前序遍歷

  對於當前節點,先輸出該節點,而後輸出它的左孩子,最後輸出它的右孩子。以上圖爲例,遞歸的過程以下:
  (1)輸出1,接着左孩子;
  (2)輸出2,接着左孩子;
  (3)輸出4,左孩子爲空,再接着右孩子;
  (4)輸出6,左孩子爲空,再接着右孩子;
  (5)輸出7,左右孩子都爲空,此時2的左子樹所有輸出,2的右子樹爲空,此時1的左子樹所有輸出,接着1的右子樹;
  (6)輸出3,接着左孩子;
  (7)輸出5,左右孩子爲空,此時3的左子樹所有輸出,3的右子樹爲空,至此1的右子樹所有輸出,結束。
  而非遞歸版本只是利用stack模擬上述過程而已,遞歸的過程也就是出入棧的過程。注意加粗部分的理解。node

void BiTree::pre_order_rec(Node * node)
{
    if (node == nullptr)
        return;
    else
    {
        cout << node->data << " ";   //先輸出當前節點   
        pre_order_rec(node->left);   //而後輸出左孩子
        pre_order_rec(node->right);  //最後輸出右孩子
    }
}

void BiTree::pre_order_non_rec()
{
    stack<Node*> S;
    Node *p = root;
    while (p || !S.empty())
    {
        while (p)
        {                            
            cout << p->data << " ";  //先輸出當前節點  
            S.push(p);
            p = p->left;  //而後輸出左孩子
        }  //while結束意味着左孩子已經所有輸出
        if (!S.empty())
        {
            p = S.top()->right;  //最後輸出右孩子
            S.pop();
        }
    }
}

2.3 中序遍歷

  對於當前節點,先輸出它的左孩子,而後輸出該節點,最後輸出它的右孩子。以(2.2)圖爲例:
  (1)1-->2-->4,4的左孩子爲空,輸出4,接着右孩子;
  (2)6的左孩子爲空,輸出6,接着右孩子;
  (3)7的左孩子爲空,輸出7,右孩子也爲空,此時2的左子樹所有輸出,輸出2,2的右孩子爲空,此時1的左子樹所有輸出,輸出1,接着1的右孩子;
  (4)3-->5,5左孩子爲空,輸出5,右孩子也爲空,此時3的左子樹所有輸出,而3的右孩子爲空,至此1的右子樹所有輸出,結束。
  注意加粗部分的理解。ios

void BiTree::in_order_rec(Node * node)
{
    if (node == nullptr)
        return;
    else
    {
        in_order_rec(node->left);   //先輸出左孩子
        cout << node->data << " ";  //而後輸出當前節點
        in_order_rec(node->right);  //最後輸出右孩子
    }
}

void BiTree::in_order_non_rec()
{
    stack<Node*> S;
    Node *p = root;
    while (p || !S.empty())
    {
        while (p)
        {
            S.push(p);   
            p = p->left;  
        }  //while結束意味着左孩子爲空
        if (!S.empty())
        {
            cout << S.top()->data << " ";  //左孩子已經所有輸出,接着輸出當前節點
            p = S.top()->right;  //左孩子所有輸出,當前節點也輸出後,最後輸出右孩子
            S.pop();
        }
    }
}

2.4 後序遍歷

  對於當前節點,先輸出它的左孩子,而後輸出它的右孩子,最後輸出該節點。依舊以(2.2)圖爲例:
  (1)1->2->4->6->7,7無左孩子,也無右孩子,輸出7,此時6無左孩子,而6的右子樹也所有輸出,輸出6,此時4無左子樹,而4的右子樹所有輸出,輸出4,此時2的左子樹所有輸出,且2無右子樹,輸出2,此時1的左子樹所有輸出,接着轉向右子樹;
  (2)3->5,5無左孩子,也無右孩子,輸出5,此時3的左子樹所有輸出,且3無右孩子,輸出3,此時1的右子樹所有輸出,輸出1,結束。
  非遞歸版本中,對於一個節點,若是咱們要輸出它,只有它既沒有左孩子也沒有右孩子或者它有孩子可是它的孩子已經被輸出(由此設置pre變量)。若非上述兩種狀況,則將該節點的右孩子和左孩子依次入棧,這樣就保證了每次取棧頂元素的時候,先依次遍歷左子樹和右子樹,而後輸出再輸出節點。c++

void BiTree::post_order_rec(Node * node)
{
    if (node == nullptr)
        return;
    else
    {
        post_order_rec(node->left);   //先輸出左孩子
        post_order_rec(node->right);  //而後輸出右孩子
        cout << node->data << " ";    //最後輸出當前節點
    }
}

void BiTree::post_order_non_rec()
{
    if (root == nullptr)
        return;

    Node *pre = nullptr;
    Node *cur = root;
    stack<Node*> S;
    S.push(cur);
    while (!S.empty())
    {
        cur = S.top();
        if ((!cur->left && !cur->right) ||                     //第一個輸出的必是無左右孩子的葉子節點,由(!cur->left && !cur->right)
            (pre && (pre == cur->left || pre == cur->right)))  //來判斷,只要第一個節點輸出,之後的pre就不會是空。此處的判斷語句加入
        {                                                      //一個pre,只是用來確保輸出正確的第一個節點。
            cout << cur->data << " ";  //左右孩子都所有輸出,再輸出當前節點
            pre = cur;
            S.pop();
        }
        else
        {
            if (cur->right)
                S.push(cur->right);  //先進右孩子,再進左孩子,取出來的纔是左孩子
            if (cur->left)
                S.push(cur->left);
        }
    }
}

2.5 層次遍歷

void BiTree::level_order()
{
    if (root == nullptr)
        return;

    Node *p = root;
    queue<Node*> Q;  //隊列
    Q.push(p);
    while (!Q.empty())
    {
        p = Q.front();
        cout << p->data << " ";
        Q.pop();
        if (p->left)
            Q.push(p->left);  //注意順序,先進左孩子
        if (p->right)
            Q.push(p->right);
    }
}

2.6 計算節點和葉子數

int BiTree::count_node(Node *node)
{
    if (node == nullptr)
        return 0;
    return count_node(node->left) + count_node(node->right) + 1;
}

int BiTree::count_leaf(Node *node)
{
    if (node == nullptr)
        return 0;
    if (!node->left && !node->right)
        return 1;
    return count_leaf(node->left) + count_leaf(node->right);
}

三:完整代碼

/**
 *
 * author 劉毅(Limer)
 * date   2017-03-19
 * mode   C++
 */
#include<iostream>
#include<stack>
#include<queue>
using namespace std;

struct Node 
{
    char data;
    Node *left, *right;
};

class BiTree
{
public:
    Node *root;  //根節點
    BiTree();
    Node* create();
    void pre_order_rec(Node *node);   //前序遍歷遞歸版
    void pre_order_non_rec();         //前序遍歷非遞歸版
    void in_order_rec(Node *node);    //中序
    void in_order_non_rec();          //
    void post_order_rec(Node *node);  //後序
    void post_order_non_rec();        //
    void level_order();               //層次遍歷
    int count_node(Node *node);       //求節點數
    int count_leaf(Node *node);       //求葉子數
};

int main()
{
    BiTree myTree;
    //1.  
    cout << "前序遍歷遞歸與非遞歸\n";
    myTree.pre_order_rec(myTree.root); cout << endl;
    myTree.pre_order_non_rec(); cout << endl << endl;
    //2.  
    cout << "中序遍歷遞歸與非遞歸\n";
    myTree.in_order_rec(myTree.root); cout << endl;
    myTree.in_order_non_rec(); cout << endl << endl;
    //3.  
    cout << "後序遍歷遞歸與非遞歸\n";
    myTree.post_order_rec(myTree.root); cout << endl;
    myTree.post_order_non_rec(); cout << endl << endl;;

    //4.  
    cout << "層次遍歷\n";
    myTree.level_order(); cout << endl << endl;;

    //5.  
    cout << "該二叉樹的節點有";
    cout << myTree.count_node(myTree.root);
    cout << "個\n\n";

    //6.  
    cout << "該二叉樹的葉子有";
    cout << myTree.count_leaf(myTree.root);
    cout << "個\n\n";

    return 0;
}

BiTree::BiTree()
{
    root = create();
}

Node * BiTree::create()
{
    Node *p = nullptr;
    char ch;
    cin >> ch;
    if (ch == '.')  //結束輸入
        p = nullptr;
    else
    {
        p = new Node;
        p->data = ch;
        p->left = create();
        p->right = create();
    }
    return p;
}

void BiTree::pre_order_rec(Node * node)
{
    if (node == nullptr)
        return;
    else
    {
        cout << node->data << " ";   //先輸出當前節點   
        pre_order_rec(node->left);   //而後輸出左孩子
        pre_order_rec(node->right);  //最後輸出右孩子
    }
}

void BiTree::pre_order_non_rec()
{
    stack<Node*> S;
    Node *p = root;
    while (p || !S.empty())
    {
        while (p)
        {                            
            cout << p->data << " ";  //先輸出當前節點  
            S.push(p);
            p = p->left;  //而後輸出左孩子
        }  //while結束意味着左孩子已經所有輸出
        if (!S.empty())
        {
            p = S.top()->right;  //最後輸出右孩子
            S.pop();
        }
    }
}

void BiTree::in_order_rec(Node * node)
{
    if (node == nullptr)
        return;
    else
    {
        in_order_rec(node->left);   //先輸出左孩子
        cout << node->data << " ";  //而後輸出當前節點
        in_order_rec(node->right);  //最後輸出右孩子
    }
}

void BiTree::in_order_non_rec()
{
    stack<Node*> S;
    Node *p = root;
    while (p || !S.empty())
    {
        while (p)
        {
            S.push(p);   
            p = p->left;  
        }  //while結束意味着左孩子爲空
        if (!S.empty())
        {
            cout << S.top()->data << " ";  //左孩子所有輸出,接着輸出當前節點
            p = S.top()->right;  //左孩子所有輸出,當前節點也輸出後,最後輸出右孩子
            S.pop();
        }
    }
}

void BiTree::post_order_rec(Node * node)
{
    if (node == nullptr)
        return;
    else
    {
        post_order_rec(node->left);   //先輸出左孩子
        post_order_rec(node->right);  //而後輸出右孩子
        cout << node->data << " ";    //最後輸出當前節點
    }
}

void BiTree::post_order_non_rec()
{
    if (root == nullptr)
        return;

    Node *pre = nullptr;
    Node *cur = root;
    stack<Node*> S;
    S.push(cur);
    while (!S.empty())
    {
        cur = S.top();
        if ((!cur->left && !cur->right) ||                     //第一個輸出的必是無左右孩子的葉子節點,由(!cur->left && !cur->right)
            (pre && (pre == cur->left || pre == cur->right)))  //來判斷,只要第一個節點輸出,之後的pre就不會是空。此處的判斷語句加入
        {                                                      //一個pre,只是用來確保輸出正確的第一個節點。
            cout << cur->data << " ";  //左右孩子都所有輸出,再輸出當前節點
            pre = cur;
            S.pop();
        }
        else
        {
            if (cur->right)
                S.push(cur->right);  //先進右孩子,再進左孩子,取出來的纔是左孩子
            if (cur->left)
                S.push(cur->left);
        }
    }
}

void BiTree::level_order()
{
    if (root == nullptr)
        return;

    Node *p = root;
    queue<Node*> Q;  //隊列
    Q.push(p);
    while (!Q.empty())
    {
        p = Q.front();
        cout << p->data << " ";
        Q.pop();
        if (p->left)
            Q.push(p->left);  //注意順序,先進左孩子
        if (p->right)
            Q.push(p->right);
    }
}

int BiTree::count_node(Node *node)
{
    if (node == nullptr)
        return 0;
    return count_node(node->left) + count_node(node->right) + 1;
}

int BiTree::count_leaf(Node *node)
{
    if (node == nullptr)
        return 0;
    if (!node->left && !node->right)
        return 1;
    return count_leaf(node->left) + count_leaf(node->right);
}

  以(2.2)圖爲例,輸入數據及測試以下圖:
post

文章轉自個人我的博客:http://www.61mon.com/index.php/archives/189/測試

相關文章
相關標籤/搜索