二叉树面试题

  • 求二叉树的高度
  • 判断一棵树是否为平衡二叉树
  • 求二叉树叶子节点的个数
  • 求二叉树第k层节点的个数
  • 判断某个节点是否存在于二叉树中
  • 求二叉树的镜像
  • 求二叉树中最远的两个节点之间的距离
  • 由前序遍历和中序遍历重建二叉树

二叉树的高度

题目:输入一棵二叉树的根结点,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
思路: 1、如果树为空,返回0
2、若果二叉树只有一个节点,返回1
3、如果二叉树中有左子树,返回左子树的高度+1
4、如果二叉树中有左子树,返回左子树的高度+1
5、如果左右子树都存在,则返回高度较大的子树值+1
代码实现如下(递归思想):

树的节点结构:

struct BTNode
{
    BTNode(const T& data=T())//定义结构体  并初始化
    :_data(data)
    , _left(NULL)
    , _right(NULL)
    {}
    BTNode* _left;
    BTNode* _right;
    T _data;
};

size_t _Depth(Node* _root)//  左右子树高的+1依次递归
    {
        if (_root == NULL)
        {
            return 0;
        }
        if (_root)
        {
        //int left=_Depth(_root->_left)
        //int right=_Depth(_root->_right) 
        //return left>right?left+1:right+1
        return (_Depth(_root->_left) > _Depth(_root->_right) 
                ? _Depth(_root->_left) : _Depth(_root->_right)) + 1;
        }
    }

判断一棵树是否为平衡二叉树

题目: 输入一棵二叉树的根结点,判断该树是不是平衡二叉树。如果某二叉树中任意结点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。例如,下图中的二叉树就是一棵平衡二叉树。
思路:
1、由于在上一个题中已经可以得到了树的高度,所以只需要求得左右子树的高度差就可以了
2、两颗子树也要平衡

bool Isbalance(Node* _root)
    {
        if (_root == NULL)
            return NULL;
        int left_height = _Depth(_root->_left);
        int right_height = _Depth(_root->_right);
        int sub_height = abs(left_height - right_height);
        if (sub_height > 1)
        return false;
        //判断两颗子树是否平衡
        return Isbalance(_root->_left) && Isbalance(_root->_right);
    }

但是,上面的“先序遍历”判断二叉树平衡的方法,时间复杂度比较大。因为,二叉树中的很多结点遍历了多次。
比如,求解根的的高度时,需要先求解根的左子树高度和右子树高度,这就遍历了整棵左子树中的结点和右子树中的结点。而求解以根的左孩子为根的子树的高度时,又需要遍历它(根的左孩子)的左子树和右子树。这样,相当于很多结点的高度重复计算了。
根本原因是采用了“先序遍历”,求根的高度,需要先知道根的左右孩子的高度。
如果采用后序遍历,先知道某结点左右子树的高度,如果左右子树的高度都不满足平衡二叉树(二者高度相减大于1),那么都不需要再去求解该结点的高度了。
因为,平衡二叉树要求二叉树中任意结点的左右子树高度相差不超过1
代码实现如下:

bool isBalance(TreeNode *root, int &height)
    {
        if (root == NULL)
        {
            height = 0;
            return true;
        }

        int leftHeight = 0, rightHeight = 0;
        bool leftRet = isBalance3(root->left, leftHeight);
        bool rightRet = isBalance3(root->right, rightHeight);

        height = max(leftHeight, rightHeight) + 1;
        if (abs(leftHeight - rightHeight) > 1)
            return false;
        return leftRet && rightRet;
    }
};

二叉树叶子节点的个数

题目:输入一棵二叉树的根结点,求该树的叶子节点的个数。

思路: 1、当树为空时,返回0;
2、当树只有根节点时,返回1
3、当树的左右子树都为空时,树的叶子节点为左右子树的叶子节点之和
代码实现如下:

size_t _LeafSize( const Node* _root)//返回二叉树中叶子节点的个数 三种情况都要分别考虑到
    {
        if (_root == NULL)
        {
            return 0;
        }
        if ((_root->_left == NULL) && (_root->_right==NULL))
        {
            return 1;
        }
        if (_root)
        {
            return _LeafSize(_root->_left) + _LeafSize(_root->_right);
        }
    }

第K层节点的个数

题目:给定一个二叉树的根节点,求二叉树第K层节点的个数
思路: 1、如果树为空或者K<0,返回0
2、如果树不为空,并且求的是树的第一层的节点个数,返回1
3、树的左右子树都不为空,返回第k-1层左右子树节点的个数之和
递归方法

size_t _GetKLevel_NodeSzie( Node* _root,int k)//返回第k层节点的个数
    {
        if (_root == NULL||K<=0)
        {
            return 0;
        }
        if (_root!=NULL&&k==1)
        {
            return 1;
        }
        return _GetKLevel_NodeSzie(_root->_left, k - 1)
            + _GetKLevel_NodeSzie(_root->_right, k - 1);
    }

判断一个节点是否在一颗二叉树中

题目: 给一个二叉树的根节点,和另外一个节点,判断此节点是否在二叉树中
思路:
1、如果树为空或者所给节点为空,返回false
2、如果所给节点的值,与树中某一个节点的值相等,返回true
3、如果树不为空,所给节点也不为空,依次遍历树的左子树和右子树
代码实现如下:

//判断一个二叉树的节点是否在二叉树中
    bool IsNodeIntree(Node* _root, Node *node)
    {
        if (_root == NULL || node == NULL)
            return false;
        if (_root->_data == node->_data)
            return true;
        if (IsNodeIntree(_root->_left, node) || IsNodeIntree(_root->_right, node))
            return true;
        return false;
     }

求二叉树的镜像

题目: 输入一颗二叉树,输出它的镜像
思路:
这里写图片描述
树的镜像就是将树的所有左右节点全部进行交换
1、当树为空时,直接返回
2、当树只有一个节点时,直接返回
3、当树的左右节点都不为空时,依次交换树的左右节点
方法1:递归法

void Mirrorcursively(Node *_root)
     {
         if (_root == NULL || (_root->_left == NULL&&_root->_right == NULL))
             return;
         Node *ptmp = _root->_left;
         _root->_left = _root->_right;
         _root->_right = ptmp;
         if (_root->_left)
             Mirrorcursively(_root->_left);
         if (_root->_right)
             Mirrorcursively(_root->_right);
       }

方法二:非递归法
用非递归方法实现,借助栈来完成

 void Iteratively(Node *_root)
     {
         if (_root == NULL)
             return;
         stack<Node*> s;
         s.push(_root);
         while (!s.empty())
         {
             Node *top = s.top();
             s.pop();
             Node *ptmp = top->_left;
             top->_left = top->_right;
             top->_right = ptmp;
             if (top->_left)
                 s.push(top->_left);
             if (top->_right)
                 s.push(top->_right)
         }
    }

求二叉树中最远的两个节点之间的距离

题目:如果我们把二叉树看做图,父子节点之间的连线看成是双向的,我们姑且定义“距离”为两个节点之间边的个数。写一个程序求一棵二叉树中相距最远的两个节点之间的距离。
思路:
1、如果最远距离的两个节点都经过了根节点,则最远距离就是左边最深的深度到根节点的距离+右边最深的深度到根节点的距离
如下图所示,树中相距最远的两个节点为A,B,最大距离为6。
这里写图片描述

2、如果具有最远距离的两个节点不经过根节点,那么最远距离就是根节点上某个子树上面的两个节点
如下图所示,树中相距最远的两个节点为A,B,最大距离为5。
这里写图片描述
代码实现如下:

int GetFarDistance()
       {
              int distance = -1;
              _Height(_root,distance);
              return distance;
       }
protected:
       int _Height(Node* root, int& distance)
       {
              if (root == NULL)
              {
                     return 0;
              }
              int leftH = _Height(root->_left);
              int rightH = _Height(root->_right);
              if (leftH+rightH > distance)
              {
                     distance = leftH + rightH;
              }
              return leftH > rightH ? leftH+1 : rightH+1;
       }

由前序遍历和中序遍历重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
思路:
1、在二叉树的前序遍历序列中,第一个数字总是树的根结点的值。但在中序遍历序列中,根结点的值在序列的中间,左子树的结点的值位于根结点的值的左边,而右子树的结点的值位于根结点的值的右边。因此我们需要扫描中序遍历序列,才能找到根结点的值。
2、如下图所示,前序遍历序列的第一个数字1就是根结点的值。扫描中序遍历序列,就能确定根结点的值的位置。根据中序遍历特点,在根结点的值1前面的3个数字都是左子树结点的值,位于1后面的数字都是右子树结点的值。
这里写图片描述
3、同样,在前序遍历的序列中,根结点后面的3个数字就是3个左子树结点的值,再后面的所有数字都是右子树结点的值。这样我们就在前序遍历和中序遍历两个序列中,分别找到了左右子树对应的子序列。
4、既然我们已经分别找到了左、右子树的前序遍历序列和中序遍历序列,我们可以用同样的方法分别去构建左右子树。也就是说,接下来的事情可以用递归的方法去完成。
代码实现如下:

#include <iostream>
#include <vector>
using namespace std;
// Definition for binary tree
struct TreeNode {
  int val;
  TreeNode *left;
  TreeNode *right;
  TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
/* 先序遍历第一个位置肯定是根节点node,
中序遍历的根节点位置在中间p,在p左边的肯定是node的左子树的中序数组,p右边的肯定是node的右子树的中序数组
另一方面,先序遍历的第二个位置到p,也是node左子树的先序子数组,剩下p右边的就是node的右子树的先序子数组
把四个数组找出来,分左右递归调用即可
 */
class Solution {
public:
struct TreeNode* reConstructBinaryTree(vector<int>       pre,vector<int> in) {

int in_size = in.size();//获得序列的长度
2 if (in_size == 0)
return NULL;
//分别存储先序序列的左子树,先序序列的右子树,中序序列的左子树,中序序列的右子树
vector<int> pre_left, pre_right, in_left, in_right;
int val = pre[0];//先序遍历第一个位置肯定是根节点node,取其值
//新建一个树结点,并传入结点值
TreeNode* node = new TreeNode(val);//root node is the first element in pre
//p用于存储中序序列中根结点的位置
int p = 0;
for (p; p < in_size; ++p){
if (in[p] == val) //Find the root position in in 
break;        //找到即跳出for循环
}
for (int i = 0; i < in_size; ++i){
if (i < p){
 //建立中序序列的左子树和前序序列的左子树
in_left.push_back(in[i]);//Construct the left pre and in 
pre_left.push_back(pre[i + 1]);//前序第一个为根节点,+1从下一个开始记录
 }
else if (i > p){
 //建立中序序列的右子树和前序序列的左子树
in_right.push_back(in[i]);//Construct the right pre and in 
 pre_right.push_back(pre[i]);
}
 }
//取出前序和中序遍历根节点左边和右边的子树
//递归,再对其进行上述所有步骤,即再区分子树的左、右子子数,直到叶节点
node->left = reConstructBinaryTree(pre_left, in_left);
node->right = reConstructBinaryTree(pre_right, in_right);
return node;
  }
 };
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值