参考资料:http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor#Another
这里加上了RQM转LCA,可以参考《RMQ转换成LCA <O(N), O(1)>》。
这个算法的思想与上一篇《LCA <O(N), O(sqrt(N))>》的思想一样:
1、求出树中每个节点的lev(树中的高度)以及每个节点的pare(节点的父亲)。
2、假设求a和b,当lev不同时,将lev大,即深一些的节点往上移,即转换成该节点的“父亲”,a=pare[a]。直到两个点的lev相同。此时,若a==b,实际上就是b就是a的祖先。
3、若此时a!=b,a、b、a和b的共同祖先c,这3点,就好比构成了一个“人”字型,a和b的lev相同。这时,可以采用逐步一层一层地往上跳,逼近公共祖先,但这样显然效率比较低。在这种算法里,先预处理一个p数组,p[i][j]表示i的第2^j(可恶的参考资料里掉了这个"^"符号,看了好久没懂)个祖先,转移方程为p[i][j] = p[p[i][j-1]][j-1],翻译一下意思就是,i的第2^j个祖先等于i的第2^(j-1)个祖先的第2^(j-1)个祖先,明白没?
在看一下的代码之前,再来看看强大的二进制与位运算:
设,a = 1101,b = 100,那么a>b,从a变到b,可以这么做:首先求出不大于a的最大2的阶数k,在这里,k就为3(2^3 < a < 2^4)。然后,i从k开始,依次减去1<<i,若减去后>=b,则a = a - (1<<i);若减去后<b则i--。代码如下:
int a, b, k;
k = (int)(log(n)/log(2)); //不大于n的最大的2的阶数。即 2^k <= n < 2^(k+1)
for(i=k; i>=0; i--)
if(a - (1<<i) >= b)
a = a - (1<<i);
这样,a就变到了b……
a的x(这里x不一定为整数,可能为小书)次方等于b,即 a^x = b,两边取log,即log(a^x) = log(b) ---> x*log(a) = log(b) ---> x = log(b)/log(a),再取个整的话,就表示不大于b的最大的a的阶数
#include "stdio.h"
#include "string.h"
#include "math.h"
#include "stdlib.h"
#define M 100
/* 0 1 2 3 4 5 6 7 8 9*/
int num[M] = {3, 9, 1, 33, 20, 18, 52, 0, 10, 7};
int n = 10;
typedef struct _Node{
int pos;
struct _Node* lf;
struct _Node* rt;
}Node, *pNode;
pNode st[M]; //stack
int top;
int pare[M]; //parent
int lev[M]; //level
pNode RMC_2_LCA(){
int i, k;
pNode w;
top = -1;
for(i=0; i<n; i++){
w = (pNode)malloc(sizeof(Node));
w->pos = i;
w->lf = w->rt = 0;
k = top;
while(k>=0 && num[i]<num[st[k]->pos])
k--;
if(k>=0)
st[k]->rt = w;
if(k<top)
w->lf = st[k+1];
st[++k] = w;
top = k;
}
return st[0];
}
void DFS(pNode r, int l){
lev[r->pos] = l;
if(r->lf){
pare[r->lf->pos] = r->pos;
DFS(r->lf, l+1);
}
if(r->rt){
pare[r->rt->pos] = r->pos;
DFS(r->rt, l+1);
}
}
/*前面是RMC转LCA,后面就是LCA的操作……*/
int p[M][M/2]; //p[i][j]表示下标为i的节点的第2^j个祖先,例如,p[i][0]表示第2^0=1,即,i的爹
/*DP预处理,求出所有节点的第2^j个祖先*/
void process(){
int i, j, k;
memset(p, -1, sizeof(p));
for(i=0; i<n; i++)
p[i][0] = pare[i];
k = (int)(log(n)/log(2)); //不大于n的最大的2的阶数。即 2^k <= n < 2^(k+1)
for(j=1; j<=k; j++){
for(i=0; i<n; i++){
if(p[i][j-1]!=-1)
p[i][j] = p[p[i][j-1]][j-1];
}
}
}
/*很漂亮的查询*/
int query(int a, int b){
int i, k;
if(lev[a] < lev[b])
a ^= b ^= a ^= b;
k = (int)(log(n)/log(2));
for(i=k; i>=0; i--)
if(lev[a] - (1<<i) >= lev[b])
a = p[a][i];
if(a==b) return a; //a==b,实际上就是b原本是a的祖先
for(i=k; i>=0; i--)
if(p[a][i]!=-1 && p[a][i]!=p[b][i]){
a = p[a][i];
b = p[b][i];
}
return pare[a]; //注意,最后p[a][i]==p[b][i]的时候,a,b肯定是都变成了最近共同祖先的直接孩子。这里比较难理解,好好想一下。
}
void main(){
int i, j;
pNode root;
root = RMC_2_LCA();
pare[root->pos] = -1;
DFS(root, 1);
process();
for(i=0; i<n; i++){
for(j=i+1; j<n; j++)
printf("%d-%d:%d ", i, j, query(i, j));
printf("\n");
}
}