二叉树的面试题

二叉树的定义:

template<class T>
struct BinaryTreeNode
{
    T _data;
    BinaryTreeNode<T> *_left;
    BinaryTreeNode<T> *_right;


    BinaryTreeNode(const T&data)
        :_data(data)
        , _left(NULL)
        , _right(NULL)

    {}
};

一:求两个节点最近的公共祖先
方法一:找最近的公共祖先我们常规的想法是先找到node1节点的父亲,再给一个节点node2,找node2的父亲与node1的父亲比较,若相等返回node1的父亲,停止查找,如果不相等,node2继续向上查找,直到根节点为止.
**

Node* _GetNearestAncestor(Node*root, Node*node1, Node*node2)
    {
        assert(root != NULL&&node1 != NULL&&node2 != NULL);
        Node *tmp;
        if (node1 != NULL)
        {
            node1 = node1->_parent;
            tmp = node2;
            while (tmp!=NULL)
            {
                if (node1 = tmp->_parent)
                {
                    return node1;
                }
                tmp = tmp->_parent;
            }
        }
        return NULL;
    }

**
这个方法时间复杂度较高O(N^2)
方法二:这颗树是普通二叉搜索树
这个二叉树是一个二叉搜索树,我们知道二叉搜索树的性质是当前点的键值大于左子树的键值,小于右子树的键值;
这里写图片描述
这里写图片描述

Node*GetLastCommonAncestor(Node*node1, Node*node2)
    {
        T  _min = 0;//小键值
        T  _max = 0;//大键值
      //根节点为空
        if (_root == NULL)
        {
            return NULL;
        }
        if (node1->_data < node2->_data)
        {
            _max = node2->_data;
            _min = node1->_data;
        }
        else
        {
            _max = node1->_data;
            _min = node2->_data;
        }

        Node*cur = _root;
        while (cur)
        {
            if (cur->_data<_max&&cur->_data>_min)
            {
                return cur;
            }
            //大的键值和小的键值都小于当前节点的键值
            else if (cur->_data > _min&&cur->_data > _max)
            {
                cur = cur->_left;
            }
            //右子树
            else
            {
                cur = cur->_right;
            }
        }
        return NULL;
    }

方法三:有父指针的二叉搜索树
当然这里面有一种特殊的情况是当大小的键值分别在左子树和右子树,这时候找最近的公共祖先就可以转换成为找两个链表的交点
这里写图片描述

int len(Node*node)
    {
        if (node== NULL)
        {
            return 0;
        }
        int Len = 0;
        while (node->_parent)
        {
            node = node->_parent;
            ++Len;
        }
        return Len;
    }
    Node*FindLastCommentAncestor(Node*node1, Node*node2)
    {
        if (node1 == NULL || node2 == NULL)
        {
            return NULL;//表明为空
        }

        int len1 = len(node1);//链表1的长度
        int len2 = len(node2);//链表2的长度

        for (; len1 > len2; --len1)
        {
            node1 = node1->_parent;
        }
        for (; len2 > len1; --len2)
        {
            node2 = node2->_parent;
        }
        while (node1&&node2&&node1 != node2)
        {
            nodo1 = node1->_parent;
            nodo2 = node2->_parent;
        }
        if (node1 == node2)
        {
            return node1;
        }
        return NULL;//没有找到交点
    }

方法四递归解法:
从根节点开始遍历在如果node1,和node2中任意一个节点和根节点匹配,那么根节点就是最近的公共祖先,如果不满足,递归子树,找到与当前节点相等的节点返回.

/*分别比较*/
    Node*GetLastCommonAncestor(Node*root, Node*node1, Node*node2)
    {
        if (root == NULL || node1 == NULL || node2 == NULL)
        {
            return NULL;
        }
        //从根节点开始遍历,如果node1和node2中任意一个节点和根节点匹配,那么根节点就是最近的公共祖先
        if (node1 == root||node2== root)
        {
            return root;
        }
        //都不匹配(分别遍历左子树和右子树)
        Node* left_lca = GetLastCommonAncestor(root->_left, node1, node2);//在左子树
        Node* right_lca = GetLastCommonAncestor(root->_right, node1, node2);//在右子树
        //有一个出现在左子树中有一个出现在右子树中,那么最近的公共祖先在根节点上
        if (left_lca&&right_lca)
            return root;
        if (left_lca == NULL)
            return right_lca;
        else
            return left_lca;

     return NULL;
   }

二:由前序序列和中序序列重建二叉树
1:我们知道前序的第一个节点就是跟节点,而中序的根节点在中间,我们可以在中序中找到值与前序 根节点的值相等的节点可以确定重建后的根节点,我们可以用递归的方法缺点子序列.这里写图片描述
递归创建子序列
这里写图片描述
由前序和中序重建二叉树

Node* _RbulidTree(T*_PreOrder, T*_InOrder, int n)
    {
        if (_PreOrder == NULL || _InOrder == NULL || n == 0)
        {
            return NULL;
        }

        T  tmp = _PreOrder[0];//前序中找到根节点
        Node* root = new Node(tmp);

        size_t index = 0;//中序中找到给根节点
        for (index = 0; index < n; ++i)
        {
            if (_PreOrder[0] == _InOrder[index])
            {
                break;
            }
        }
        int Start = index;//元素的首位置
        int End = n - Start - 1;//元素尾部的位置

        if (Start > 0)
        {
            root->_left = _RbulidTree(_PreOrder + 1, _InOrder, Strat);//重建左序列
        }

        if (End > 0)
        {
            root->_right = _RbulidTree(_PreOrder + Start + 1, _InOrder + Start + 1, End);//重建右子序列
        }
        return root;
    }

三:找二叉树两个节点最远的距离
(1)如果二叉树为空那么左子树和有右子树的距离为0 ,返回0;
(2)如果二叉树不为空,两个节点最远距离要么是左子树的最远距离,要么是右子树的最远距离,要么是左子树的最远距离+上右子树的最远距离;
这里写图片描述

//求两个节点的最远距离
    void MaxDistance()
    {
        int MaxLet = 0;
        cout << _MaxDistance(_root, MaxLet) << endl;
        cout << endl;
    }
//两个节点最远的距离
    int _MaxDistance(Node*root,int &MaxLen)
    {
        if (root == NULL)
        {
            return 0;
        }
        int _MaxLeftLen = 0;
        int _MaxRightLen = 0;
        _MaxLeftLen = _MaxDistance(root->_left,MaxLen);//左子树的最远距离
        _MaxRightLen = _MaxDistance(root->_right,MaxLen);//右子树的最远距离

        int tmp = _MaxLeftLen + _MaxRightLen;
        //判断当前最远距离与左右子树的最远距离比较
        if (tmp > MaxLen)
            MaxLen = tmp;
        return _MaxLeftLen > _MaxRightLen ? (_MaxLeftLen + 1) : (_MaxRightLen + 1);
    }

四:判断一颗树是不是完全二叉树
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左
边,这就是完全二叉树
这里写图片描述

void  IsCompleteBTree()
    {
        _IsCompleteBTree(_root);
    }
    bool _IsCompleteBTree(Node*root)
    {
        queue<Node*>q;
        if (_root == NULL)
        {
            return false;
        }
        Node*cur = _root;
        q.push(cur);
        //先进行层序遍历
        while ((cur=q.front())!=NULL)
        {
            q.pop();
            q.push(cur->_left);
            q.push(cur->_right);

        }
        //看队找那个是否还有元素
        while (!q.empty())
        {
            cur = q.front();
            q.pop();
            if (cur != NULL)
            {
                return false;
            }
        }
        return true;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值