您的位置:首页 > 理论基础 > 数据结构算法

数据结构和算法经典100题-第11题

2015-05-09 22:34 239 查看
题目要求:

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

如果我们把二叉树看成一个图,父子节点之间的连线看成是双向的,我们姑且定义”距离”为两节点之间边的个数。

写一个程序:

求一棵二叉树中相距最远的两个节点之间的距离?

题目分析:

一棵二叉树求两个结点之间的边数,按照树的平衡性分为两种情况:

1.一棵平衡或接近平衡的树,平衡的树如下图caseA所示,从这个图中可以很明显的看出最短的距离是通过树的根结点的左右子树上两个结点的距离,如左图中红线连接在一起的两结点的距离。

2.一棵极不平衡的树如图caseB种所示,从caseB中可以看到距离最远的两个结点分布在树根结点的一个子树上,如右图中红线连接起来的两个结点。

从这两种情况继续分析:

caseA最大距离是:左子树深度+右子树深度+2

caseB最大距离是:根节点的左右子树中的某子树最大距离;于是根节点转移到某子树中,可以看出根节点是在不断向下转移,可以将此问题转化成掰树枝的过程。

综合以上两种情况的分析可以得出如下结论:

最大距离一定是以树上的某个节点为根的左子树的深度+右子树的深度+2;

从这里可以看出在掰树枝的过程中,循环的判断的两个变量是左右子树的深度上一层树枝的最大距离。分析到这里写代码已经不难了。



那么我们看一下代码,头文件:

//
//  11th.h
//  100-alg-tests
//
//  Created by bobkentt on 15-5-4.
//  Copyright (c) 2015年 kedong. All rights reserved.
//

#ifndef ___00_alg_tests___1th__
#define ___00_alg_tests___1th__

#include <stdio.h>

struct node {
int value;
node* left;
node* right;
};

struct distenceMax
{
int depthMax;   /* 树的深度 */
int distenceMax;/* 树结点中的最大距离 */
};

/* 代表一棵二叉树 */
class BinaryTree {

node* m_root; /* 树的根结点 */

/* 递归求解树德最大距离 */
distenceMax findDistence(node* obj);

public:
BinaryTree(node* obj) : m_root(obj) { };

~BinaryTree() { };

/* 包裹方法,封装递归调用过程 */
long getMaxDistence() { \
distenceMax max = findDistence(m_root); \
return (long)max.distenceMax;
};
};

int test_11();

#endif /* defined(___00_alg_tests___1th__) */


首先定义了树的结点。然后定义了树用于迭代的结构。接下来定义类,代表一棵二叉树。定义包裹函数封装了迭代查找最大距离的函数。

再看一下查找最大距离的成员函数的定义:

distenceMax BinaryTree::findDistence(node* obj) {
#define MAX(a,b) ((a)>(b)) ? (a) : (b)

distenceMax dMax;

/* leaf node,return: depthMax=-1,distenceMax=0 */
if (NULL == obj ) {
dMax.depthMax = -1;
dMax.distenceMax = 0;
return dMax;
}

distenceMax dLeft = findDistence(obj->left);
distenceMax dRight = findDistence(obj->right);
dMax.depthMax = MAX(dLeft.depthMax+1,dRight.depthMax+1);
dMax.distenceMax = MAX(MAX(dLeft.distenceMax, dRight.distenceMax),dLeft.depthMax + dRight.depthMax +2);

return dMax;
}


以上定义的迭代函数如上面所说的思路,这里不详细介绍。我们再看一下测试程序。在测试程序中,我们定义两棵树,测试他们最大的深度。

我们看一下测试代码:

void Link(node* obj, int parrent, int left, int right) {

if (-1 != left) {
obj[parrent].left = &obj[left];
}

if (-1 != right) {
obj[parrent].right = &obj[right];
}

return ;
}

int test_11()
{
// case 1:tree like below,and the max distence of the tree is  6
/*       0
/    \
1        2
/ \      / \
3   4    5   6
/          \
7            8
*/
node obj_1[9];
memset(obj_1, 0, sizeof(node)*9);
Link(obj_1, 0, 1, 2);
Link(obj_1, 1, 3, 4);
Link(obj_1, 2, 5, 6);
Link(obj_1, 3, 7, -1);
Link(obj_1, 5, -1, 8);
BinaryTree treeCase_1(&obj_1[0]);
long distence = treeCase_1.getMaxDistence();
cout<<"distence = "<<distence<<endl;

// case 2:tree like below,the max distence of the tree is eque 6
/*        0
\
1
/ \
2   3
/   / \
4   5   6
/     \
7       8

*/
node obj_2[9];
Link(obj_2, 0, -1, 1);
Link(obj_2, 1, 2, 3);
Link(obj_2, 2, 4, -1);
Link(obj_2, 3, 5, 6);
Link(obj_2, 4, 7, -1);
Link(obj_2, 5, -1, 8);
BinaryTree treeCase_2(&obj_2[0]);
distence = treeCase_2.getMaxDistence();
cout<<"distence = "<<distence<<endl;

return 0;
}

int main(int argc, const char * argv[]) {

test_11();
return 0;
}


测试代码中Link函数负责构建二叉树中各个结点之间的关系。

最后看一下测试函数的打印输出:

distence = 6
distence = 6
Program ended with exit code: 0


补充说明一下:

此题在编程之美上也有,编程之美上面给出了更详细的分析,这个题目JULY大神也给过很好的答案。这里贴上JULY大神给出的答案,答案如下:

// 数据结构定义
struct NODE
{
NODE* pLeft;        // 左子树
NODE* pRight;       // 右子树
int nMaxLeft;       // 左子树中的最长距离
int nMaxRight;      // 右子树中的最长距离
char chValue;       // 该节点的值
};

int nMaxLen = 0;

// 寻找树中最长的两段距离
void FindMaxLen(NODE* pRoot)
{
// 遍历到叶子节点,返回
if (pRoot == NULL)
{
return ;
}

// 如果左子树为空,那么该节点的左边最长距离为0
if (pRoot -> pLeft == NULL)
{
pRoot -> nMaxLeft = 0;
}

// 如果右子树为空,那么该节点的右边最长距离为0
if (pRoot -> pRight == NULL)
{
pRoot -> nMaxRight = 0;
}

// 如果左子树不为空,递归寻找左子树最长距离
if (pRoot -> pLeft != NULL)
{
FindMaxLen(pRoot -> pLeft);
}

// 如果右子树不为空,递归寻找右子树最长距离
if (pRoot -> pRight != NULL)
{
FindMaxLen(pRoot -> pRight);
}

// 计算左子树最长节点距离
if (pRoot -> pLeft != NULL)
{
int nTempMax = 0;
if (pRoot -> pLeft -> nMaxLeft > pRoot -> pLeft -> nMaxRight)
{
nTempMax = pRoot -> pLeft -> nMaxLeft;
}
else
{
nTempMax = pRoot -> pLeft -> nMaxRight;
}
pRoot -> nMaxLeft = nTempMax + 1;
}

// 计算右子树最长节点距离
if (pRoot -> pRight != NULL)
{
int nTempMax = 0;
if (pRoot -> pRight -> nMaxLeft > pRoot -> pRight -> nMaxRight)
{
nTempMax = pRoot -> pRight -> nMaxLeft;
}
else
{
nTempMax = pRoot -> pRight -> nMaxRight;
}
pRoot -> nMaxRight = nTempMax + 1;
}

// 更新最长距离
if (pRoot -> nMaxLeft + pRoot -> nMaxRight > nMaxLen)
{
nMaxLen = pRoot -> nMaxLeft + pRoot -> nMaxRight;
}
}

**评论**
编程之美的答案中,本人认为也不是最完美的。主要有以下几个原因:

1.在数据结构的定义部分,引入了副作用的两个变量int nMaxLeft,int nMaxRight。这两个变量的加入,降低了代码的抽象性。若只是想往一棵普通的二叉树加入能查找最大距离的方法,树在之前已经有一些其他的功能,那么很明显,修改数据结构不是十分合适的。还有就是这样增加了算法的空间复杂度(具体不讨论)。
2.算法中有过多的空判断,使代码不够清晰,逻辑也相对复杂;
3.使用了全局变量nMaxLen,每次使用需要额外赋值,而且造成了线程的不安全。

这个想法是本人自己有的,后来看了Milo Yip在2010年的一个博客,发现自己的想法和他的想法不谋而合,而且上面的caseA和caseB两种情况的图,还引用了他的图。
这里就留一下他的那篇博文的地址吧: http://www.cnblogs.com/miloyip/archive/2010/02/25/binary_tree_distance.html


路漫漫其修远兮,吾将上下而…
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息