关于二叉树的一些题

二叉树的创建我们已经了解了,现在我们在这一个基础上来拓展一些常出现的题

1、在二叉树中寻找一个节点——>存在,返回节点,不存在返回NULL

2、求一个节点的双亲节点——>存在,返回节点,不存在返回NULL

3、求二叉树的高度

4、求叶子节点的个数

5、求第K层的节点个数

6、求一个二叉树的镜像

……

不过在这这钱我们先来补充一下二叉树的性质:

1、若规定根节点的层次为0,则一个非空的二叉树的第i个节点最多有2^i(i>=0)个节点。

2、若规定只有根节点的深度为0,则深度为K的二叉树的最大节点数为2^(K+1)-1(K>=-1).

3、对任何二叉树,如果叶子节点为n0,度为n2的飞叶子节点的n2,则有n0=n2+1

证明过程如下:

这里写图片描述

4、具有n个节点的完全二叉树的深度K为log2(n+1)向上取整

懂得人都知道log后面的不是2*(n+1),而是“以2为底(n+1)”。具体怎么验证这个性质,那么不妨回顾一下完全二叉树的定义吧。而且这个性质的中心位置就是完全二叉树。

5、对具有N的节点的完全二叉树,如果按照从上到下,从左到右,的顺序对所有的节点进行排序,从0开始编号。则:i

①、i的双亲节点为(i-1)/2

②、i的左孩子,为2*i+1,右孩子为2*i+2

③、i=0,则为根节点,无双亲节点

④2*i+1>=N,2*i+2>=N,则没有双亲节点

了解了这些之后,我们再来看一下这些编程题:

1、在二叉树中寻找一个节点——>存在,返回节点,不存在返回NULL

如果用先序的遍历方式,那么用递归的话只需四步即可

这里写图片描述

非递归的话,也是可以参考先序遍历的非递归的写法,只需将对根节点的处理,改成和要查找的节点进行比较即可

Node* _Find(Node* pRoot, const T& value)
    {
        //递归:
        //if (NULL == pRoot)//树为空,则返回空
        //  return NULL;

        //if (pRoot->_value == value)//先确定根结点是不是所要查找的值
        //  return pRoot;

        //_Find(pRoot->_pLeft, value);//再查找左右子树
        //_Find(pRoot->_pRight, value);

        //非递归:
        if (pRoot == NULL)
            return NULL;
        stack<Node*> s;
        s.push(pRoot);

        while (!s.empty())
        {
            Node *pCur = s.top();
            if (pCur->_value == value)//注意:两个value类型不一样
                return pCur;

            s.pop();//时刻记住,访问完后要出栈

            if (pCur->_pRight)//注意,入栈顺序是先右后左
                s.push(pCur->_pRight);
            if (pCur->_pLeft)
                s.push(pCur->_pLeft);
        }
    }

2、求一个节点的双亲节点——>存在,返回节点,不存在返回NULL

首先我们得将参数检验一下:对根结点检验,判断树是不是空树;对pCur检验,验证它是不是存在;还有一根特殊的情况,根结点没有双亲。之后其他的都是普通情况。再进行递归运算。

Node* _Parent(Node* pRoot, Node* pCur)
    {
        if (NULL == pRoot)//首先确定树是不是空的
            return NULL;

        //参数验证2:判断pCur是不是为NULL   或    根结点是不是pCur(根结点是没有双亲结点的)
        if (NULL == pCur || pCur == pRoot)
            return NULL;

        if (pRoot->_pLeft)//在左子树中查找
            return _Parent(pRoot->_pLeft, pCur);

        return _Parent(pRoot->_pRight, pCur);//无论找没找到,都返回(这一步可以拆分开)

3、求二叉树的高度

一个二叉树的高度是多少?如果说根结点的层数为1的话,那最下面一层的层数就是二叉树的高度。那么用递归的话,它是左子树的高度和右子树高度的最大值再加1。

    size_t _Height(Node* pRoot)
    {
        if (NULL == pRoot)//没有结点,返回0
            return 0;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
            return 1;

        //遍历左右子树,返回左右子树最大值 +1(根结点)
        return max(_Height(pRoot->_pLeft),_Height(pRoot->_pRight))+1;
    }

4、求叶子节点的个数

首先想一下叶子结点的特点是啥,没有左右子树,所以用递归的话,这就是我们的一个递归出口,那么叶子结点的个数就是:递归左子树的个数加上递归右子树的个数。

size_t _GetLeafCount(Node* pRoot)
    {
        if (NULL == pRoot)
            return 0;

        //叶子结点
        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
            return 1;

        //递归:左子树的叶子结点+右子树的叶子结点
        return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
    }

5、求第K层的节点个数

第K层的结点个数,如果K=高度,那么这种情况和叶子结点个数还是不一样,叶子结点不一定都在同一层,所以,我们要求第K层的需要将左子树的第K-1层的计算出来,再加上右子树的第K-1层结点个数。

不过这里涉及参数检验的讨论,对于根结点的检验我们已经很熟悉了,判断其是不是空树就行了,不过这里还有一个参数:K

对于K来说,有一个特殊的情况那就是K==1,那么我们就返回1,至于对其进行参数检验,只要其不小于1就行了;那么你或许会问了,为什么不对它进行上限处理呢?需要吗?

/*假设我们进行了上限处理:K<Height(),我们这是递归,每一次进入这个函数走到这一步是不是都需要求一下高度,每次求高度还要将树遍历一下,那么算下来我们求了几次了,很多。而不加的时候会出错吗?我们假设K是5,(层数是3),那么到第四层(所有节点都是NULL)直接会从判断根结点为空的地方返回,所以,这是不会影响结果的。*/

//第k层的结点个数
    size_t _GetKLevelCount(Node* pRoot, size_t k)
    {
        if (NULL == pRoot || k < 1)//注意:参数检验
            return 0;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
            return 1;

        return _GetKLevelCount(pRoot->_pLeft, k - 1) + _GetKLevelCount(pRoot->_pRight, k - 1);
    }

6、求一个二叉树的镜像

这里写图片描述

如图所示,我们看到,左边的是我们一直使用的一个二叉树,镜像之后我们可以直观上看到,两个二叉树的关系:关于中间的一条隐形的线(镜子)对称。那么接下来我们就来根据这一个性质来求一个二叉树的镜像

首先我们处理的是二叉树的特殊情况:

1、要是树为空,那么就不用处理了,直接返回就行了

2、镜像是将二叉树中的左子树和右子树交换,那么如果这个数只有一个节点,那么就不用交换了。所以这一个情况是,二叉树只有一个节点。

3.普通情况:这要是用递归的话,很简单的几句代码就搞定了:首先是将根节点的两个子树交换,之后再进入左子树进行递归,然后再进入右子树进行递归。

这样之后,就完了,不过非递归又该怎么写呢?

同样我们来屡一下思路:

1、首先还式对树进行判断:树空的话,返回NULL

2、其次创建一个数据结构(队列),将根节点入队列

3、取出对头元素,判断其左右子树,存在,则入队列

4、对头元素的左右子树。最后将对头元素出队列

    void _BinaryMirror(Node* pRoot)
    {
        if (pRoot == NULL)
            return;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)
            return;

        swap(pRoot->_pLeft, pRoot->_pRight);

        if (pRoot->_pLeft!=NULL)
            _BinaryMirror(pRoot->_pLeft);
        if (pRoot->_pRight!=NULL)
            _BinaryMirror(pRoot->_pRight);
    }

    void _BinaryMirror_Nor(Node* pRoot)
    {
        if (pRoot == NULL)
            return;

        queue<Node*> q;
        q.push(pRoot);

        while (!q.empty())
        {
            Node* pCur = q.front();//取队头元素

            //如果左右孩子存在,将其保存在队列中
            if (pCur->_pLeft)
                q.push(pCur->_pLeft);
            if (pCur->_pRight)
                q.push(pCur->_pRight);

            //交换左右
            swap(pCur->_pLeft, pCur->_pRight);

            //队头元素出队列
            q.pop();
        }
    }

这里写图片描述
下面是完整的代码,以及测试的语句:

#include<iostream>
using namespace std;

#include<queue>
#include<stack>

// 孩子表示法
template<class T>
struct BinaryTreeNode
{
    BinaryTreeNode(const T& value)
    : _value(value)
    , _pLeft(NULL)
    , _pRight(NULL)
    {}

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


template<class T>
class BinaryTree
{
    typedef BinaryTreeNode<T> Node;//换个名字Node
public:
    //构造函数①
    BinaryTree()
        :_pRoot(NULL)
    {}

    //构造函数②
    BinaryTree(const T array[], size_t size, const T& invalid)
    {
        size_t index = 0;
        _CreateBinaryTree(_pRoot, array, size, index, invalid);
    }

    //拷贝构造函数
    BinaryTree(const BinaryTree<T>& bt)
    {
        _pRoot = _CopyBirnaryTree(bt._pRoot);
    }

    //赋值运算符重载
    BinaryTree<T>& operator=(const BinaryTree<T>& bt)
    {
        if (this != &bt)
        {
            _CopyBirnaryTree(bt._pRoot);
        }
        return this;
    }

    //析构函数
    ~BinaryTree()
    {
        _DestroyBinaryTree(_pRoot);
    }

    // 层序遍历
    void LevelOrder()
    {
        cout << "LevelOrder:" << endl;
        _LevelOrder(_pRoot);
        cout << endl;
    }

    //查找函数
    Node* Find(const T& value)
    {
        return _Find(_pRoot, value);
    }

    // 找一个结点的双亲结点
    Node* Parent(Node* pCur)
    {
        return _Parent(_pRoot, pCur);
    }

    //找一个结点的左孩子结点
    Node* GetLeftChild(Node* pCur)
    {
        return (pCur == NULL) ? NULL : pCur->_pLeft;
    }

    //找一个结点的右孩子结点
    Node* GetRightChild(Node* pCur)
    {
        return (pCur == NULL) ? NULL : pCur->_pRight;
    }

    //求二叉树的高度
    size_t Height()
    {
        return _Height(_pRoot);
    }

    //求二叉树叶子结点的个数
    size_t GetLeafCount()
    {
        return _GetLeafCount(_pRoot);
    }

    //求第K层结点的个数
    size_t GetKLevelCount(size_t k)
    {
        return _GetKLevelCount(_pRoot, k);
    }

    //求二叉树的镜像    (非递归)
    void BinaryMirror_Nor()
    {
        _BinaryMirror_Nor(_pRoot);
    }

    //求二叉树的镜像
    void BinaryMirror()
    {
        _BinaryMirror(_pRoot);
    }

private:
    //创建二叉树
    void _CreateBinaryTree(Node* &pRoot, const T array[], size_t size, size_t& index, const T& invalid)
    {
        if (index < size&&array[index] != invalid)
        {
            pRoot = new Node(invalid);//注意new的时候要和结构体中写的函数参数对应
            pRoot->_value = array[index];

            _CreateBinaryTree(pRoot->_pLeft, array, size, ++index, invalid);//注意:++index
            _CreateBinaryTree(pRoot->_pRight, array, size, ++index, invalid);
        }
    }

    // pRoot-->被拷贝树的根节点
    Node* _CopyBirnaryTree(Node* pRoot)
    {
        if (pRoot == NULL)
            return NULL;
        Node *NewRoot = new Node(pRoot->_value);
        NewRoot->_pLeft = _CopyBirnaryTree(pRoot->_pLeft);
        NewRoot->_pRight = _CopyBirnaryTree(pRoot->_pRight);
        return NewRoot;
    }

    //销毁二叉树
    void _DestroyBinaryTree(Node*& pRoot)
    {
        Node* temp = pRoot;
        if (temp == NULL)
            return;
        _DestroyBinaryTree(temp->_pLeft);
        _DestroyBinaryTree(temp->_pRight);
        delete temp;
        temp = NULL;
    }


    Node* _Find(Node* pRoot, const T& value)
    {
        //递归:
        //if (NULL == pRoot)//树为空,则返回空
        //  return NULL;

        //if (pRoot->_value == value)//先确定根结点是不是所要查找的值
        //  return pRoot;

        //_Find(pRoot->_pLeft, value);//再查找左右子树
        //_Find(pRoot->_pRight, value);

        //非递归:
        if (pRoot == NULL)
            return NULL;
        stack<Node*> s;
        s.push(pRoot);

        while (!s.empty())
        {
            Node *pCur = s.top();
            if (pCur->_value == value)//注意:两个value类型不一样
                return pCur;

            s.pop();//时刻记住,访问完后要出栈

            if (pCur->_pRight)//注意,入栈顺序是先右后左
                s.push(pCur->_pRight);
            if (pCur->_pLeft)
                s.push(pCur->_pLeft);
        }
    }

    //查找pCur的双亲结点
    Node* _Parent(Node* pRoot, Node* pCur)
    {
        if (NULL == pRoot)//首先确定树是不是空的
            return NULL;

        //参数验证2:判断pCur是不是为NULL   或    根结点是不是pCur(根结点是没有双亲结点的)
        if (NULL == pCur || pCur == pRoot)
            return NULL;

        if (pRoot->_pLeft)//在左子树中查找
            return _Parent(pRoot->_pLeft, pCur);

        return _Parent(pRoot->_pRight, pCur);//无论找没找到,都返回(这一步可以拆分开)
    }

    size_t _Height(Node* pRoot)
    {
        if (NULL == pRoot)//没有结点,返回0
            return 0;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
            return 1;

        //遍历左右子树,返回左右子树最大值 +1(根结点)
        return max(_Height(pRoot->_pLeft),_Height(pRoot->_pRight))+1;
    }

    size_t _GetLeafCount(Node* pRoot)
    {
        if (NULL == pRoot)
            return 0;

        //叶子结点
        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
            return 1;

        //递归:左子树的叶子结点+右子树的叶子结点
        return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
    }

    //第k层的结点个数
    size_t _GetKLevelCount(Node* pRoot, size_t k)
    {
        if (NULL == pRoot || k < 1)//注意:参数检验
            return 0;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一个结点,返回1
            return 1;

        return _GetKLevelCount(pRoot->_pLeft, k - 1) + _GetKLevelCount(pRoot->_pRight, k - 1);
    }

    void _BinaryMirror(Node* pRoot)
    {
        if (pRoot == NULL)
            return;

        if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)
            return;

        swap(pRoot->_pLeft, pRoot->_pRight);

        if (pRoot->_pLeft!=NULL)
            _BinaryMirror(pRoot->_pLeft);
        if (pRoot->_pRight!=NULL)
            _BinaryMirror(pRoot->_pRight);
    }

    void _BinaryMirror_Nor(Node* pRoot)
    {
        if (pRoot == NULL)
            return;

        queue<Node*> q;
        q.push(pRoot);

        while (!q.empty())
        {
            Node* pCur = q.front();//取队头元素

            //如果左右孩子存在,将其保存在队列中
            if (pCur->_pLeft)
                q.push(pCur->_pLeft);
            if (pCur->_pRight)
                q.push(pCur->_pRight);

            //交换左右
            swap(pCur->_pLeft, pCur->_pRight);

            //队头元素出队列
            q.pop();
        }
    }

private:
    Node* _pRoot;   // 指向树的根节点
};

void FunTest2()
{
    char* pStr = "124###35##6";
    BinaryTree<char> bt(pStr, strlen(pStr), '#');

    //cout << bt.Find('5')->_value << endl;//注意:传参的类型
    cout << bt.Parent(bt.Find('5'))->_value << endl;
    //cout << bt.Height() << endl;//高度

    //cout << bt.GetLeafCount() << endl;//叶子结点个数

    //cout << bt.GetKLevelCount(3) << endl;

    //bt.BinaryMirror();            //????
    //bt.BinaryMirror_Nor();
}


int main()
{
    FunTest2();
    return 0;
}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值