二叉树面试题

1.求二叉树中最远的两个结点的距离:

思路:对于某个结点,求出其左边的高度和右边的高度,然后加起来,最后选择最大的距离就行。核心代码如下:

 size_t _GetMaxDistance(Node* root,size_t& distance)
    {
       if(root == NULL)
           return 0;
       int left = _GetMaxDistance(root->_left,distance);
       int right = _GetMaxDistance(root->_right,distance);
       if(left + right > distance)
           distance = left + right;
       return left > right ? left + 1: right + 1;
    }

时间复杂度:O(N)。每次递归求最大距离的时候就会带回当前的高度,减少递归次数,降低时间复杂度。


2.根据前序遍历序列和中序遍历序列求一棵二叉树。

那么我们就是很容易的写出完整代码。

template<typename T>
struct BinaryTreeNode
{
    T _data;
    BinaryTreeNode<T>* _left;
    BinaryTreeNode<T>* _right;
    BinaryTreeNode(const T& data = T())
       :_data(data)
       ,_left(NULL)
       ,_right(NULL)
    {}
};
template<typename T>
class RebuildTree
{
   typedef BinaryTreeNode<T> Node;
public:
    RebuildTree(T* pre,T* in,size_t size)
    {
       assert(pre && in);
       _root = new Node(pre[0]);
       _CreateTree(pre,in,size,_root);
    }
    void PreOrder()
    {
       _PreOrder(_root);
    }
protected:
    void _CreateTree(T* pre,T* in,size_t size,Node*& root)
    {
       if(size <= 0)
           return;
       if(root == NULL)
           root = new Node(pre[0]);
       int index = 0;
       for(index = 0; index < size; ++index)
       {
           if(pre[0] == in[index])//根节点
               break;
       }
       int leftNum = index;
       int rightNum = size - leftNum - 1;
        _CreateTree(pre+1,in,leftNum,root->_left);
       _CreateTree(pre+index+1,in + index + 1,rightNum,root->_right);
    }
    void _PreOrder(Node* root)
    {
       if(NULL == root)
           return;
       cout<<root->_data<<" ";
       _PreOrder(root->_left);
       _PreOrder(root->_right);
    }
private:
    Node* _root;
};
void TestRebuildTree()
{
    int pre[] = {1,2,3,4,5,6};
    int in[] = {3,2,4,1,6,5};
    RebuildTree<int> rt(pre,in,6);
    rt.PreOrder();
}



3.判断一棵二叉树是否是完全二叉树。
关于这个题目,有以下思路:
思路一:层序遍历,如果发现一个结点只有右孩子,没有左孩子,那么它一定不是完全二叉树,如果一个结点它仅有左孩子或者没有孩子,那么它之后的结点都必须是叶子结点,如果后边有非叶子结点,那么就不是完全二叉树。
思路二:层序遍历(将空节点也入队),如果遇到空节点时,队列中还有非空结点,那么就说明不是完全二叉树;如果队列中没有非空结点,那么就是完全二叉树。
bool _IsComplete_1()
    {
        if(_bt._root == NULL)
            return false;
        queue<Node*> q;
        q.push(_bt._root);
        bool result = true;
        bool IsNul = false;//出现一个叶子结点,后边的结点必须都是叶子结点
        while(!q.empty())
        {
            Node* pNode = q.front();
            q.pop();
            if(IsNul)
            {
                //只有pNode是叶子结点,这棵树才是完全二叉树;否则不是
                if(pNode->_left != NULL || pNode->_right != NULL)
                {
                    result = false;
                    break;
                }
            }
            else
            {
                if(pNode->_left != NULL && pNode->_right != NULL)
                {
                    q.push(pNode->_left);
                    q.push(pNode->_right);
                }
                else if(pNode->_left != NULL && pNode->_right == NULL)
                {
                    q.push(pNode->_left);
                    IsNul = true;
                }
                else if(pNode->_left == NULL && pNode->_right != NULL)
                {
                    result = false;//说明这棵树不是完全二叉树
                    break;
                }
                else
                {
                    IsNul = true;
                }
            }
        }
        return result;
    }
    bool _IsComplete_2()
    {
        if(_bt._root == NULL)
            return false;
        queue<Node*> q;
        q.push(_bt._root);
        while(!q.empty())
        {
            Node* top = q.front();
            if(top != NULL)
            {
                q.pop();
                q.push(top->_left);
                q.push(top->_right);
            }
            else
                break;
        }
        //计算队列中非空节点的个数
        int i = 0;
        while(i++ < q.size())
        {
            if(q.front() != NULL)
                return false;
            else
                q.pop();
        }
        return true;
    }


4.求两个结点的最低公共祖先。
分析:对于这样的问题,就要考虑到各种情况。
1)二叉搜索树的情况:根据二叉搜索树的性质(左孩子的值小于根节点的值,右孩子的值大于根节点的值),如果给定的两个结点的值都大于当前子树的根节点的值,那么这两个结点必然都位于当前子树的右子树上;如果给定的两个结点的值都小于当前子树的根节点,那么这两个结点都在当前子树的左子树上;如果给定的两个结点的值一个大于当前子树的根节点的值,一个小于当前子树的根节点的值,那么当前的子树的根节点就是所要找的最低公共祖先。
完整代码:
template<typename T>
class CommonParent
{
    typedef TreeNode<T> Node;
public:
    CommonParent(T* array,size_t size,const T& invalid)
       :_bt(array,size,invalid)
    {}
    //是二叉搜索树的情况
    Node*  LeastCommonParent(const T& a, const T& b)
    {
       if(_bt._root == NULL || (_bt.Find(a)&& _bt.Find(b)))
       {
           return  NULL;
       }
       Node* cur = _bt._root;
       //当前子树的根的值比两个节点的值都要大,说明两个节点都在当前根的左子树上
       if(cur->_data > a && cur->_data > b)
       {
           cur = cur->_left;
       }
       //当前子树的根的值比两个节点的值都要小,说明两个节点都在当前根的右子树上
       else if(cur->_data < a && cur->_data < b)
       {
           cur = cur->_right;
       }
       else
           return cur;
    }
private:
    BinaryTree<T> _bt;
};
void TestCommonParent_1()
{
    int array[] = {8,7,5,'#','#','#',12,11,'#','#',18};
    CommonParent<int> cp(array,11,'#');
    TreeNode<int>* ret = cp.LeastCommonParent(7,20);
    if(ret == NULL)
       cout<<"没有公共祖先";
    else
       cout<<ret->_data<<endl;
}

2)非搜索树的情况:
a、树中有指向父节点的指针:从给定的两个结点开始,反向遍历,找到路径。。然后求出两个链表的第一个公共结点。
b、树中没有指向父节点的指针:从根节点分别找出给定结点的路径,然后求出两个链表的最后一个交点。这样做的话,就需要遍历树一次,遍历两个链表。下边提供一种新的高效的方法~~



代码:

Node* _LeastCommonParent(Node* root,Node* p1,Node* p2)
    {
       if(root == NULL)
           return NULL;
       if(root == p1 || root == p2)
           return root;
       Node* left = _LeastCommonParent(root->_left,p1,p2);
       Node* right = _LeastCommonParent(root->_right,p1,p2);
       if(left &&  right)
           return root;
       return left?left:right;
    }

时间复杂度是O(N)。

5.将二叉搜索树改为有序的双向链表。
分析:二叉搜索树的中序遍历就是一个升序序列。像前边线索化二叉树那样,如果可以让一个结点的left指向其中序遍历的前一个结点(当该结点不是第一个结点的时候),将该结点的right指向其后序遍历的下一个结点。遍历完了之后,我们还是需要沿着树的方向,一直向左走,找到链表的第一个结点。
代码:
void _ToList(Node* root,Node*& prev)
    {
        if (root == NULL)
            return;
        _ToList(root->_left,prev);
        root->_left = prev;
        if (prev)
            prev->_right = root;
         prev = root;
        _ToList(root->_right,prev);
    }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值