《剑指offer》:[39-1]判断是否为平衡二叉树

题目:输入一棵二叉树的结点,判断该树是不是平衡二叉树。如果某二叉树中任意结点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

上图中的二叉树就是一棵平衡二叉树。

分析:有了求二叉树深度的思路后,我们很快就能找到求解该问题的方法,就是从根结点开始开始判断其左右子结点的深度之差是否为1。如果从根结点开始到叶子结点的每一个结点的左右子树的深度相差都不超过1,则说明该二叉树是平衡二叉树。但是其时间复杂度接近O(N*N),因为里面有重复的遍历和访问。例如我们在判断8这个根结点是否是平衡的时候,我们需要判断其左右的两棵子树深度的差,需遍历3,5,7,6,但是当我们判断5结点是不是平衡的时候,我们还得遍历3,7,6这三个节点,所以重复的遍历使效率十分低下。还记得面试12(书上是面试9)中的斐波拉切数列的求法吗?典型的递归,但是那里的递归效率不高,和此题有相同的问题。

具体实现代码如下:

#include <iostream>
using namespace std;
struct BinaryTree
{
	int data;
	BinaryTree *pLeft;
	BinaryTree *pRight;
};
BinaryTree *pRoot1=NULL;
int arr[7]={8,5,15,3,7,16,6};
void InserTree(BinaryTree **root,int data)
{
	BinaryTree *temp=new BinaryTree;
	temp->data=data;
	temp->pLeft=temp->pRight=NULL;
	if(NULL==*root)
	{
		*root=temp;
	}
	else
	{
		BinaryTree *current=*root;
		BinaryTree *back=NULL;
		while(current)
		{
			back=current;
			if(data > current->data)
				current=current->pRight;
			else
				current=current->pLeft;
		}
		if(NULL==back->pLeft)
			back->pLeft=temp;
		else
			back->pRight=temp;
	}
}
void CreateTreeLoop(BinaryTree **root,int *array,int length)
{
	for(int i=0;i<length;i++)
		InserTree(root,array[i]);
}
void Inorder(BinaryTree *root)
{
	if(NULL!=root)
	{
		Inorder(root->pLeft);
		cout<<root->data<<" ";
		Inorder(root->pRight);
	}
}
int TreeDepth(BinaryTree*root)
{
	if(NULL==root)
		return 0;
	int left=TreeDepth(root->pLeft);
	int right=TreeDepth(root->pRight);
	return left>right?(left+1):(right+1);
}
bool IsBalance(BinaryTree *root)
{
	if(NULL==root)
		return true;
	int leftIsBla=TreeDepth(root->pLeft);
	int rightIsBla=TreeDepth(root->pRight);
	int diff=leftIsBla-rightIsBla;
	if(diff>1 || diff<-1  )
		return false;
	return IsBalance(root->pLeft) && IsBalance(root->pRight);
}

int main()
{
	CreateTreeLoop(&pRoot1,arr,7);
	cout<<"中序遍历:";
	Inorder(pRoot1);
	cout<<endl;
	bool result=IsBalanced(pRoot1);
	if(result)
		cout<<"The tree is balance!"<<endl;
	else
		cout<<"The tree is not a balance tree!"<<endl;
	system("pause");
	return 0;
}

运行结果:

基于上面重复遍历效率较低的问题,我们对其进行了改进,通过后续遍历,从叶子结点->到根结点开始判断该树是不是平衡的。因为当我们在遍历某节点的左右子节点的左右子节点是否平衡后,我们相应的就得到了当前节点的深度,我们就不用重复遍历该树的一些节点,当最后遍历到树的根结点的时候,也就判断了整棵二叉树是不是平衡二叉树。

其是实现关键代码如下:

bool IsBalanceHelp(BinaryTree *root,int *depth)
{
	if(root==NULL)
	{
		*depth=0;
		return true;
	}
	int left,right;
	if(IsBalanceHelp(root->pLeft,&left)&& IsBalanceHelp(root->pRight,&right))
	{
		int diff=left-right;
		if(diff<=1 && diff>=-1)
		{
			*depth=1+(left>right?left:right); //当遍历每个结点时,记录下该结点的深度,下次就不用再遍历已经遍历过的结点;
			return true;
		}
	}
	return false;
}
bool IsBalanced(BinaryTree *root)
{
	int depth=0;
	return IsBalanceHelp(root,&depth);
}

次种算法避免了不必要的重复计算,对于数据量大的二叉树效率明显提高。

时间: 2024-10-20 14:26:15

《剑指offer》:[39-1]判断是否为平衡二叉树的相关文章

剑指offer (39) 二叉树深度

题目:输入一棵二叉树的根节点,求该树的深度 题解分析: 二叉树具有天然的递归性,首先应该想递归解法 /** * Definition for binary tree * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: int maxDept

【Java】 剑指offer(39) 数组中出现次数超过一半的数字

本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1, 2, 3, 2, 2, 2, 5, 4, 2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. 思路 思路一:数字次数超过一半,则说明:排序之后数组中间的数字一定就是所求的数字. 利用partition()函数获得某一随机数字,其余数字按大小排在该数字的左右.若该

[剑指Offer] 39.平衡二叉树

题目描述 输入一棵二叉树,判断该二叉树是否是平衡二叉树. 1 class Solution { 2 public: 3 int Get_Height(TreeNode* root) { 4 if(root == NULL){ 5 return 0; 6 } 7 int LeftHeight = Get_Height(root->left); 8 int RightHeight = Get_Height(root->right); 9 return max(LeftHeight, RightHe

【剑指Offer】面试题55 - II. 平衡二叉树

题目 输入一棵二叉树的根节点,判断该树是不是平衡二叉树.如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树. 示例 1: 给定二叉树 [3,9,20,null,null,15,7] 3 / 9 20 / 15 7 返回 true . 示例 2: 给定二叉树 [1,2,2,3,3,null,null,4,4] 1 / 2 2 / 3 3 / 4 4 返回?false . 限制: 1 <= 树的结点个数 <= 10000 本题同[LeetCode]110. 平衡二叉树 思

《剑指offer》面试题39 二叉树的深度(java)

摘要: 今天翻到了<剑指offer>面试题39,题目二中的解法二是在函数的参数列表中通过指针的方式进行传值,而java是没有指针的,所以函数要进行改造.然而我翻了下别人的java版本(我就想看看有什么高大上的改造,毕竟要传递多个参数,是不是会涉及到那么一点点设计模式呢?),简直不能忍了,我只能用一句话形容:"一本正经的胡说八道",不过我就是喜欢看你胡说八道还迷之自信的样子. 下面吐槽一下这个版本的java代码: 1 //高效率的判断是否是一棵平衡二叉树 2 public b

剑指 Offer 题目汇总索引

剑指 Offer 总目录:(共50道大题) 1. 赋值运算符函数(或应说复制拷贝函数问题) 2. 实现 Singleton 模式 (C#) 3.二维数组中的查找 4.替换空格               时间:O(n) 空间:O(1) 5.从尾到头打印链表 6. 重建二叉树          && 二叉树的各种遍历(BFS,DFS,DLR,LDR,LRD) 7.用两个栈实现队列 8.旋转数组的最小数字 9.斐波那契数列第 n 项        时间O(lgn) 10.一个整数的二进制表示中

剑指offer编程题Java实现——面试题12相关题大数的加法、减法、乘法问题的实现

用字符串或者数组表示大数是一种很简单有效的表示方式.在打印1到最大的n为数的问题上采用的是使用数组表示大数的方式.在相关题实现任意两个整数的加法.减法.乘法的实现中,采用字符串对大数进行表示,不过在具体的计算中,还是要将字符串转化成字符数组来进行计算. 实现两个大数的加法,要考虑到两个问题,两个数的和的位数问题,以及如何处理两个数按位相加产生的进位问题.首先两个整数相加,两个数的和的位数最多比最大的整数的位数多1:这样和的位数就确定了.对于进位问题,我的做法是先进行按位相加,相加操作完成后再按照

剑指offer (2) c++实现singleton模式

转自:http://www.jellythink.com/archives/82 问题描述 现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能:在实际开发过程中,会专门有一个日志模块,负责写日志,由于在系统的任何地方,我们都有可能要调用日志模块中的函数,进行写日志.那么,如何构造一个日志模块的实例呢?难道,每次new一个日志模块实例,写完日志,再delete,不要告诉我你是这么干的.在C++中,可以构造一个日志模块的全局变量,那么在任何地方就都可以用了,是的,不错.但是

剑指OFFER之二叉树的镜像(九度OJ1521)

题目描述: 输入一个二叉树,输出其镜像. 输入: 输入可能包含多个测试样例,输入以EOF结束.对于每个测试案例,输入的第一行为一个整数n(0<=n<=1000,n代表将要输入的二叉树节点的个数(节点从1开始编号).接下来一行有n个数字,代表第i个二叉树节点的元素的值.接下来有n行,每行有一个字母Ci.Ci='d'表示第i个节点有两子孩子,紧接着是左孩子编号和右孩子编号.Ci='l'表示第i个节点有一个左孩子,紧接着是左孩子的编号.Ci='r'表示第i个节点有一个右孩子,紧接着是右孩子的编号.C