二叉树相关面试题

一、什么是二叉树

在计算机科学中,二叉树是每个节点最多有两个子树的树结构,通常子树被称为左子树和右子树,左右子树又是一个二叉树,其次序不能任意颠倒。

二、二叉树的相关操作

1、二叉树结点的定义

二叉树的存储结构一般采用二叉链表的方式,每一个结点都有一个数据域_data和指向左右子树的两个指针域,通过这两个指针域就可以建立二叉树中的上下层关系。下面就是二叉树结点的定义:

template<class T>
struct BinTreeNode
{

    BinTreeNode()
    {}
    BinTreeNode(T data) //构造函数
        :_left(NULL)
        , _right(NULL)
        , _data(data)
    {}
    BinTreeNode*_left; //左指针域
    BinTreeNode*_right; //右指针域
    T _data;  //数据域
};

2、二叉树的创建

首先我们要创建一个形如下面的二叉树,对应的输入数组为“char a[]=”ABD###CE##F”,即按照先序遍历的方法实现,’#’代表为空:
创建示例
代码实现模块:

     // 根+左子树+右子树 
Node* _CreateBinTree( T* array, size_t size, size_t& index, const T& invalid)
{
        Node *_pRoot = NULL;
        if (invalid == array[index])//判断根节点是否为空
            return _pRoot;
        if (index < size && array[index] != invalid)
        {
             _pRoot = new BinTreeNode<T>(array[index]);//创建根节点
            _pRoot->_left = _CreateBinTree(array, size, ++index, invalid);//创建左子树
            _pRoot->_right = _CreateBinTree(array, size, ++index, invalid);//创建右子树
        }
        return  _pRoot;
}

3、二叉树遍历

(1) 先序遍历(递归实现):根节点–>左子树—>右子树

    void PreOrder()
    {
        _PreOrder(_pRoot);
    }
    void _PreOrder(PNode _pRoot)
    {
        if (NULL == _pRoot)//判空
            return;
        else
        {
            cout << _pRoot->_data << " ";//访问根节点
            _PreOrder(_pRoot->_left);//遍历左子树
            _PreOrder(_pRoot->_right);//遍历右子树
        }

非递归实现先序遍历:

   void PreOrder_Nor()//先序遍历--循环
    {
        if (NULL == _pRoot)//判断树是否为空
            return;
        stack<PNode>s;
        s.push(_pRoot);  //根节点入栈
        while (!s.empty())
        {
            PNode pCur = s.top();//取栈顶元素并出栈
            s.pop();
            while (pCur)
            {
                cout << pCur->_data << " ";//访问当前结点
                if (pCur->_right)  
                    s.push(pCur->_right);//保存当前结点的右子树

                pCur = pCur->_left;//保存当前结点的左子树
            }
        }
        cout << endl;
    }   

(2)中序遍历递归实现:左子树–>访问根节点–>右子树

    void InOrder()
    {
         _InOrder(_pRoot);
    }
    void _InOrder(PNode _pRoot)
    {
        if (NULL == _pRoot)
            return;
        else
        {
            _InOrder(_pRoot->_left);//遍历左子树
            cout << _pRoot->_data << " ";//访问根节点
            _InOrder(_pRoot->_right);//遍历右子树
        }
    }

非递归实现:

     void InOrder_Nor()
    {
        if (NULL == _pRoot)
            return;
        stack<PNode>s;
        PNode pCur = _pRoot;
        while (pCur || !s.empty())
        {
            //保存左侧路径的所有节点
            while (pCur)
            {
                s.push(pCur);
                pCur = pCur->_left;
            }
            pCur = s.top();//取栈顶元素并访问
            cout << pCur->_data << " ";
            s.pop();

            pCur = pCur->_right;//如果pCur->_left为空,将pCur->_right作为当前节点,重复以上操作
        }
        cout << endl;
    }

(3)后序遍历递归实现:左子树–>右子树–>根节点

void PostOrder()
    {
        _PostOrder(_pRoot);
    }
    void _PostOrder(PNode _pRoot)//后序遍历
    {
        if (NULL == _pRoot)//判空
            return;
        else
        {
            _PostOrder(_pRoot->_left);//先遍历左子树
            _PostOrder(_pRoot->_right);//遍历右子树
            cout << _pRoot->_data << " ";//打印根节点
        }
    }   

非递归实现:

    void PostOrder_Nor()
    {
        stack<PNode>s;//存放所有节点
        PNode pCur = _pRoot;
        PNode prev = NULL;
        while (pCur || !s.empty())
        {
            while (pCur&&pCur != prev)  //存放左侧路径所有节点
            {
                s.push(pCur);            //当左子树未被标记时入栈
                pCur = pCur->_left;
            }
            PNode pTop = s.top();
            if (NULL == pTop->_right || prev == pTop->_right)//如果栈顶元素的右子树为空,并且已经被访问过
            {
                cout << pTop->_data << " ";          //则输出该栈顶元素并出栈
                prev = pTop;             
                s.pop();
                if (!s.empty())
                    pCur = s.top();
                else
                    return; //当栈为空时,退出
            }
            pCur = pCur->_right; 
        }
    }   

(4)层序遍历

需要借助队列先进先出的特性,主要有以下几个步骤:
a、定义一个队列用于存储根节点的指针
b、将根节点的指针入队列
c、当队列非空时:取队列队头结点并出队列,若该节点的左子树非空,该节点左子树入队列,若该节点的右子树非空,则该节点右子树入队列。

        void _LevelOrder(PNode _pRoot)
        {
        queue<Node *> q;
        q.push(_pRoot);
        while (!q.empty())
        {
            Node*pCur = q.front();//取栈顶元素并访问
            cout << pCur->_data << " ";
            if (NULL == _pRoot)
                return;
            if (pCur->_left)//左子树入栈
            {
                q.push(pCur->_left);
            }
            if (pCur->_right)//右子树入栈
                q.push(pCur->_right);
            q.pop();//每当被访问过就pop出栈
        }
    }           

4、判断是否为完全二叉树

由完全二叉树的定义可知,完全二叉树是指:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
代码实现过程如下:

        bool IsCompleteBInTree()
        {
        if (NULL == _pRoot)//如果是空树,返回true
            return true;
        if (_pRoot->_left == NULL&&_pRoot->_right == NULL)
            return true;
        queue<PNode>q;
        q.push(_pRoot);
        bool flag = false;  //用来标记是否需要对结点进行处理
        while (!q.empty())
        {
            if (flag)
            {
                while (!q.empty())
                {
                    PNode Front = q.front();
                    if (Front->_left == NULL&&Front->_right == NULL)// 若其他节点都为空,则
                    {
                        q.pop();
                    }
                    else
                        return false;
                }
            }
            else
            {
                PNode Front = q.front();
                q.pop();
                if (Front->_left && Front->_right)//左右节点都不为空
                {
                    q.push(Front->_left);
                    q.push(Front->_right);
                }
                else if (Front->_left)//左结点不为空,右节点为空
                {
                    flag = true;
                }
                else if (Front->_right)//右节点不为空,左结点为空
                    return false;
                else      //左右节点都为空
                {

                    flag = true;
                }
            }

        }
        return true;
    }   

5、二叉树的镜像转换

镜像转换即就是交换二叉树的左右子树,实现代码如下:
递归实现:
PNode MirrorTree()
{
return _MirrorTree(_pRoot);
}

    PNode _MirrorTree(PNode pRoot)//镜像转换
    {
        if (NULL == pRoot)
            return NULL;
        if (pRoot->_left == NULL&&pRoot->_right == NULL)
            return pRoot;
        else
        {
            swap(pRoot->_left, pRoot->_right);
            _MirrorTree(pRoot->_left);//递归镜像左子树
            _MirrorTree(pRoot->_right);//递归镜像右子树
        }
    }       

非递归实现(借助栈实现):

void _MirrorTree_Nor()
    {
        if (NULL == _pRoot)
            return;
        stack<PNode> q;
        q.push(_pRoot);
        while (!q.empty())
        {
            PNode pTop = q.top();
            q.pop();
            swap(pTop->_left, pTop->_right);//交换左右子树
            if (pTop->_left)//遍历左子树
                q.push(pTop->_left);
            else if (pTop->_right)//遍历右子树
                q.push(pTop->_right);
        }
    }   

6、求二叉树叶子节点个数

递归实现:左子树叶子结点个数+右子树叶子结点个数

    size_t GetLeefCount()//求叶子节点个数
    {
        return  _GetLeefCount(_pRoot);
    }
    size_t _GetLeefCount(PNode _pRoot)
    {
        if (NULL == _pRoot)
            return 0;
        if (_pRoot->_left == NULL&&_pRoot->_right == NULL)
            return 1;
        return  _GetLeefCount(_pRoot->_left) +  _GetLeefCount(_pRoot->_right);
    }   

7、获取第K层节点个数

    size_t GetKLevelCount(size_t K)
    {
        size_t index = 1;
        return _GetKLevelCount(_pRoot,K, index);
    }
    size_t _GetKLevelCount(PNode pRoot,size_t K, size_t index)
    {

        if (NULL == pRoot)
            return 0;
        if (index < K - 1)
        {
            return _GetKLevelCount(pRoot->_left, K, index + 1) + _GetKLevelCount(pRoot->_right, K, index + 1);
        }
        if (index == K - 1)
        {
            return (pRoot->_left!=NULL) + (pRoot->_right!=NULL);
        }
        return 0;
    }   

8、求二叉树的深度

深度为左右子树深度最大值+1

    size_t Height()
    {
        return _Height(_pRoot);
    }
    size_t _Height(PNode pRoot)
    {
        if (NULL == pRoot)
            return 0;
        return _Height(pRoot->_left) > _Height(pRoot->_right) ? _Height(pRoot->_left) + 1 : _Height(pRoot->_right) + 1;
    }   

9、查找指定数据

    PNode Find(const T&data)
    {
        return _Find(_pRoot, data);
    }
    PNode _Find(PNode pRoot,const T& data)
    {
        if (NULL == pRoot)
            return NULL;

            if (pRoot->_data == data)
                return pRoot;
            //在左子树中进行查找
            Node * ret = _Find(pRoot->_left,data);
            if (ret != NULL)
                return ret;
            //在右子树中进行查找
            else
            {
                Node*cur=_Find(pRoot->_right,data);
                if (cur != NULL)
                    return cur;
            }
            return NULL;
    }   

10、查找双亲节点

若树为空或只有一个节点—>返回NULL;
否则遍历左子树查找;
遍历右子树查找;
若左右子树都没有要查找的数据,返回空;

    PNode Parent(PNode pNode)
    {
        return _Parent(_pRoot, pNode);
    }
    PNode _Parent(PNode pRoot, PNode pNode)//查找双亲节点
    {
        if (NULL == pRoot)
            return NULL;
        if (pRoot->_left == pNode || pRoot->_right == pNode)
            return pRoot;
        else
        {
            Node*tmp = NULL;
            if (tmp = _Parent(pRoot->_left, pNode))
                return tmp;
            if (tmp = _Parent(pRoot->_right, pNode))
                return tmp;
        }
    }   

11、查找指定节点的左孩子

PNode _LeftChild(PNode pNode)
{
    if (NULL == pNode)
        return;
    return pNode->_left;
}   

12、查找指定节点的右孩子

PNode _RightChild(PNode pNode)
{
    if (NULL == pNode)
        return;
    return pNode->_right;
}

13、二叉树的复制

拷贝根节点
拷贝左子树
拷贝右子树

PNode _CopyBinTree(PNode pRoot)
{
    if (NULL == pRoot)
        return NULL;
    //拷贝根节点
    Node* pNewNode = new Node(pRoot->_data);
    //拷贝左子树
    if (pRoot->_left)
    pNewNode->_left = _CopyBinTree(pRoot->_left);
    //拷贝右子树
    if (pRoot->_right)
    pNewNode->_right = _CopyBinTree(pRoot->_right);

    return pNewNode;
}

14、二叉树的销毁

void _DestroyBinTree(PNode pRoot)
{
    while(pRoot != NULL)
    {
        if (pRoot->_left)
        {
            _DestroyBinTree(pRoot->_left);
            pRoot->_left = NULL;
        }
        if (pRoot->_right)
        {
            _DestroyBinTree(pRoot->_right);
            pRoot->_right = NULL;
        }
        if (pRoot != NULL)
        {
            free(pRoot);
            pRoot = NULL;
        }
    }   
}

15、测试代码

int main()
{
char a[] = "ABD###CE##F";
//char a[] = "ABDE#####CF";

BinTree<char> s(a,strlen(a),'#');
//s.InOrder();
//s.InOrder_Nor();
/*s.PreOrder();
s.PreOrder_Nor();*/
//s.PostOrder();
//s.PostOrder_Nor();
/*int ret = 0;
ret = s.IsCompleteBInTree();
if (ret == 0)
    cout << "不是完全二叉树" << endl;
else
    cout << "是完全二叉树" << endl;*/
/*s.PreOrder();
cout << endl;
s.MirrorTree();*/

s.PreOrder();
cout << endl;
s._MirrorTree_Nor();
s.PreOrder();
/*BinTree<char> s2(s);
BinTree<char>s1 = s;
s1.PreOrder();
s2.PreOrder();*/
//s.Leve1Order();
/*cout << endl;
cout << s.Size() << endl;
cout << s.GetLeefCount() << endl;
cout << s.GetKLevelCount(3) << endl;
cout << s.Height() << endl;
cout << s.Find('B')->_data<<endl;*/
/*cout<<s.Parent(s.Find('B'))->_data<<endl;
cout << s.LeftChild(s.Find('A'))->_data;*/
//BinTree<char>*cur = s.Parent()

system("pause");
return 0;

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值