二叉树中节点的最大的距离

Posted taxue505

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树中节点的最大的距离相关的知识,希望对你有一定的参考价值。

问题定义

把二叉树看成一个图,父子节点之间的连线看成是双向的,定义“距离”为两个节点之间的边数。例如下图中最大距离为红线的条数为6.

分析


方法一、

typedef struct Node   
    struct Node *pleft;     //左孩子  
    struct Node *pright;    //右孩子  
    char chValue;           //该节点的值  
  
    int leftMaxValue;       //左子树最长距离  
    int rightMaxValue;      //右子树最长距离  
LNode, *BinTree;  
  
void findMaxLen(BinTree root, int *maxLen)   
    //遍历到叶子结点,返回  
    if(root == NULL)  
        return;  
  
    //如果左子树为空,那么该节点左边最长距离为0  
    if(root->pleft == NULL)  
        root->leftMaxValue = 0;  
  
    //如果右子树为空,那么该节点右边最长距离为0  
    if(root->pright == NULL)  
        root->rightMaxValue = 0;  
  
    //如果左子树不为空,递归寻找左子树最长距离  
    if(root->pleft != NULL)  
        findMaxLen(root->pleft, maxLen);  
  
    //如果右子树不为空,递归寻找右子树最长距离  
    if(root->pright != NULL)  
        findMaxLen(root->pright, maxLen);  
  
    //计算左子树中距离根节点的最长距离  
    if(root->pleft != NULL)   
        if(root->pleft->leftMaxValue > root->pleft->rightMaxValue)  
            root->leftMaxValue = root->pleft->leftMaxValue + 1;  
        else  
            root->leftMaxValue = root->pleft->rightMaxValue + 1;  
      
  
    //计算右子树中距离根节点的最长距离  
    if(root->pright != NULL)   
        if(root->pright->leftMaxValue > root->pright->rightMaxValue)  
            root->rightMaxValue = root->pright->leftMaxValue + 1;  
        else  
            root->rightMaxValue = root->pright->rightMaxValue + 1;  
      
  
    //更新最长距离  
    if(root->leftMaxValue + root->rightMaxValue > *maxLen)  
        *maxLen = root->leftMaxValue + root->rightMaxValue;  
  

int *maxLen中始终存储的是当前两个节点间的最远距离,在遍历的过程中更新。

下面的程序描述是为了测试上面的代码是否正确,包括建立二叉树,销毁二叉树,打印二叉树:

#include <stdlib.h>  
#include <stdio.h>  
  
typedef struct Node   
    struct Node *pleft;     //左孩子  
    struct Node *pright;    //右孩子  
    char chValue;           //该节点的值  
  
    int leftMaxValue;       //左子树最长距离  
    int rightMaxValue;      //右子树最长距离  
LNode, *BinTree;  
  
void findMaxLen(BinTree root, int *maxLen)   
    //遍历到叶子结点,返回  
    if(root == NULL)  
        return;  
  
    //如果左子树为空,那么该节点左边最长距离为0  
    if(root->pleft == NULL)  
        root->leftMaxValue = 0;  
  
    //如果右子树为空,那么该节点右边最长距离为0  
    if(root->pright == NULL)  
        root->rightMaxValue = 0;  
  
    //如果左子树不为空,递归寻找左子树最长距离  
    if(root->pleft != NULL)  
        findMaxLen(root->pleft, maxLen);  
  
    //如果右子树不为空,递归寻找右子树最长距离  
    if(root->pright != NULL)  
        findMaxLen(root->pright, maxLen);  
  
    //计算左子树中距离根节点的最长距离  
    if(root->pleft != NULL)   
        if(root->pleft->leftMaxValue > root->pleft->rightMaxValue)  
            root->leftMaxValue = root->pleft->leftMaxValue + 1;  
        else  
            root->leftMaxValue = root->pleft->rightMaxValue + 1;  
      
  
    //计算右子树中距离根节点的最长距离  
    if(root->pright != NULL)   
        if(root->pright->leftMaxValue > root->pright->rightMaxValue)  
            root->rightMaxValue = root->pright->leftMaxValue + 1;  
        else  
            root->rightMaxValue = root->pright->rightMaxValue + 1;  
      
  
    //更新最长距离  
    if(root->leftMaxValue + root->rightMaxValue > *maxLen)  
        *maxLen = root->leftMaxValue + root->rightMaxValue;  
  
  
//创建二叉树  
void buildBinTree(BinTree *root)  
  
    char ch;  
    scanf("%c", &ch);    //输入一个元素  
    fpurge(stdin);  
    if(ch == ' ')        //若输入的是空格符,表明二叉树为空,置*root为NULL  
        *root = NULL;  
    else                //若输入的不是空格符,则将该值赋值给根节点的chValue, 递归建立左子树和右子树  
        *root = (BinTree)malloc(sizeof(LNode));  
        (*root)->chValue = ch;  
        (*root)->leftMaxValue = 0;  
        (*root)->rightMaxValue = 0;  
  
        buildBinTree(&(*root)->pleft);  
        buildBinTree(&(*root)->pright);  
      
  
  
//销毁二叉树,释放内存  
void destroyBinTree(BinTree *root)  
  
    if(*root != NULL)   
        destroyBinTree(&(*root)->pleft);  
        destroyBinTree(&(*root)->pright);  
  
        free(*root);  
        *root = NULL;  
      
  
  
//前序遍历二叉树  
void preOrderTraverse(BinTree root)  
  
    if(root != NULL)   
        preOrderTraverse(root->pleft);  
        printf("%c", root->chValue);  
        preOrderTraverse(root->pright);  
      
  
  
int main()   
    BinTree root;  
    buildBinTree(&root);  
    preOrderTraverse(root);  
    printf("\\n");  
    int maxLen = 0;  
    findMaxLen(root, &maxLen);  
    printf("maxLen = %d\\n", maxLen);  
    destroyBinTree(&root);  
  

这段代码有几个缺点:

  1. 算法加入了侵入式(intrusive)的资料nMaxLeft, nMaxRight
  2. 使用了全局变量 nMaxLen。每次使用要额外初始化。而且就算是不同的独立资料,也不能在多个线程使用这个函数
  3. 逻辑比较复杂,也有许多 NULL 相关的条件测试。

方法二、

定义:过以节点x作为根节点的子树中,节点间的最大距离为Dis(x)。

上图,左图中Dis(根节点)最大,右图中Dis(根节点->left)最大。从上边可以看出每个节点都可能成为最大距离根节点的潜质。

因此可以求出每个Dis(节点),从中得出最大值即为整个二叉树的根节点最大值。

在求过点x的最大距离时,最大距离的两个点有可能出现在三种情况下

  1. 左子树
  2. 右子树
  3. 过节点x

经分析得出以下特点

  1. 以上三种情况最终必定一叶子结束
  2. 在第三种情况下必然是左子树高度 与 右子树高度 之和(只有这样,才可能取得最大值)

经过以上分析即可得出递推式

Dis(x) = max(Dis(x->left), Dis(x->right), height(x->left)+height(x->right))

参考代码

int treeDistance(BiTree root)

    if(root == NULL)
        return 0;
    else if(root->left == NULL && root->right == NULL)
        return 0;
    int dis = max(height(root->left) + height(root->right), treeDistance(root->left), treeDistance(root->right));
    if(maxDis < dis)
        maxDis = dis;
    return dis;
这里用了一个技巧:maxDis是个全局变量,递归一次根节点会遍历到每个节点,在这期间于maxDis比较,从而得出了最大值,而不需要额外的空间。
完整运行代码

#include<iostream>
using namespace std;
typedef struct BiTNode

    BiTNode *left;
    BiTNode *right;
BiTNode, *BiTree;

int maxDis = 0;

void createTree(BiTree &root)

    BiTree left1 = new(BiTNode);
    BiTree right1 = new(BiTNode);
    
    left1->left = NULL;
    left1->right = NULL;
    right1->left = NULL;
    right1->right = NULL;

    root->left = left1;
    root->right = right1;


    BiTree left2 = new(BiTNode);
    left2->left = NULL;
    left2->right = NULL;
    BiTree right2 = new(BiTNode);
    right2->left = NULL;
    right2->right = NULL;
    left1->left = left2;
    left1->right = right2;

    BiTree left3 = new(BiTNode);
    left3->left = NULL;
    left3->right = NULL;
    BiTree right3 = new(BiTNode);
    right3->left = NULL;
    right3->right = NULL;
    left2->left = left3;
    left2->right = right3;


void deleteTree(BiTree root)

    if(root)
    
        deleteTree(root->left);
        deleteTree(root->right);
        delete(root);
        root = NULL;
    


int height(BiTree root)

    if(root == NULL)
        return 0;
    else
        return height(root->left) > height(root->right) ? height(root->left) + 1 : height(root->right) + 1;


int max(int a, int b, int c)

    int tmp = a > b ? a : b;
    return tmp > c ? tmp : c;


int treeDistance(BiTree root)

    if(root == NULL)
        return 0;
    else if(root->left == NULL && root->right == NULL)
        return 0;
    int dis = max(height(root->left) + height(root->right), treeDistance(root->left), treeDistance(root->right));
    if(maxDis < dis)
        maxDis = dis;
    return dis;


int main()

    BiTree root = new(BiTNode);
    root->right = root->left = NULL;
    createTree(root);    
    cout << "height:" << height(root) << endl;
    cout << "treeDistance:" << treeDistance(root) << endl;
    cout << "_____________________" << endl;
    deleteTree(root);
结果
4

方法三、


我认为这个问题的核心是,情况A 及 B 需要不同的信息: A 需要子树的最大深度,B 需要子树的最大距离。只要函数能在一个节点同时计算及传回这两个信息,代码就可以很简单:

#include <iostream>
 
using namespace std;
 
struct NODE

    NODE *pLeft;
    NODE *pRight;
;
 
struct RESULT

    int nMaxDistance;
    int nMaxDepth;
;
 
RESULT GetMaximumDistance(NODE* root)

    if (!root)
    
        RESULT empty =  0, -1 ;   // trick: nMaxDepth is -1 and then caller will plus 1 to balance it as zero.
        return empty;
    
 
    RESULT lhs = GetMaximumDistance(root->pLeft);
    RESULT rhs = GetMaximumDistance(root->pRight);
 
    RESULT result;
    result.nMaxDepth = max(lhs.nMaxDepth + 1, rhs.nMaxDepth + 1);
    result.nMaxDistance = max(max(lhs.nMaxDistance, rhs.nMaxDistance), lhs.nMaxDepth + rhs.nMaxDepth + 2);
    return result;

计算 result 的代码很清楚;nMaxDepth 就是左子树和右子树的深度加1;nMaxDistance 则取 A 和 B 情况的最大值。

为了减少 NULL 的条件测试,进入函数时,如果节点为 NULL,会传回一个 empty 变量。比较奇怪的是 empty.nMaxDepth = -1,目的是让调用方 +1 后,把当前的不存在的 (NULL) 子树当成最大深度为 0。

除了提高了可读性,这个解法的另一个优点是减少了 O(节点数目) 大小的侵入式资料,而改为使用 O(树的最大深度) 大小的栈空间。这个设计使函数完全没有副作用(side effect)。





以上是关于二叉树中节点的最大的距离的主要内容,如果未能解决你的问题,请参考以下文章

树形dp (二叉树中的最大路径最长距离)

二叉树内两个节点的最长距离

二叉树进阶之求一棵二叉树中结点间最大距离

二叉树上节点间的最大距离

11.求二叉树中节点的最大距离

求二叉树中节点的最大距离