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

来源:互联网 发布:局域网ip mac扫描导出 编辑:程序博客网 时间:2024/05/21 17:28

问题定义

把二叉树看成一个图,父子节点之间的连线看成是双向的,定义“距离”为两个节点之间的边数。例如下图中最大距离为红线的条数为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)。





0 0