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

来源:互联网 发布:windows激活不可用 编辑:程序博客网 时间:2024/06/03 16:55

前面一篇博客: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        节点自己不是pif(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;elsedis[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是不是大于0printf("节点%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);}}}
原创粉丝点击