程序员面试金典: 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
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞