二叉树常见面试题汇总

来源:互联网 发布:金融职位人数 知乎 编辑:程序博客网 时间:2024/05/22 05:11

1.先序/中序/后序遍历二叉树(递归非递归实现),层序遍历二叉树遍历

2.求二叉树的高度

size_t Height()//获取该树的高度    {        return _Height(_pRoot);    }    size_t _Height(Node* pRoot)//获取数树的深度,根节点为1    {        if(pRoot==NULL)            return 0;        if(pRoot->_pLeft==NULL && pRoot->_pRight==NULL)            return 1;        size_t LeftHeight = _Height(pRoot->_pLeft);        size_t RightHeight = _Height(pRoot->_pRight);        if(LeftHeight > RightHeight)            return LeftHeight+1;        else            return RightHeight+1;    }

3.叶子节点的个数

size_t GetLeefNode()//获取叶子节点的个数(左子树叶子节点加上右子树的叶子节点)    {         return _GetLeefNode(_pRoot);    }    size_t _GetLeefNode(Node* pRoot)//获取叶子节点的个数---递归左子树叶子节点加上右子树的叶子节点数    {        if(pRoot==NULL)            return 0;        if(pRoot->_pLeft==NULL || pRoot->_pRight==NULL)            return 1;        size_t pLeftLeefNode = _GetLeefNode(pRoot->_pLeft);        size_t pRightLeefNode = _GetLeefNode(pRoot->_pRight);        return pLeftLeefNode+pRightLeefNode;    }

4.求第k层节点的个数

size_t GetKLevelNode(const size_t& k)//获取第K层节点总数    {        return _GetKLevelNode(_pRoot,k);    }size_t _GetKLevelNode(Node* pRoot,size_t k)//获取第k层节点个数--递归K-1层节点数    {        if(pRoot==NULL || k<=0 || k>_Height(pRoot))            return 0;        if(k == 1)            return 1;        size_t LeftKLevelNode = _GetKLevelNode(pRoot->_pLeft,k-1);        size_t RightKLevelNode = _GetKLevelNode(pRoot->_pRight,k-1);        return LeftKLevelNode+RightKLevelNode;    }

2、3、4这三道常见面试题都是采用根据先序遍历的方法,来对节点进行递归操作

5.树中两个节点的最低公共祖先
思考分析:
该题根据题目给出的条件不同,将会有不同的简便算法;
1>.该树是二叉收索树。
二叉收索树的左孩子小于根节点,根节点小于右孩子,所 以我们可以直接根据节点的值,对左右子树进行判断,从而得到结果
2>.该树是普通树,但有pParent指针。此时我们可以根据pPaent指针将该问题转化为求相交两个链表第一个相交的节点
3>.该树是普通的树,不含pParent指针

我们今天就介绍普通的树,有三种得到最低公共祖先的方法

  • 从根节点开始遍历节点,判断所求节点是否在其左右子树,若在,继续向下遍历,否则,根节点就是它的双亲节点
    缺点:重复遍历节点,时间开销大
//树中两个节点的最低公共祖先(实现2---利用先序遍历每一个节点,并判断两个节点是否在该节点的左右分支上,在,则该节点即是最低公共祖先,否则,继续遍历查找)    Node* GetLastCommonParent2(Node* pNode1,Node* pNode2)    {        return _GetLastCommonParent2(_pRoot,pNode1,pNode2);    }    //树中两个节点的最低公共祖先---实现2(重复遍历节点并进行判断,该方法的时间消耗大)    Node* _GetLastCommonParent2(Node* pRoot,Node* pNode1,Node* pNode2)    {        if(pRoot==NULL || pNode1==NULL ||pNode2==NULL)            return NULL;        Node* pCur = NULL;        if(IsFindNode(pRoot,pNode1) && IsFindNode(pRoot,pNode2))//若在根为pRoot的子树中查找到pNode和pNode2,则继续向下寻找,若找不到,二叉树的根为最低祖先        {            if(pCur=_GetLastCommonParent2(pRoot->_pLeft,pNode1,pNode2))                return pCur;            if(pCur=_GetLastCommonParent2(pRoot->_pRight,pNode1,pNode2))                return pCur;            return pRoot;        }        return NULL;    }
  • 普通树,用辅助内存栈实现,查找两个栈中最后一个相同的元素
Node* _GetLastCommonParent1(Node* pRoot,Node* pNode1,Node* pNode2)    {        if(pRoot == NULL || pNode1==NULL || pNode2==NULL)            return NULL;        stack<Node*> s1;        stack<Node*> s2;        //调用查找该节点并保存路径的函数        bool flags = false;        FindNodeAndPush(pRoot,pNode1,s1,flags);        flags = false;        FindNodeAndPush(pRoot,pNode2,s2,flags);        //进行出栈操作,找到最低公共祖先        while(!s1.empty() && !s2.empty())        {            if(s1.size()>s2.size())                s1.pop();            else if(s1.size() < s2.size())                s2.pop();            else            {                while(s1.top() != s2.top())                {                    s1.pop();                    s2.pop();                }                return s1.top();            }        }        return NULL;    }    void FindNodeAndPush(Node* pRoot,Node* pNode,stack<Node*> &s,bool& flags)//从根节点开始查找该节点并保存路径到栈    {        if(pRoot==NULL || pNode == NULL)            return;        s.push(pRoot);        if(pRoot == pNode)        {            flags = true;            return;        }        if(!flags)            FindNodeAndPush(pRoot->_pLeft,pNode,s,flags);        if(!flags)            FindNodeAndPush(pRoot->_pRight,pNode,s,flags);        if(!flags)        {            s.pop();            return;        }        return;    }
  • 利用辅助空间链表,将问题转化为求两个链表的最后一个相交的节点
Node* _GetLastCommonParent3(Node* pRoot,Node* pNode1,Node* pNode2)    {        if(pRoot==NULL || pNode1==NULL ||pNode2==NULL)            return NULL;        list<Node*> l1;        list<Node*> l2;        //从根节点开始查找节点并保存路径        bool flags = false;        FindNodeAndRemPath(pRoot,pNode1,l1,flags);        flags = false;        FindNodeAndRemPath(pRoot,pNode2,l2,flags);        //求两个相交链表中最后一个相交的节点        return FindCrossPointOfTwoList(l1,l2);    }    void FindNodeAndRemPath(Node* pRoot,Node* pNode,list<Node*>& l,bool& flags)//从根节点开始查找节点并保存路径    {        if(pRoot==NULL)            return;        l.push_back(pRoot);        if(pRoot == pNode)        {            flags = true;            return;        }        if(!flags)            FindNodeAndRemPath(pRoot->_pLeft,pNode,l,flags);        if(!flags)            FindNodeAndRemPath(pRoot->_pRight,pNode,l,flags);        if(!flags)        {            l.pop_back();            return;        }    }    Node* FindCrossPointOfTwoList(list<Node*> l1,list<Node*> l2)//求两个相交链表中最后一个相交的节点    {        //Node* pCur1 = l1.front();        //Node* pCur2 = l2.front();        list<Node*>::iterator it1 = l1.begin();        list<Node*>::iterator it2 = l2.begin();        Node* pCur = NULL;        while(it1 != l1.end() && it2 != l2.end())        {            if(*it1 == *it2)            {                pCur = *it1;                it1++;                it2++;            }            else            {                return pCur;            }        }        return NULL;    }

6.求二叉树的镜像

  • 先序遍历得到结果
//获取一棵树的镜像---实现1(利用先序遍历)    void GetMirrorTree1()    {        _pRoot = _GetMirrorTree1(_pRoot);    }    //获取一棵树的镜像树---先序遍历实现    Node* _GetMirrorTree1(Node* pRoot)    {        if(pRoot == NULL || (pRoot->_pRight==NULL && pRoot->_pLeft==NULL))            return NULL;        std::swap(pRoot->_pLeft,pRoot->_pRight);        Node* pCur = NULL;        pCur = _GetMirrorTree(pRoot->_pLeft);        pCur = _GetMirrorTree(pRoot->_pRight);        return pRoot;    }
  • 层序遍历的到结果
//获取一棵树的镜像---实现2(利用层序遍历)    void GetMirrorTree2()    {        _pRoot = _GetMirrorTree2(_pRoot);    }//获取一棵树的镜像树---层序遍历实现    Node* _GetMirrorTree2(Node* pRoot)    {        if(pRoot==NULL)            return NULL;        queue<Node*> q;        q.push(pRoot);        Node* pCur = NULL;        while(!q.empty())        {            pCur=q.front();            if(pCur->_pLeft!=NULL || pCur->_pRight!=NULL)                std::swap(pCur->_pLeft,pCur->_pRight);            q.pop();            if(pCur->_pLeft!=NULL)                q.push(pCur->_pLeft);            if(pCur->_pRight!=NULL)                q.push(pCur->_pRight);        }        return pRoot;    }

7.判断一棵树是否为完全二叉树

//判断一棵树是否为完全二叉树    bool IsCompleteTree()    {        return _IsCompleteTree(_pRoot);    }        bool _IsCompleteTree(Node* pRoot)//判断一颗树是不是完全二叉树    {        //用一个队列来保存数据,从根节开始,访问他的左右子树,若左右子树都存在,则说明该树有可能成为完全二叉树,左右节点入栈        //取队列的头节点,访问其左右子树,若只有左子树存在,说明有可能是完全二叉树,入栈继续访问,此时该节点前面的节点都满足        //完全二叉树的要求,若后面的节点有一个孩子节点,则说明该树不是完全二叉树;        //若只有右子树存在,则节点没有左子树该树一定不是完全二叉树        //若该节点没有左右孩子,同只有左孩子一样,继续访问后面的节点进行判断        if(pRoot==NULL)            return true;        queue<Node*> q;        q.push(pRoot);        Node* pCur = NULL;        while(!q.empty())        {            pCur = q.front();            if(pCur->_pLeft!=NULL && pCur->_pRight!=NULL)            {                q.push(pCur->_pLeft);                q.push(pCur->_pRight);            }            else if(pCur->_pLeft!=NULL)            {                q.push(pCur->_pLeft);                //判断处理                q.pop();                while(!q.empty())                {                    pCur=q.front();                    if(pCur->_pLeft!=NULL || pCur->_pRight!=NULL)                        return false;                    q.pop();                }            }            else if(pCur->_pRight!=NULL)            {                return false;            }            else            {                //判断处理                q.pop();                while(!q.empty())                {                    pCur=q.front();                    if(pCur->_pLeft!=NULL || pCur->_pRight!=NULL)                        return false;                    q.pop();                }            }            if(!q.empty())            q.pop();        }        return true;    }

8.用先序和中序列表重建二叉树

void RebuildTree(char* preorder,char* inorder)//根据先序数列和中序数列重建二叉树    {        if(preorder==NULL || inorder==NULL)            return;        size_t height1 = strlen(preorder);        size_t height2 = strlen(inorder);        size_t index = 0;        _pRoot = _RebuildTree(_pRoot,preorder,preorder+height1-1,inorder,inorder+height2-1,index);    }Node* _RebuildTree(Node* pRoot,char* pstart,char* pend,char* instart,char* inend,size_t& index)    {        if(pstart==NULL || pend==NULL || instart==NULL || inend==NULL)            return NULL;        if(instart>inend)        {            --index;            return NULL;        }        pRoot = new Node;        pRoot->_data = pstart[index];        size_t idx=0;        for(idx=0; idx<(inend-instart); ++idx)        {            if(pstart[index]==instart[idx])                break;        }        pRoot->_pLeft = _RebuildTree(pRoot->_pLeft,pstart,pend,instart,instart+idx-1,++index);        pRoot->_pRight = _RebuildTree(pRoot->_pRight,pstart,pend,instart+idx+1,inend,++index);        return pRoot;    }

9.根据中序和后序数列重建二叉树

void RebuildTree2(char* inorder,char* postorder)//根据后序数列和中序数列重建二叉树    {        if(inorder==NULL || postorder==NULL)            return;        size_t height1 = strlen(inorder);        size_t height2 = strlen(postorder);        if(height1 != height2)            return;        size_t index=height2-1;        _pRoot = _RebuildTree2(_pRoot,inorder,inorder+height1-1,postorder,postorder+height2-1,index);    }    //根据中序数列和后序数列重建二叉树    Node* _RebuildTree2(Node* pRoot,char* instart,char* inend,char* poststart,char* postend,size_t& index)    {        if(instart==NULL || inend==NULL || poststart==NULL || postend==NULL)            return NULL;        if(instart>inend)        {            ++index;            return NULL;        }        pRoot = new Node;        pRoot->_data = poststart[index];        size_t idx=0;        for(; idx<(inend-instart); ++idx)        {            if(poststart[index]==instart[idx])                break;        }        pRoot->_pRight = _RebuildTree2(pRoot->_pRight,instart+idx+1,inend,poststart,postend,--index);        pRoot->_pLeft = _RebuildTree2(pRoot->_pLeft,instart,instart+idx-1,poststart,postend,--index);        return pRoot;    }

10.获取一棵完全二叉树所有节点的个数

  • 普通遍历计数
    缺点:需要遍历每个节点,时间开销大
size_t GetAllNode(size_t& count)//利用先序遍历,查找每一个节点并计数,时间复杂度O(N),N是总的节点个数    {        return _GetAllNode(_pRoot,count);    }    size_t _GetAllNode(Node* pRoot,size_t& count)    {        if(pRoot==NULL)            return count;        if(pRoot!=NULL)             ++count;        count = _GetAllNode(pRoot->_pLeft,count);        count = _GetAllNode(pRoot->_pRight,count);    }
  • 代码优化:注意条件—完全二叉树
//计算一棵完全二叉树的所有节点的个数---优化,时间复杂度O(logN)2(平方)    size_t GetAllNode1(size_t& count)    {        return _GetAllNode1(_pRoot,count);    }    size_t _GetAllNode1(Node* pRoot,size_t& count)    {        if(pRoot==NULL)            return 0;        if(pRoot->_pLeft==NULL && pRoot->_pRight==NULL)            return 1;        int h = GetHight(pRoot);//求深度O(logN)        size_t ret = 0;        if((ret=GetHight(pRoot->_pRight)+1)==h)//左子树为满二叉树,用公式计算,右子树遍历计算        {            count = ((1<<(h-1))-1+1+_GetAllNode(pRoot->_pRight,count));//O(logN)        }        else        {            count = ((1<<(h-2))-1+1+_GetAllNode(pRoot->_pLeft,count));//右子树为满二叉树,用公式计算,左子树遍历计算        }        return count;    }    size_t GetHight(Node* pRoot)//计算树的最大深度    {        Node* pCur = pRoot;        size_t ret=0;        while(pCur)        {            pCur = pCur->_pLeft;            ret++;        }        return ret;    }

11.判断一棵树是不是平衡二叉树

  • 普通解法—从根节点开始访问,判断其左右子树是否为平衡二叉树,若不是,这直接退出,若是,继续访问左右子树
    缺点:计算节点左右子树高度时重复计算,多次访问,时间开销大
bool IsBalanceTree1()//实现1---利用先序遍历的方法,但是节点重复遍历了,从根节点开始计算并比较每个子树的深度    {        return _IsBalanceTree1(_pRoot);    }    bool _IsBalanceTree1(Node* pRoot)//判断一棵树是不是平衡二叉树---实现1    {        //从根节点开始,得到左右子树的高度,比较,若不满足条件,则直接退出,后面再不用比较        if(pRoot==NULL)            return true;        int left= _Height(pRoot->_pLeft);        int right = _Height(pRoot->_pRight);        int diff = left-right;        if(diff>1 || diff<-1)            return false;        return (_IsBalanceTree1(pRoot->_pLeft) && _IsBalanceTree1(pRoot->_pRight));    }
  • 利用后序遍历的方法实现对节点的访问
bool IsBalanceTree2()//实现二---利用后序遍历的方法,保证每个节点只遍历一次,根据根节点的深度为左右子树深度的最大值加一    {        int depth = 0;        return _IsBalanceTree2(_pRoot,depth);    }bool _IsBalanceTree2(Node* pRoot,int& depth)//判断一棵树是不是平衡二叉树---实现2    {        //从树的最左端的节点开始访问其左右子树,这里没有调用_Height函数,最左端的节点的左子树高度一定为1,最右端的右节点的深度也是1        //再递归回退的过程中,得到节点左子树的高度和右子树的高度(下层高度+1),比较,若满足条件,则继续回退,得到当前层的高度,继续比较上一层        //若不满足,则直接返回错误        if(pRoot==NULL)        {            depth = 0;            return true;        }        int left;        int right;        if(_IsBalanceTree2(pRoot->_pLeft,left)&&_IsBalanceTree2(pRoot->_pRight,right))        {            int diff = left-right;            if(diff<=1 && diff>=-1)            {                depth = 1+((left>right)? left:right);                return true;            }            return false;        }        return false;    }
0 0