程序员面试金典: 9.4树与图 4.1实现一个函数检查二叉树是否平衡。

#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>

using namespace std;

/*
问题:实现一个函数,检查二叉树是否平衡。在这个问题中,平衡树的定义如下:任意一个节点,其两颗子树的高度差不超过1.
分析:关于树的大部分问题都是和递归相关。
      所以如果一棵树的左子树的高度和右子树各自平衡,且这两颗子树的高度之差不超过1即可。
	  递归基:
	  isAVLTree(Tree* head)
	  {
	     //如果还树为空,一定是平衡的
		 if(NULL == head)
		 {
		   return true
		 }
	  }

	  这里的关键是需要记录树的高度,最好能做成根据节点就能知道高度

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

样例输入:
6
8 6 5 7 10 9
d 2 5 
d 3 4 
z 
z 
l 6 
z 


9 
8 6 5 7 10 9 11 13 15
d 2 5 
d 3 4 
z 
z 
d 6 7 
l 8
z 
r 9
z

样例输出:
Is AVL Tree!
Not AVL Tree!


关键:
1 构建树的节点,采用的是直接静态创建N个节点,每次创建节点,就是把静态节点数组中某个节点地址分配给它
对于某个节点的子节点,则直接根据子节点下标,取出静态节点数组中对应节点地址

TreeNode g_treeNodeArray[MAXSIZE];
int g_index; //用于标示创建的节点的下标

//创建节点
TreeNode* createNode()
{
	//先使节点标记累加,初始化节点中左右孩子的值为NULL
	++g_index;
	g_treeNodeArray[g_index]._pLeft = g_treeNodeArray[g_index]._pRight = g_treeNodeArray[g_index]._pParent = NULL;
	return &g_treeNodeArray[g_index];
}

2 判断平衡树中需要计算左右子树的高度,注意不是左右孩子节点的高度
int getTreeHeight(TreeNode* pNode)
{
	if(NULL == pNode)
	{
		return 0;
	}
	int leftHeight = getTreeHeight(pNode->_pLeft) + 1;
	int rightHeight = getTreeHeight(pNode->_pRight) + 1;
	int height = leftHeight > rightHeight ? leftHeight : rightHeight;
	return height;
} 

3判断平衡树除了递归基放在前面,判定条件放在中间,而对左右子树判断是否平衡要放在最后,这是因为,如果把判断左右子树
平衡放在中间,那么判定条件在最后面起不到作用
		int leftHeight= getTreeHeight(head->_pLeft);
		int rightHeight = getTreeHeight(head->_pRight);
		int result = abs(leftHeight - rightHeight);
		if(result > 1)
		{
			return false;
		}
		//返回两个子树是否平衡
		else
		{
			return isAVLTree(head->_pLeft) && isAVLTree(head->_pRight);
		}

*/

typedef struct TreeNode
{
	TreeNode():_value(-1),_height(-1),_pLeft(NULL),_pRight(NULL),_pParent(NULL){}
	int _value;
	int _height; //该节点距离树顶部的高度,第一层高度为1,默认初始化树的根节点高度为1
	//树中肯定包含左子树和右子树,还包括当前节点自身的值
	TreeNode* _pLeft;
	TreeNode* _pRight;
	TreeNode* _pParent; // 父节点,根节点的父节点为空,高度
	int _index; //创建的节点的下标
}TreeNode;

//关键就设置一个节点数组
const int MAXSIZE = 1001;
TreeNode g_treeNodeArray[MAXSIZE];
int g_index; //用于标示创建的节点的下标

//创建节点
TreeNode* createNode()
{
	//先使节点标记累加,初始化节点中左右孩子的值为NULL
	++g_index;
	g_treeNodeArray[g_index]._pLeft = g_treeNodeArray[g_index]._pRight = g_treeNodeArray[g_index]._pParent = NULL;
	return &g_treeNodeArray[g_index];
}



//获取当前节点高度,仍然是用递归做
int getHeight(TreeNode* pNode)
{
	//如果当前节点为空,高度为0? ,这个有问题,应该修改为如果当前节点为空,其高度等于其父节点高度
	//参见这种情况:一个父节点只有左孩子,父节点高度为h,左孩子高度则为h+1,如果把父节点的右孩子(实际为空)的高度设置为0,会有可能判断得出为非平衡树
	//此时应该设置空节点高度为其父节点高度,但不对,当前节点都为空了,怎么可能获取到父节点,只能在调用的地方控制,做非空检查,如果为空,就用其父节点计算其高度
	if(NULL == pNode)
	{
		return 0;
	}
	//记忆化搜索
	if(pNode->_height != -1)
	{
		return pNode->_height;
	}
	//如果是遇到根节点,那么根节点对应高度为0
	else if(pNode->_pParent == NULL)
	{
		return 0;
	}
	else
	{
		return getHeight(pNode->_pParent) + 1;
	}
}

//获取树的顶点
TreeNode* getTreeHeadNode(TreeNode* pNode)
{
	//如果当前传入的节点为空,就直接返回该树的顶点为空
	if(NULL == pNode)
	{
		return NULL;
	}

	//根据树的顶点没有父节点,因此如果某个节点的父节点为空,该节点就是树的顶点
	if(NULL == pNode->_pParent)
	{
		return pNode;
	}
	return getTreeHeadNode(pNode->_pParent);
}

//其实是计算左子树高度和右子树高度,而不是左孩子和右孩子高度;这个函数作用是计算以pNode为顶点的树的高度(左右子树中的较大值)
int getTreeHeight(TreeNode* pNode)
{
	if(NULL == pNode)
	{
		return 0;
	}
	int leftHeight = getTreeHeight(pNode->_pLeft) + 1;
	int rightHeight = getTreeHeight(pNode->_pRight) + 1;
	int height = leftHeight > rightHeight ? leftHeight : rightHeight;
	return height;
}

bool isAVLTree(TreeNode* head)
{
	//如果还树为空,一定是平衡的
	if(NULL == head)
	{
		return true;
	}
	//如果左子树和右子树都不空,那么需要判断两者的高度之差是否<=1,并且两颗子树都需要平衡,必须要记录高度
	else
	{
		int leftHeight= getTreeHeight(head->_pLeft);
		int rightHeight = getTreeHeight(head->_pRight);
		int result = abs(leftHeight - rightHeight);
		if(result > 1)
		{
			return false;
		}
		//返回两个子树是否平衡
		else
		{
			return isAVLTree(head->_pLeft) && isAVLTree(head->_pRight);
		}
	}
}

int main(int argc, char* argv[])
{
	int nodeNum;
	int value;
	string childFlag;
	int leftChild;
	int rightChild;
	while(cin >> nodeNum)
	{
		g_index = 0;

		//输入n个树节点对应的值,实际上就是创建节点
		for(int i = 0 ; i < nodeNum ; i++)
		{
			cin >> value;
			TreeNode* pNode = createNode();
			pNode->_value = value;
		}

		//接下来就是对n个节点的孩子节点进行赋值
		for(int i = 1 ; i <= nodeNum ; i++)
		{
			cin >> childFlag;
			if("d" == childFlag)
			{
				cin >> leftChild >> rightChild;
				g_treeNodeArray[i]._pLeft = &g_treeNodeArray[leftChild];
				g_treeNodeArray[i]._pRight = &g_treeNodeArray[rightChild];

				//注意设置父节点的指向,否则无法从任意节点推算根节点
				g_treeNodeArray[leftChild]._pParent = &g_treeNodeArray[i];
				g_treeNodeArray[rightChild]._pParent = &g_treeNodeArray[i];
			}
			else if("l" == childFlag)
			{
				cin >> leftChild;
				g_treeNodeArray[i]._pLeft = &g_treeNodeArray[leftChild];
				g_treeNodeArray[leftChild]._pParent = &g_treeNodeArray[i];
			}
			else if("r" == childFlag)
			{
				cin >> rightChild;
				g_treeNodeArray[i]._pRight = &g_treeNodeArray[rightChild];
				g_treeNodeArray[rightChild]._pParent = &g_treeNodeArray[i];
			}
			//没有孩子节点无需处理
			else if("z" == childFlag)
			{

			}
			else
			{
				cout << "command is error!" << endl;
			}
		}

		//获取树的顶点
		TreeNode* pHead = getTreeHeadNode( &g_treeNodeArray[1] );

		//高度验证正确后,判断平衡树就可以了
		bool isAVL = isAVLTree(pHead);
		if(isAVL)
		{
			cout << "Is AVL Tree!" << endl;
		}
		else
		{
			cout << "Not AVL Tree!" << endl;
		}
	}
	getchar();
	return 0;
}
    原文作者:平衡二叉树
    原文地址: https://blog.csdn.net/qingyuanluofeng/article/details/53861169
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞