现在的位置: 首页 > 综合 > 正文

编程之美系列之二叉树2—二叉树的距离问题(续)

2017年10月17日 ⁄ 综合 ⁄ 共 3320字 ⁄ 字号 评论关闭

前面一篇博客:http://blog.csdn.net/kay_zhyu/article/details/8867839讲了二叉树的一些基本的操作,鉴于内容有点多,所以分成两个,这一部分主要是将怎么求给定的两个节点之间的距离。有两种情况:
3、如果很频繁的询问两个节点的距离,那么就用一个二维数组来维护这个距离,询问的时间就是O(1).当然,如果只是一次询问,那这样做就很费时。
OK,先不管那么多,先说说怎么去维护任意两个节点的距离。假设我需要求b和e两个节点的距离。那么他们之间的最小距离也就是b到e的父节点和e到b的父节点的距离+1,即dis[b][e]=min(dis[b][parent[e]],dis[parent[b]][e])+1。好吧,被你发现了,其实这就是动态规划的思想啦。初始条件是什么?当然是自己到自己的距离为0,到其它点的距离是一个比较大的数咯!废话不多说,直接上代码:

//情况一的代码:
#include<stdio.h>
#include<string.h>
struct NODE
{
	int data;
	NODE *Left;
	NODE *Right;
};
const int N = 20;
const int Inf = 1e5;
NODE node[N];
int parent[N];//节点的父节点
int dis[N][N];//已访问过的节点到未访问过的节点的距离
inline int min(const int a, const int b)
{
	return a < b ? a : b;
}
void FindDis(int root)//得到任意两点的距离矩阵
{
	NODE *Queue[N];
	int head,tail;
	NODE *p;
	memset(parent, -1, sizeof(parent));
	memset(dis, 1, sizeof(dis));
	head = tail = 0;
	int i;
	for(i = 0; i < N; ++i)
		dis[i][i] = 0;
	Queue[head] = &node[root];
	parent[root] = root;
	while(head <= tail)
	{
		p = Queue[head];
		if(p->Left != NULL)
		{
			Queue[++tail] = p->Left;
			parent[p->Left->data] = p->data;
			dis[p->Left->data][p->data] = dis[p->data][p->Left->data] = 1;
		}
		if(p->Right != NULL)
		{
			Queue[++tail] = p->Right;
			parent[p->Right->data] = p->data;
			dis[p->Right->data][p->data] = dis[p->data][p->Right->data] = 1;
		}
		//更新访问过的节点与新加入的节点的距离
		for(i = 0; i <	N; ++i)
		{		
			//该节点访问过       节点的父节点不是p        节点自己不是p
			if(parent[i] > -1 && parent[i] != p->data && i != p->data)
			{
				if(i == root)
					dis[i][p->data] = dis[i][parent[p->data]] + 1;
				else if(p->data == root)
					dis[i][p->data] = dis[parent[i]][p->data] + 1;
				else				
					dis[i][p->data] = min(dis[parent[i]][p->data], dis[i][parent[p->data]]) + 1;
				dis[p->data][i] = dis[i][p->data];
			}			
		}
		++head;
	}
}

4、OK,现在拿出我的杀手锏...................如果只是单次的求两个节点的距离怎么办呢?怎么样使最快的!!!嘿嘿,代码写出来之后再说啦!!
好,这个问题中根节点到目标节点的路径可以抽象成一个链表。怎么抽象?其实就是树的前序遍历,想想,好像是这样的耶!OK,那问题就转化成两个链表之间求最后一个公共节点的问题了。我觉得我有必要写一个链表求公共节点的专题。好吧。额......这个等有空了再说吧。不过写好之后,会再链接发过来的。

//情况二的代码#include<stdio.h>
bool used[N];//标记节点是否被访问过
//其他的变量定义跟前面一样,这里加了一个used数组
//用前序遍历来抽取根节点到目标节点的路径
int FindRoad(NODE **stack, int root, int n)
{
	int top = -1;
	NODE *p = &node[root];
	memset(used, 0, sizeof(used));
	while(top > -1 || p != NULL)
	{
		while(p != NULL)
		{
			stack[++top] = p;
			if(p->data == n)
				return top;
			p = p->Left;
		}
		while(top > -1 && used[stack[top]->data])
			--top;
		if(top > -1)
		{
			p = stack[top]->Right;
			used[stack[top]->data] = true;
		}
	}
	return -1;//未找到
}
//计算两个节点之间的距离
int FindDis(int root, int b, int e)
{
	NODE *stack1[N];
	NODE *stack2[N];
	int top1,top2;
	int i,j;
	//找到根节点到b的路径,保存在stack1中
	top1 = FindRoad(stack1, root, b);
	top2 = FindRoad(stack2, root, e);
	if(top1 < 0 || top2 < 0)//如果不在树中,返回不可达
		return Inf;
	for(i = 0, j = 0; i <= top1 && j <= top2; ++i, ++j)
	{
		if(stack1[i] != stack2[j])
			break;
	}
	int nLen = 0;
	if(i <= top1)
		nLen += top1 - i + 1;
	if(j <= top2)
		nLen += top2 - j + 1;
	return nLen;
}

5、如果不是求树中的距离,而是需要找到数组符合条件的路径呢?参考另一篇博客:http://blog.csdn.net/kay_zhyu/article/details/8870048OK,为了方便,下面给出两种情况下的main函数的调用,如果不需要可以直接pass掉这一部分。

//情况1的main函数
int main()
{
	int n,i,m;
	int root,num,r;//r为1表示左节点,0表示右节点
	int nLen;
	while(scanf("%d %d", &n, &m) != EOF)
	{
		memset(node, 0, sizeof(node));
		nLen = 0;
		for(i = 0; i < n; ++i)
		{
			scanf("%d %d %d", &r, &root, &num);
			Add(root, num, r);
		}
		FindDis(0);//数据输入保证以0作为根节点
		for(i = 0; i < m; ++i)
		{
			printf("Enter the b and e:");
			scanf("%d %d", &num, &r);
			//其实需要判断一下节点在不在树中,其实也很简单,直接判断parent是不是大于0
			printf("节点%d与节点%d的距离为%d\n", num, r, dis[num][r]);
		}
	}
}
//情况二的main函数
int main()
{
	int n,i,m;
	int root,num,r;//r为1表示左节点,0表示右节点
	int nLen;
	while(scanf("%d %d", &n, &m) != EOF)
	{
		memset(node, 0, sizeof(node));
		nLen = 0;
		for(i = 0; i < n; ++i)
		{
			scanf("%d %d %d", &r, &root, &num);
			Add(root, num, r);
		}	
		for(i = 0; i < m; ++i)
		{
			printf("Enter the b and e:");
			scanf("%d %d", &num, &r);
			nLen = FindDis(0, num, r);//数据输入保证以0作为根节点
			printf("节点%d与节点%d的距离为%d\n", num, r, nLen);
		}
	}
}

抱歉!评论已关闭.