二叉树相关面试题汇总

说明:本博文在以前面试题的基础上汇总了二叉树的常见面试题。

二叉树中封装的功能有:

    void _CreateTree(Node*& pRoot, const T array[], size_t size, size_t& index, const T& invalid);//创建树

    Node* _CopyBinaryTree(Node* pRoot);//拷贝树

    void _Destroy(Node*& pRoot);//清空树

    void _PreOrder(Node* pRoot);//递归实现:先序遍历
    void _PreOrder_nor(Node* pRoot);//非递归实现:先序遍历

    void _InOrder(Node* pRoot);//递归实现:中序遍历
    void _InOrder_nor(Node* pRoot);//非递归实现:中序遍历

    void _PostOrder(Node* pRoot);//递归实现:后序遍历
    void _PostOrder_nor(Node* pRoot);//非递归实现:后序遍历

    void _LevelOrder(Node* pRoot);//层序遍历

    Node* _GetParent(Node* pRoot, Node* x);//获取双亲结点

    Node* _Find(Node* pRoot, const T& value);//找到值为value的结点

    size_t _Height(Node* pRoot);//获取树的高度

    size_t _GetLeefNode(Node* pRoot);//获取叶子结点个数

    size_t _GetKLevelNode(Node* pRoot, size_t k);//获取某一层结点个数

    void _GetBinaryMirror(Node* pRoot);//递归实现:将二叉树变为其镜像

    bool IsCompleteBinaryTree();//判断二叉树树是否是完全二叉树

二叉树信息:

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

template <typename T>
struct BinaryTreeNode  //节点信息
{
    BinaryTreeNode(const T& data)
        :m_data(data)
        , m_pLeft(nullptr)
        , m_pRight(nullptr)
    {}
    T m_data;
    BinaryTreeNode<T>* m_pLeft;//左孩子
    BinaryTreeNode<T>* m_pRight;//右孩子
};


template <typename T>
class BinaryTree//将成员函数的实现进行简单的封装,用户只能显示调用公共接口
{
    typedef BinaryTreeNode<T> Node;
public:
    BinaryTree()//无参构造函数
        :m_pRoot(nullptr)
    {}
    BinaryTree(const T array[], size_t size, const T& invalid)//带参构造函数
    {
        size_t index = 0;
        _CreateTree(m_pRoot, array, size, index, invalid);
    }
    BinaryTree(const BinaryTree<T>& t)//拷贝构造函数
    {
        m_pRoot = _CopyBinaryTree(t.m_pRoot);
    }
    BinaryTree<T>& operator=(const BinaryTree<T>& t)//赋值运算符重载成员函数
    {
        _Destroy(m_pRoot);
        m_pRoot = _CopyBinaryTree(t.m_pRoot);
        return *this;
    }
    ~BinaryTree()//析构函数
    {
        _Destroy(m_pRoot);
    }
    void PreOrder()//前序遍历
    {
        cout << "前序遍历:";
        _PreOrder_nor(m_pRoot);
        cout << endl;
    }
    void InOrder()//中序遍历
    {
        cout << "中序遍历";
        _InOrder_nor(m_pRoot);
        cout << endl;
    }
    void PostOrder()//后序遍历
    {
        cout << "后序遍历";
        _PostOrder_nor(m_pRoot);
        cout << endl;
    }
    void LevelOrder()//层序遍历
    {
        cout << "层序遍历";
        _LevelOrder(m_pRoot);
        cout << endl;
    }

    Node* GetParent(Node* x)//获取该结点的双亲结点
    {
        return _GetParent(m_pRoot, x);
    }

    Node* Find(const T& value)//找到值为value的结点
    {
        return _Find(m_pRoot, value);
    }

    Node* GetLeftChild(Node* pCur)//返回左孩子
    {
        if (pCur)
            return pCur->m_pLeft;
        return nullptr;
    }

    Node* GetRightChild(Node* pCur)//返回右孩子
    {
        if (pCur)
            return pCur->m_pRight;
        return nullptr;
    }

    size_t Height()//获取树的高度
    {
        return _Height(m_pRoot);
    }

    size_t GetLeefNode()//获取叶子结点个数
    {
        return _GetLeefNode(m_pRoot);
    }

    size_t GetKLevelNode(size_t k)//获取某一层结点个数
    {
        return _GetKLevelNode(m_pRoot, k);
    }

    void GetBinaryMirror_Nor();// 将二叉树变为其镜像:非递归

    void GetBinaryMirror()  // 将二叉树变为其镜像:递归版本
    {
        _GetBinaryMirror(m_pRoot);
    }

    // 利用层序遍历来处理--> 关键:找第一个度不为2的结点-->后续结点
    // 如果有孩子则不是完全二叉树
    // 否则:是
    bool IsCompleteBinaryTree();

private:
    void _CreateTree(Node*& pRoot, const T array[], size_t size, size_t& index, const T& invalid);//创建树

    Node* _CopyBinaryTree(Node* pRoot);//拷贝树

    void _Destroy(Node*& pRoot);//清空树

    void _PreOrder(Node* pRoot);//递归实现:先序遍历
    void _PreOrder_nor(Node* pRoot);//非递归实现:先序遍历

    void _InOrder(Node* pRoot);//递归实现:中序遍历
    void _InOrder_nor(Node* pRoot);//非递归实现:中序遍历

    void _PostOrder(Node* pRoot);//递归实现:后序遍历
    void _PostOrder_nor(Node* pRoot);//非递归实现:后序遍历

    void _LevelOrder(Node* pRoot);//层序遍历

    Node* _GetParent(Node* pRoot, Node* x);//获取双亲结点

    Node* _Find(Node* pRoot, const T& value);//找到值为value的结点

    size_t _Height(Node* pRoot);//获取树的高度

    size_t _GetLeefNode(Node* pRoot);//获取叶子结点个数

    size_t _GetKLevelNode(Node* pRoot, size_t k);//获取某一层结点个数

    void _GetBinaryMirror(Node* pRoot);//递归实现:将二叉树变为其镜像

private:
    BinaryTreeNode<T>* m_pRoot;//根结点
};

利用数组,创建一个二叉树(比如“124#7#35##68#”表示先序遍历顺序,#用来表示空结点)
这里写图片描述

template <typename T>//创建树
void BinaryTree<T>::_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->m_pLeft, array, size, ++index, invalid);
        _CreateTree(pRoot->m_pRight, array, size, ++index, invalid);
    }
}

拷贝一个二叉树

template <typename T>//拷贝树
BinaryTreeNode<T>* BinaryTree<T>::_CopyBinaryTree(Node* pRoot)
{
    Node* pNewRoot = nullptr;
    if (pRoot)
    {
        pNewRoot = new Node(pRoot->m_data);
        pNewRoot->m_pLeft = _CopyBinaryTree(pRoot->m_pLeft);
        pNewRoot->m_pRight = _CopyBinaryTree(pRoot->m_pRight);
    }
    return pNewRoot;
}

销毁一颗二叉树

template <typename T>
void BinaryTree<T>::_Destroy(Node*& pRoot)//清空树
{
    if (pRoot)
    {
        _Destroy(pRoot->m_pLeft);
        _Destroy(pRoot->m_pRight);
        delete pRoot;
        pRoot = nullptr;
    }
}

递归实现前序遍历

template <typename T>
void BinaryTree<T>::_PreOrder(Node* pRoot)//递归实现:前序遍历
{
    if (pRoot)
    {
        cout << pRoot->m_data << " ";
        _PreOrder(pRoot->m_pLeft);
        _PreOrder(pRoot->m_pRight);
    }
}

非递归实现前序遍历

template <typename T>
void BinaryTree<T>::_PreOrder_nor(Node* pRoot)//非递归实现:前序遍历(利用栈实现)
{
    if (nullptr == pRoot)//空树
        return;
    stack<Node*> s;
    s.push(pRoot);

    while (!s.empty())
    {
        Node* pTemp = s.top();
        cout << pTemp->m_data << " ";
        s.pop();//pop放在push之前,由于栈的LIFO特性

        if (pTemp->m_pRight)
            s.push(pTemp->m_pRight);//一定要先存入右子树,后存入左子树,因为栈的LIFO特性
        if (pTemp->m_pLeft)
            s.push(pTemp->m_pLeft);
    }
}

递归实现中序遍历

template <typename T>
void BinaryTree<T>::_InOrder(Node* pRoot)//递归实现:中序遍历
{
    if (pRoot)
    {
        _InOrder(pRoot->m_pLeft);
        cout << pRoot->m_data << " ";
        _InOrder(pRoot->m_pRight);
    }
}

非递归实现中序遍历

template <typename T>
void BinaryTree<T>::_InOrder_nor(Node* pRoot)//非递归实现:中序遍历(1,找到树最左边的节点,并保存路径 2,访问当前节点,并将其右子树作为根节点。3,重复12)
{
    if (nullptr == pRoot)
        return;

    stack<Node*> s;
    Node* pCur = pRoot;

    while (!s.empty() || pCur)//注意:后面的条件,因为是中序遍历,当根节点出栈之后,还需遍历右子树
    {
        while (pCur)//找到最左边的叶子结点,将路径上结点入栈
        {
            s.push(pCur);
            pCur = pCur->m_pLeft;
        }

        Node* pTemp = s.top();
        cout << pTemp->m_data << " ";
        s.pop();

        pCur = pTemp->m_pRight;//进入右子树
    }
}

递归实现后序遍历

template <typename T>
void BinaryTree<T>::_PostOrder(Node* pRoot)//后序遍历
{
    if (pRoot)
    {
        _PostOrder(pRoot->m_pLeft);
        _PostOrder(pRoot->m_pRight);
        cout << pRoot->m_data << " ";
    }
}

非递归实现后序遍历

template <typename T>
void BinaryTree<T>::_PostOrder_nor(Node* pRoot)//非递归实现:后序遍历
{/*顺序:总是:左-->右-->根
 1,找到最左边的叶子结点
 2,访问最左边的结点(即栈顶结点)
 3,若栈顶元素的兄弟结点为空,或者不为空但已经访问过了(不为空时,采用标记避免陷入死循环(一直保存右孩子)中),就输出栈顶结点值
 4,否则,进入栈顶元素的兄弟结点所在树中,进行1-2步迭代
 */
    if (nullptr == pRoot)//空树
        return;
    stack<Node*> s;
    Node* pCur = pRoot;//当前树的根结点
    Node* pPre = nullptr;//最近一次访问的节点
    while (!s.empty() || pCur)//树不为空,或者树为空时根结点不为空(即还没有将节点放入栈中)
    {
        while (pCur)//找到该树最左边的叶子结点,并且保存路径
        {
            s.push(pCur);
            pCur = pCur->m_pLeft;//一直找左孩子。
        }

        pCur = s.top();//取出栈顶元素

        if ((nullptr == pCur->m_pRight) || (pPre == pCur->m_pRight))//若栈顶元素的右孩子为空,或者栈顶元素的右孩子孩子不为空并且等于刚才输出的节点(避免陷入死循环:一直保存右孩子)
        {
            cout << pCur->m_data << " ";//打印根结点的前提是:右子树为空
            s.pop();
            pPre = pCur;
            pCur = nullptr;//置为空,使得下次迭代中,跳出寻找那次迭代中的树的最左边结点,避免陷入死循环(一直保存左孩子)
        }
        else
            pCur = pCur->m_pRight;
    }
}

实现层序遍历

template <typename T>
void BinaryTree<T>::_LevelOrder(Node* pRoot)//层序遍历(利用队列实现)
{   /*
    画图理解:
    1,先将根节点入队列,
    2,进入循环,队首结点出队列,输出其值。
    3,将其左右孩子节点入队列,
    4,进行2-3循环,直到队列为空
    */
    if (!pRoot)
        return;

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

    while (!q.empty())
    {
        Node* pCur = q.front();
        q.pop();
        cout << pCur->m_data << " ";

        if (pCur->m_pLeft)
            q.push(pCur->m_pLeft);

        if (pCur->m_pRight)
            q.push(pCur->m_pRight);
    }
}

递归实现获取双亲结点

template <typename T>
BinaryTreeNode<T>* BinaryTree<T>::_GetParent(Node* pRoot, Node* x)//递归实现:获取双亲结点
{
    if ((nullptr == pRoot) || (pRoot == x))//空树或者该结点是根结点,没有双亲结点,返回nullptr
        return nullptr;

    if ((pRoot->m_pLeft == x) || (pRoot->m_pRight == x))//若x是根结点个左右孩子,返回该根结点
        return pRoot;
    //以上两条判断语句是递归出口

    //利用递归,将问题变为在左右子树中寻找x的双亲结点
    Node* parent = nullptr;
    if (parent = _GetParent(pRoot->m_pLeft, x))//左子树中寻找
        return parent;
    if (parent = _GetParent(pRoot->m_pRight, x))//右子树中寻找
        return parent;

    return nullptr;//在左右子树中未找到x的双亲结点。
}

递归实现查找某一值

template <typename T>
BinaryTreeNode<T>* BinaryTree<T>::_Find(Node* pRoot, const T& value)//递归实现:找到值为value的结点
{
    if (nullptr == pRoot)//空树或空结点
        return nullptr;

    if (pRoot->m_data == value)
        return pRoot;
    //以上两条语句是递归出口

    //利用递归,将问题变为在左右子树中寻找值为value的节点
    Node* p_node = nullptr;
    if (p_node = _Find(pRoot->m_pLeft, value))//左子树中寻找
        return p_node;
    if (p_node = _Find(pRoot->m_pRight, value))//右子树中寻找
        return p_node;

    return nullptr;//在左右子树中未找到值为value的结点。
}

递归实现获取树的高度

template <typename T>
size_t BinaryTree<T>::_Height(Node* pRoot)//递归实现:获取树的高度
{
    if (nullptr == pRoot)//空树或空节点
        return 0;
    //以上语句为递归出口

    //利用递归:获取左右子树的高度,并将左右子树的高度中较大的高度加1,返回(当前树的pRoot不为空,所以加1)
    size_t h_left = _Height(pRoot->m_pLeft);//获得左子树高度
    size_t h_right = _Height(pRoot->m_pRight);//获得右子树高度

    return h_left > h_right ? h_left + 1 : h_right + 1;//返回树的高度
}

递归实现获取叶子结点个数

template <typename T>
size_t BinaryTree<T>::_GetLeefNode(Node* pRoot)//递归实现:获取叶子结点个数
{
    if (nullptr == pRoot)//空树或空结点
        return 0;
    if ((nullptr == pRoot->m_pLeft) && (nullptr == pRoot->m_pRight))//当前节点时叶子结点
        return 1;
    //以上两个判断语句是递归出口

    //利用递归,获取左右子树的叶子结点个数
    size_t leaves_left = _GetLeefNode(pRoot->m_pLeft);//获取左子树叶子结点数
    size_t leaves_right = _GetLeefNode(pRoot->m_pRight);//获取右子树叶子结点数

    return leaves_left + leaves_right;//返回左右子树叶子结点之和
}

递归实现获取第K层节点个数

template <typename T>
size_t BinaryTree<T>::_GetKLevelNode(Node* pRoot, size_t k)//递归实现:获取某一层结点个数
{
    if ((nullptr == pRoot) || (1 > k))//空树或空结点、k小于1
        return 0;
    if (1 == k)//k=1表示当前结点
        return 1;
    //以上两个判断语句是递归出口

    //利用递归,获取左右子树中处于k-1层结点个数
    size_t nodes_left = _GetKLevelNode(pRoot->m_pLeft, k - 1);//获取左子树中k-1层结点个数
    size_t nodes_right = _GetKLevelNode(pRoot->m_pRight, k - 1);//获取右子树中k-1层结点个数

    return nodes_left + nodes_right;
}

非递归获取二叉树的镜像

template <typename T>
void BinaryTree<T>::GetBinaryMirror_Nor()// 将二叉树变为其镜像:非递归
{
    if (nullptr == m_pRoot)//空树
        return;

    //利用层序遍历的思想,将每一层中,每一个节点按顺序放入队列中,然后改变其左右孩子的位置
    queue<Node*> q;
    Node* pCur = m_pRoot;
    q.push(pCur);
    while (!q.empty())
    {
        pCur = q.front();
        q.pop();//由于是队列,对于出队列和入队列的顺序没有要求

        if (pCur->m_pLeft || pCur->m_pRight)//如果当前节点不是叶子结点(即:有左右孩子)
        {
            std::swap(pCur->m_pLeft, pCur->m_pRight);//交换左右孩子

                                                     //将存在的左孩子或右孩子入队列
            if (pCur->m_pLeft)
                q.push(pCur->m_pLeft);
            if (pCur->m_pRight)
                q.push(pCur->m_pRight);
        }
    }
}

递归获取二叉树的镜像

template <typename T>
void BinaryTree<T>::_GetBinaryMirror(Node* pRoot)   // 将二叉树变为其镜像:递归版本
{
    if (nullptr == pRoot)//空树
        return;

    if ((nullptr == pRoot->m_pLeft) && (nullptr == pRoot->m_pRight))//如果_是叶子结点
        return;
    //以上两条判断语句是递归出口

    std::swap(pRoot->m_pLeft, pRoot->m_pRight);//交换左右孩子

    //将存在的左右孩子树,作为新树,执行相同功能
    //if (pRoot->m_pLeft),不加判空语句,在下次递归中会判空。
    _GetBinaryMirror(pRoot->m_pLeft);
    //if (pRoot->m_pRight),不加判空语句,在下次递归中会判空。
    _GetBinaryMirror(pRoot->m_pRight);
}

判断一个树是否是完全二叉树

/*
由于完全二叉树的规律,可以使用以下解法。
1,利用层序遍历的顺序找到第一个度为0或度为1(只有左子树,没有右子树)的结点。(关键)
2,若在层序遍历中,该结点之后存在某个结点拥有孩子,则不是完全二叉树。
3,否则,是完全二叉树。
*/
template <typename T>
bool BinaryTree<T>::IsCompleteBinaryTree()
{
    if (nullptr == m_pRoot)
        return true;//空树也是完全二叉树

    queue<Node*> q;
    Node* pCur = m_pRoot;
    q.push(pCur);
    bool flag = false;//flag表示是否找到了满足条件的节点

    while (!q.empty())
    {
        pCur = q.front();
        q.pop();

        if (!flag && pCur->m_pLeft && pCur->m_pRight)//flag=false且该结点度为2
        {
            q.push(pCur->m_pLeft);
            q.push(pCur->m_pRight);
        }
        else if ((nullptr == pCur->m_pLeft) && pCur->m_pRight)//该结点只有右子树,没有左子树,不是完全二叉树
        {
            return false;
        }
        else if (flag && (pCur->m_pLeft || pCur->m_pRight))//flag=true且该结点有孩子节点,不是完全二叉树
        {
            return false;
        }
        else//剩余情况,度为0,度位1(只有左子树,没有右子树,另外一种情况在if_2中排除)。(度为2情况在if_1, if_3中已经排除)。
            flag = true;//找到满足条件的结点,并且标记已找到。
    }

    return true;//遍历二叉树过程中,没有提前退出,表示是一个完全二叉树
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章