六大类二叉树面试题汇总解答

本文总结了二叉树面试题的六大类问题,包括判断二叉树是否为二叉搜索树、完全二叉树、平衡二叉树以及两棵二叉树是否同构。此外,还讨论了如何构建二叉树,存储和恢复二叉搜索树和一般二叉树,以及查找类问题,如最低公共祖先结点和最大二叉搜索子树。最后,文章涵盖了距离类问题,如结点间最短距离,以及混合类问题,如有序数组构建平衡二叉搜索树和二叉搜索树转有序循环链表。
摘要由CSDN通过智能技术生成

0 概述

这一篇文章汇总下常见的二叉树相关面试题,主要分为判断类、构建类、存储类、查找类、距离类、混合类这六类大问题。

1 判断类问题

判断类问题主要分下下判断二叉树是否是二叉搜索树、二叉完全树,以及两棵二叉树是否同构这三个问题。

1.1 判断一棵二叉树是否是二叉搜索树(BST)

题:给定一棵二叉树,判断该二叉树是否是二叉搜索树。

二叉搜索树是一种二叉树,但是它有附加的一些约束条件,这些约束条件必须对每个结点都成立:

  • 结点的左子树所有结点的值都小于等于该结点的值。

  • 结点的右子树所有结点的值都大于该结点的值。

  • 结点的左右子树同样都必须是二叉搜索树。

一种错误解法

初看这个问题,容易这么实现:假定当前结点值为 k,对于二叉树中每个结点,判断其左孩子的值是否小于 k,其右孩子的值是否大于 k。如果所有结点都满足该条件,则该二叉树是一棵二叉搜索树。

实现代码如下:

int isBSTError(BTNode *root){    if (!root) return 1;      if (root->left && root->left->value >= root->value)          return 0;      if (root->right && root->right->value < root->value)          return 0;      if (!isBSTError(root->left) || !isBSTError(root->right))          return 0;      return 1;  }

很不幸,这种做法是错误的,如下面这棵二叉树满足上面的条件,但是它并不是二叉搜索树。

    10   /  \  5    15     -------- binary tree(1) 符合上述条件的二叉树,但是并不是二叉搜索树。      /  \     6    20

解1:蛮力法

上面的错误解法是因为判断不完整导致,可以这样来判断:

  • 判断结点左子树最大值是否大于等于结点的值,如果是,则该二叉树不是二叉搜索树,否则继续下一步判断…

  • 判断右子树最小值是否小于或等于结点的值,如果是,则不是二叉搜索树,否则继续下一步判断。

  • 递归判断左右子树是否是二叉搜索树。(代码中的 bstMax 和 bstMin 函数功能分别是返回二叉树中的最大值和最小值结点,这里假定二叉树为二叉搜索树,实际返回的不一定是最大值和最小值结点)

int isBSTUnefficient(BTNode *root){    if (!root) return 1;    if (root->left && bstMax(root->left)->value >= root->value)        return 0;    if (root->right && bstMin(root->right)->value < root->value)        return 0;    if (!isBSTUnefficient(root->left) || !isBSTUnefficient(root->right))        return 0;    return 1;}

解2:一次遍历法

以前面提到的 binary tree(1) 为例,当我们遍历到结点 15 时,我们知道右子树结点值肯定都 >=10。当我们遍历到结点 15 的左孩子结点 6 时,我们知道结点 15 的左子树结点值都必须在 10 到 15 之间。显然,结点 6 不符合条件,因此它不是一棵二叉搜索树。

int isBSTEfficient(BTNode* root, BTNode *left, BTNode *right){   if (!root) return 1;   if (left && root->value <= left->value)       return 0;   if (right && root->value > right->value)       return 0;   return isBSTEfficient(root->left, left, root) && isBSTEfficient(root->right, root, right);}

解3:中序遍历解法

还可以模拟树的中序遍历来判断BST,可以直接将中序遍历的结果存到一个辅助数组,然后判断数组是否有序即可判断是否是BST。当然,我们可以不用辅助数组,在遍历时通过保留前一个指针 prev,据此来实现判断BST的解法,初始时 prev = NULL。

int isBSTInOrder(BTNode *root, BTNode *prev){    if (!root) return 1;    if (!isBSTInOrder(root->left, prev))        return 0;    if (prev && root->value < prev->value)        return 0;    return isBSTInOrder(root->right, root);}

1.2 判断二叉树是否是完全二叉树

题:给定一棵二叉树,判断该二叉树是否是完全二叉树(完全二叉树定义:若设二叉树的深度为 h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树)。

解1:常规解法-中序遍历

先定义一个 满结点 的概念:即一个结点存在左右孩子结点,则该结点为满结点。在代码中定义变量 flag 来标识是否发现非满结点,为1表示该二叉树存在非满结点。完全二叉树如果存在非满结点,则根据层序遍历队列中剩下结点必须是叶子结点,且如果一个结点的左孩子为空,则右孩子结点也必须为空。

int isCompleteBTLevelOrder(BTNode *root){    if (!root) return 1;    BTNodeQueue *queue = queueNew(btSize(root));    enqueue(queue, root);    int flag = 0;    while (QUEUE_SIZE(queue) > 0) {        BTNode *node = dequeue(queue);        if (node->left) {            if (flag) return 0;            enqueue(queue, node->left);        } else {            flag = 1;        }        if (node->right) {            if (flag) return 0;            enqueue(queue, node->right);        } else {            flag = 1;        }    }    return 1;}

解2:更简单的方法-判断结点序号法

更简单的方法是判断结点序号法,因为完全二叉树的结点序号都是有规律的,如结点 i 的左右子结点序号为 2i+1 和 2i+2,如根结点序号是 0,它的左右子结点序号是 1 和 2(如果都存在的话)。

我们可以计算二叉树的结点数目,然后依次判断所有结点的序号,如果不是完全二叉树,那肯定会存在结点它的序号大于等于结点数目的。如前面提到的 binary tree(1) 就不是完全二叉树。

    10(0)   /  \  5(1) 15(2)    - 结点数目为5,如果是完全二叉树结点最大的序号应该是4,而它的是6,所以不是。      /  \     6(5) 20(6)

实现代码如下:

int isCompleteBTIndexMethod(BTNode *root, int index, int nodeCount){    if (!root) return 1;    if (index >= nodeCount)        return 0;    return (isCompleteBTIndexMethod(root->left, 2*index+1, nodeCount) &&            isCompleteBTIndexMethod(root->right, 2*index+2, nodeCount));}

1.3 判断平衡二叉树

题:判断一棵二叉树是否是平衡二叉树。所谓平衡二叉树,指的是其任意结点的左右子树高度之差不大于1。

     __2__    /     \   1       4       ---- 平衡二叉树示例    \     / \     3   5   6

解1:自顶向下方法

判断一棵二叉树是否是平衡的,对每个结点计算左右子树高度差是否大于1即可,时间复杂度为O(N^2) 。

int isBalanceBTTop2Down(BTNode *root){    if (!root) return 1;    int leftHeight = btHeight(root->left);    int rightHeight = btHeight(root->right);    int hDiff = abs(leftHeight - rightHeight);    if (hDiff > 1) return 0;    return isBalanceBTTop2Down(root->left) && isBalanceBTTop2Down(root->right);}

解2:自底向上方法

因为解1会重复的遍历很多结点,为此我们可以采用类似后序遍历的方式,自底向上来判断左右子树的高度差,这样时间复杂度为 O(N)。

int isBalanceBTDown2Top(BTNode *root, int *height){    if (!root) {        *height = 0;        return 1;    }    int leftHeight, rightHeight;    if
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值