您的位置:首页 > 职场人生

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

2016-12-24 17:27 369 查看
#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐