程序员面试题精选100题(60)-判断二叉树是不是平衡
题目:输入一棵二叉树的根结点,判断该树是不是平衡二叉树。如果某二叉树中任意结点的左右子树的深度相差不超过
1
,那么它就是一棵平衡二叉树。
例如下图
中的二叉树就是一棵平衡二叉树:
方法一:我们曾介绍过如何求二叉树的深度。有了求二叉树的深度的经验之后再解决这个问题,我们很容易就能想到一个思路:在遍历树的每个结点的时候,调用函数TreeDepth得到它的左右子树的深度。如果每个结点的左右子树的深度相差都不超过1,按照定义它就是一棵平衡的二叉树。这种思路对应的代码如下:
bool IsBalanced(BinaryTreeNode* pRoot)
{
if(pRoot == NULL)
return true;
int left = TreeDepth(pRoot->m_pLeft);
int right = TreeDepth(pRoot->m_pRight);
int diff = left - right;
if(diff > 1 || diff < -1)
return false;
return IsBalanced(pRoot->m_pLeft) && IsBalanced(pRoot->m_pRight);
}
上面的代码固然简洁,但我们也要注意到由于一个节点会被重复遍历多次,这种思路的时间效率不高。例如在函数IsBalance中输入上图中的二叉树,首先判断根结点(值为1的结点)的左右子树是不是平衡结点。此时我们将往函数TreeDepth输入左子树根结点(值为2的结点),需要遍历结点4、5、7。接下来判断以值为2的结点为根结点的子树是不是平衡树的时候,仍然会遍历结点4、5、7。毫无疑问,重复遍历同一个结点会影响性能。接下来我们寻找不需要重复遍历的算法。
方法二:如果我们用后序遍历的方式遍历二叉树的每一个结点,在遍历到一个结点之前我们已经遍历了它的左右子树。只要在遍历每个结点的时候记录它的深度(某一结点的深度等于它到叶节点的路径的长度),我们就可以一边遍历一边判断每个结点是不是平衡的。下面是这种思路的参考代码:
bool IsBalanced(BinaryTreeNode* pRoot, int* pDepth)
{
if(pRoot == NULL)
{
*pDepth = 0;
return true;
}
int left, right;
if(IsBalanced(pRoot->m_pLeft, &left)
&& IsBalanced(pRoot->m_pRight, &right))
{
int diff = left - right;
if(diff <= 1 && diff >= -1)
{
*pDepth = 1 + (left > right ? left : right);
return true;
}
}
return false;
}
我们只需要给上面的函数传入二叉树的根结点以及一个表示结点深度的整形变量就可以了:
bool IsBalanced(BinaryTreeNode* pRoot)
{
int depth = 0;
return IsBalanced(pRoot, &depth);
}
在上面的代码中,我们用后序遍历的方式遍历整棵二叉树。在遍历某结点的左右子结点之后,我们可以根据它的左右子结点的深度判断它是不是平衡的,并得到当前结点的深度。当最后遍历到树的根结点的时候,也就判断了整棵二叉树是不是平衡二叉树了。
上面两张方法的测试代码为:
方法一:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int data;
struct Node* left;
struct Node* right;
}TreeNode;
void buildTree(TreeNode** root,int data)
{
if(*root==NULL)
{
*root=(TreeNode*)malloc(sizeof(TreeNode));
(*root)->data=data;
(*root)->left=NULL;
(*root)->right=NULL;
return;
}
if((*root)->data>=data)
{
buildTree(&((*root)->left),data);
}
else
{
buildTree(&((*root)->right),data);
}
}
int depth(TreeNode *root)
{
int left,right;
if(root == NULL)
return 0;
left = depth(root->left)+1;
right = depth(root->right)+1;
return (left>right?left:right);
}
void traverse(TreeNode *root,int *ret)
{
int gap;
int left,right;
if(root == NULL)
return;
left = depth(root->left);
right = depth(root->right);
gap=left-right;
if((gap>=-1)&&(gap<=1))
{
}
else
{
*ret = -1;
}
traverse(root->left,ret);
traverse(root->right,ret);
}
int main()
{
TreeNode *root=NULL ;
int data[] = {6,4,7,2,5,1};
int i;
int ret=1;
int len = sizeof(data)/sizeof(int);
for(i=0;i<len;i++)
buildTree(&root,data[i]);
traverse(root,&ret);
if(ret == 1)
{
printf("ok\n");
}
else if(ret == -1)
{
printf("no\n");
}
return 0;
}
方法二:
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int data;
struct Node* left;
struct Node* right;
}TreeNode;
void buildTree(TreeNode** root,int data)
{
if(*root==NULL)
{
*root=(TreeNode*)malloc(sizeof(TreeNode));
(*root)->data=data;
(*root)->left=NULL;
(*root)->right=NULL;
return;
}
if((*root)->data>=data)
{
buildTree(&((*root)->left),data);
}
else
{
buildTree(&((*root)->right),data);
}
}
int isBalanced(TreeNode* root,int *depth,int *v)
{
int left,right,gap;
if(root==NULL)
{
*depth=0;
return 1;
}
if(isBalanced(root->left,&left,v)&&isBalanced(root->right,&right,v))
{
gap=left-right;
if(gap>=-1&&gap<=1)
{
*depth=1+(left>right?left:right);
return 1;
}
}
*v=-1;
return -1;
}
int main()
{
TreeNode* root=NULL;
int data[]={6,4,7,2,5,1};
int i;
int depth=0;
int v=1;
int len=sizeof(data)/sizeof(int);
for(i=0;i<len;i++)
buildTree(&root,data[i]);
isBalanced(root,&depth,&v);
if(v==1)
{
printf("OK\n");
}
else if(v==-1)
{
printf("no\n");
}
return 0;
}
参考来源:《剑指offer名企面试官精讲典型编程题》——何海涛