二叉树之创建与遍历

本文深入探讨二叉树这一数据结构,解析二叉树的定义、满二叉树与完全二叉树的区别,以及五条重要性质。通过实例解释性质在解题中的应用。此外,介绍了二叉树的四种遍历方法,包括前序、中序、后序和层序遍历,并展示了如何使用C语言实现相关操作。
摘要由CSDN通过智能技术生成

本篇博客中的一些规则(比如根节点的层次为1)全部来自于数据结构(C语言版)

在数据结构中树是一个非常重要的知识点,在树的学习过程中我们可以知道的是,它中的很多歌关键名词,这些基本概念有时可以让我们很清楚的做一些关于性质的选择填空题:

结点:结点包括一个数据元素及若干指向其他子树的分支(指针)。
结点的度:结点所拥有的子树的个数称为该结点的度。
叶结点:度为0的结点称为叶结点,叶结点叶称为中断结点;
分支结点:度补位0的结点称分支结点,分支结点叶称为非终端结点。一个数中除叶结点外的所有结点都是分支结点;
祖先结点:从根结点到该结点所经分支上的所有结点;
子孙结点:以某结点为根结点的子树中所有结点;
双亲结点:树中某结点有孩子结点,则这个结点称为它孩子结点的双亲结点,双亲结点叶称为前驱结点;
孩子结点:树中一个结点的子树的根结点称为该结点的孩子结点,孩子结点叶称为后继结点;
兄弟结点:具有相同双亲结点的结点称为兄弟结点;
树的度:树中所有结点的度的最大值称为该树的度;
结点的层次:从根节点带树中某结点所经路径上的分支数称为该结点的层次,根结点的层次为1,其他结点的层次是双亲层次加1;
树的深度:树中所有结点的层次的最大值成为树的深度;
森林:树m颗树的集合(m大于等于0)。在自然界树和senlin是两个完全不同的概念,但在数据结构中华,它们之间的差别很小。删去一颗非空数的根节点,树就变为了森林;反之若增加一个结点,让森林汇总每棵树的根结点都变成了他的子女,森林就变成了一棵树;

二叉树

二叉树(Binary Tree)是另一种树形结构,它的特点是每个结点至多只有两个子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树中有左右之分,其次序不能任意颠倒;
二叉树中的两个相似但又区别很大的概念:满二叉树/完全二叉树
满二叉树:在一颗二叉树中,如果所有分支都存在左子树和右子树,并且所有叶子节点都在同一层。
完全二叉树:如果一颗具有N个结点的二叉树的结构与满二叉树的前N个结点的结构相同。称为完全二叉树;
这里写图片描述
二叉树的性质:

1、在二叉树的第i层上至多有2^(i-1)个结点(i >= 1);
2、深度为k的二叉树至多有(2^k)-1个结点;
3、对任何一颗二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则no = n2 + 1;
4、具有n个结点的完全二叉树的深度为Llog2(n)」+1;(log以2为底n的对数向下取整在加1);
5、如果对一颗有n个结点的完全二叉树的结点按层序编号(从1到第Llog2(n)」+1层,每层从左向右),则对任一结点i(1<=i<=n),有:
(1)、如果i=1,则结点i是二叉树的根,无shuangqin;如果i>1,则其双亲Paren(i)是结点Li/2」;
(2)、如果2i>n,则结点i无左孩子;否则其左孩子LCHILD(i)是结点2i;
(3)、如果2i+1>n,则结点i无右孩子;否则其右孩子RCHILD(i)是结点2i+1;

根据这5点性质,我们可以看看下面的题:
这里写图片描述
1中:我们可以满二叉树是没有度为1的结点的,所以 分支结点有31个,叶子结点有32个;
2中:利用性质4中的公式可以很简单的得到深度为9;
3中:在完全二叉树中它的结点数是奇数说明它中没有度为1的节点,所以,它的叶子结点是:36个;
4中:它的结点数是偶数,所以它有一个度为1的结点,所以有500个叶子结点,度为2的结点为499个,只有1个非空左子树,没有非空右子树;
看到上面的一些题,我们性质还是很重要的,所以我们要牢记这几条性质;

在了解二叉树的基本概念和性质后,我们要对二叉树进行遍历,其中遍历二叉树一共分为4中,

先序遍历二叉树:
    若二叉树为空,则空操作;否则:
    (1)访问根结点;
    (2)先序遍历左子树;
    (3)先序遍历右子树;
中序遍历二叉树:
    若二叉树为空,则空操作;否则:
    (1)中序遍历左子树;
    (2)访问根结点;
    (3)中序遍历右子树;
后序遍历二叉树:
    若二叉树为空,则空操作;否则:
    (1)后序遍历左子树;
    (2)后序遍历右子树;
    (3)访问根结点;

其实他们三中方法很相似,就是访问的顺序不一样而已,他们在实现的时候都是采用递归的写法;
后面还有一个层序遍历,就和上面三种不一样了:
按照二叉树的层序次序(从根结点到叶结点层),同一层中按先左右后的次序遍历二叉树,

层序遍历算法:
a、初始化一个队列;
b、吧根结点的指针如队列;
c、当队列非空时,循环执行以下步骤
>> 出队列取一个结点
>> 若该节点的左子树非空,将改结点的左子树指针入队列
>> 若改结点的右子树非空,将改结点的右子树指针入队列
d、结束

在本篇博客的代码中,我从用先序构建一个二叉树到它的构造,拷贝构造,赋值运算符的重载,析构,四种遍历方式,全部完成

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

template<class T>
struct BinaryTreeNode
{
    BinaryTreeNode(const T& data)
    : _data(data)
    , _pLeft(NULL)
    , _pRight(NULL)
    {}

    T _data;
    BinaryTreeNode<T>* _pLeft;    // 左孩子
    BinaryTreeNode<T>* _pRight;   // 右孩子
};

template<class T>
class BinaryTree
{
    typedef BinaryTreeNode<T> Node;
public:
    BinaryTree()
        : _pRoot(NULL)
    {}


    BinaryTree(const T array[], size_t size, const T& invalid)
    {
        // 创建树
        size_t index = 0;
        _CreateTree(_pRoot, array, size, index, invalid);
    }

    BinaryTree(const BinaryTree<T>& t)
    {
        _pRoot = _CopyBinaryTree(t._pRoot);
    }

    BinaryTree<T>& operator=(const BinaryTree<T>& t)
    {
        if (_pRoot)
        {
            _DestroyTree(_pRoot);
            _pRoot = _CopyBinaryTree(t._pRoot);
        }
        else
            _pRoot = _CopyBinaryTree(t._pRoot);
        return *this;
    }

    // 先序遍历
    void PreOrder()
    {
        _PreOrder(_pRoot);
        cout << endl;
    }

    // 中序遍历:访问左子树-->根-->右子树 
    void InOrder()
    {
        _InOrder(_pRoot);
        cout << endl;
    }

    // 后续遍历:访问左子树-->访问右子树-->访问根节点
    void PostOrder()
    {
        _PostOrder(_pRoot);
        cout << endl;
    }

    // 层序遍历
    void LevelOrder()
    {
        queue<Node*> q;
        if (_pRoot)
            q.push(_pRoot);
        while (!q.empty())
        {
            Node* pCur = q.front();
            cout << pCur->_data << " ";
            q.pop();
            if (pCur->_pLeft)
            {
                q.push(pCur->_pLeft);
            }
            if (pCur->_pRight)
            {
                q.push(pCur->_pRight);
            }
        }
    }

    ~BinaryTree()
    {
        _DestroyTree(_pRoot);
    }

private:
    void _CreateTree(Node*& pRoot, const T array[], size_t size, size_t& index, const T& invalid)//在递归实现的时候我们必须给它的变量取引用,不然最后返回的时候不能按照字符串的遍历顺序走;
    {
        if (index < size && array[index] != invalid)
        {
            pRoot = new Node(array[index]);
            _CreateTree(pRoot->_pLeft, array, size, ++index, invalid);
            _CreateTree(pRoot->_pRight, array, size, ++index, invalid);
        }
    }

    Node* _CopyBinaryTree(Node* pRoot)
    {
        Node* tmp = NULL;
        if (NULL != pRoot)
        {
            tmp = new Node(pRoot->_data);
            tmp->_pLeft = _CopyBinaryTree(pRoot->_pLeft);
            tmp->_pRight = _CopyBinaryTree(pRoot->_pRight);
        }
        return tmp;
    }

    void _DestroyTree(Node* &pRoot)
    {
        if (pRoot)
        {
            _DestroyTree(pRoot->_pLeft);
            _DestroyTree(pRoot->_pRight);
            delete pRoot;
            pRoot = NULL;

        }
    }

    void _PreOrder(Node* pRoot)
    {
        if (pRoot)
        {
            cout << pRoot->_data << " ";
            _PreOrder(pRoot->_pLeft);
            _PreOrder(pRoot->_pRight);
        }
    }

    void _InOrder(Node* pRoot)
    {
        if (pRoot)
        {
            _InOrder(pRoot->_pLeft);
            cout << pRoot->_data << " ";
            _InOrder(pRoot->_pRight);
        }
    }

    void _PostOrder(Node* pRoot)
    {
        if (pRoot)
        {
            _PostOrder(pRoot->_pLeft);
            _PostOrder(pRoot->_pRight);
            cout << pRoot->_data << " ";
        }
    }
private:
    BinaryTreeNode<T>* _pRoot;
};

void Test()
{
    char array[] = "124###35##6";
    BinaryTree<char> s(array, strlen(array), '#');
    BinaryTree<char> s1(s);
    s.PreOrder();
    s1.PreOrder();
    s.InOrder();
    s1.InOrder();
    s.PostOrder();
    s1.PostOrder();
    BinaryTree<char> s2;
    s2 = s;
    s.LevelOrder();
}

int main()
{
    Test();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值