二叉树基础

一:前言TOC

  本文主要讲解以下二叉树的 4 个部分:
(1)构造二叉树;
(2)前,中,后序遍历(递归与非递归)和层次遍历;
(3)求节点数;
(4)求叶子数。
在此先约定下二叉树的节点结构和类的结构:

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

class BiTree
{
public:
    Node * root;  //根节点
public:
    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);       //求叶子数
};


二:具体实现与代码分析TOC

2.1 构造二叉树TOC

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 前序遍历TOC


   对于当前节点,先输出该节点,然后输出它的左孩子,最后输出它的右孩子。 以上图为例,递归的过程如下:
(1)输出 1,接着左孩子;
(2)输出 2,接着左孩子;
(3)输出 4,左孩子为空,再接着右孩子;
(4)输出 6,左孩子为空,再接着右孩子;
(5)输出 7,左右孩子都为空,此时 2 的左子树全部输出,2 的右子树为空,此时 1 的左子树全部输出,接着 1 的右子树;
(6)输出 3,接着左孩子;
(7)输出 5,左右孩子为空,此时 3 的左子树全部输出,3 的右子树为空,至此 1 的右子树全部输出,结束。
  而非递归版本只是利用 stack 模拟上述过程而已,递归的过程也就是出入栈的过程。注意加粗部分的理解。

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 中序遍历TOC

  对于当前节点,先输出它的左孩子,然后输出该节点,最后输出它的右孩子。以(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 的右子树全部输出,结
    束。

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 后序遍历TOC

  对于当前节点,先输出它的左孩子,然后输出它的右孩子,最后输出该节点。依旧以(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 变量)。若非上述两种情况,则将该节点的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候, 先依次遍历左子树和右子树,然后输出再输出节点。

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) ||                     //第一个输出的必是无左右孩子的叶子节点,只要第一个节点输出,
            (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 层次遍历TOC

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 计算节点和叶子数TOC

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);
}


三:完整代码TOC


#include<iostream>
#include<stack>
#include<queue>
using namespace std;

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

class BiTree
{
public:
    Node * root;  //根节点
public:
    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) ||                     //第一个输出的必是无左右孩子的叶子节点,只要第一个节点输出,
            (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)图为例,输入数据及测试如下图:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值