涉及题目描述:(BST—binary search tree,BT—binary tree)
1、BST构建
2、BT的层次遍历
3、BST的LCA(最近公共祖先)求解
4、BST 转化为有序的double link list
5、BT的LCA求解(将LCA问题转化为RMQ(上一篇blog涉及)问题求解)
相关分析:
1、2问题简单,不予分析
3、
对于BST,它有一个重要的性质:节点的左子树包含且只包含比它小的节点,
右子树包含且只包含比它大的节点。因此LCA的值一定在这两个节点之间。
1. 如果u<root && v<root,则去左子树搜索;
2. 如果u>root && v>root,则去右子树搜索;
3. 否则,root就是LCA
4、
左、右子树分别转化为double link list,然后将左子树list、root、右子树list连接成一个list,递归实现
5、
一、最近公共祖先(Least Common Ancestors)
对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。另一种理解方式是把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上深度最小的点。
这里给出一个LCA的例子:
例
对于T=<V,E>
V={1,2,3,4,5}
E={(1,2),(1,3),(3,4),(3,5)}
则有:
LCA(T,5,2)=1
LCA(T,3,4)=3
LCA(T,4,5)=3
二、RMQ问题(Range Minimum Query)
RMQ问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在[i,j]里的最小值下标。这时一个RMQ问题的例子:
例
对数列:5,8,1,3,6,4,9,5,7 有:
RMQ(2,4)=3
RMQ(6,9)=6
RMQ问题与LCA问题的关系紧密,可以相互转换,相应的求解算法也有异曲同工之妙。
下面给出LCA问题向RMQ问题的转化方法。
对树进行深度优先遍历,每当“进入”或回溯到某个结点时,将这个结点的值存入数组E最后一位。同时记录结点i在数组中第一次出现的位置(事实上就是进入结点i时记录的位置),记做R[i]。如果结点E[i]的深度记做D[i],易见,这时求LCA(T,u,v),就等价于求 E[RMQ(D,R[u],R[v])],(R[u]<R[v])。
例如,对于第一节的例,求解步骤如下:
数列E[i]为:1,2,1,3,4,3,5,3,1
R[i]为:1,2,4,5,7
D[i]为:0,1,0,1,2,1,2,1,0
于是有:
LCA(T,5,2) = E[RMQ(D,R[2],R[5])] = E[RMQ(D,2,7)] = E[3] = 1
LCA(T,3,4) = E[RMQ(D,R[3],R[4])] = E[RMQ(D,4,5)] = E[4] = 3
LCA(T,4,5) = E[RMQ(D,R[4],R[5])] = E[RMQ(D,5,7)] = E[6] = 3
易知,转化后得到的数列长度为树的结点数的两倍加一,所以转化后的RMQ问题与LCA问题的规模同次。
代码:
#include <functional>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <queue>
#include <fstream>
#include <vector>
using namespace std;
#define pow2(n) (1 << (n))
#define MAXN 10000
#define mmin(seq, a, b) ((seq[a] < seq[b]) ? (a) : (b))
//// DP status
int fij[MAXN][100];
//// for output fij
template <typename T>
void Dp_st_print(T seq[], int n)
{
//// 输出待RMQ的序列
for(int i = 0; i < n; i++)
cout<<seq[i]<<"/t";
cout<<endl;
//// 输出状态f[][]
for(int i = 0; i < n; i++)
{
for(int j = 0; i + (1<<j) - 1 < n; j++)
{
cout<<"fij["<<i<<", "<<j<<"] = "<<fij[i][j]<<"/t";
}
cout<<endl;
}
cout<<endl;
}
// RMQ(Range Minimum/Maximum Query)问题:
// 预处理:
// sparse table算法(st)
template <typename T>
void st(T seq[], int n)
{
memset(fij, 0, 100 * MAXN * sizeof(int));
int k = (int)(log((double)n) / log(2.0));
////初始状态
for(int i = 0; i < n; i++)
fij[i][0] = i;
////递推计算状态
for(int j = 1; j <= k; j++)
{
for(int i = 0; i + (1 << j) - 1 < n; i++)
{
//
int m = i + (1 << (j - 1));
//fij[i][j] = seq[fij[i][j - 1]] < seq[fij[m][j - 1]] ? fij[i][j - 1] : fij[m][j - 1];
fij[i][j] = mmin(seq, fij[i][j - 1], fij[m][j - 1]);
}
}
}
// 查询:
template <typename T>
int RMQ(T seq[], int i, int j)
{
//// 用对2去对数的方法求出k
int k = (int)(log(double(j - i + 1)) / log(2.0));
////
//int t = seq[fij[i][k]] < seq[fij[j - (1 << k) + 1][k]] ? fij[i][k] : fij[j - (1 << k) + 1][k];
int t = mmin(seq, fij[i][k], fij[j - (1 << k) + 1][k]);
return t;
//cout<<"RMQ("<<i<<", "<<j<<") = "<<seq[t]<<" @ pos("<<t<<")/n";
}
////////---------------------------------------------------
typedef struct node
{
int key;
node* left;
node* right;
}Node, *pNode;
// normal output
void inorder_output(pNode root)
{
if(root != NULL)
{
inorder_output(root->left);
cout<<root->key<<" ";
inorder_output(root->right);
}
}
// layer output
void layer_output(pNode root)
{
queue<pNode> tq;
pNode p;
tq.push(root);
tq.push(NULL);
while(!tq.empty())
{
p = tq.front();
tq.pop();
if(NULL == p)
{
cout<<endl;
if(!tq.empty())
tq.push(NULL);
}
else
{
cout<<p->key<<" ";
if(p->left != NULL)
tq.push(p->left);
if(p->right != NULL)
tq.push(p->right);
}
}
}
//
int getheight_tree(pNode root)
{
if(NULL == root)
return 0;
int lh = getheight_tree(root->left);
int rh = getheight_tree(root->right);
return lh > rh ? lh + 1 : rh + 1;
}
//
//
void insert_BST(pNode *root, int value)
{
pNode tmp;
if(NULL == *root)
{
tmp = (pNode)malloc(sizeof(Node));
tmp->key = value;
tmp->left = NULL;
tmp->right = NULL;
*root = tmp;
}
else
{
if(value <= (*root)->key)
insert_BST(&(*root)->left, value);
else
insert_BST(&(*root)->right, value);
}
}
// LCA of BST
// 对于BST,它有一个重要的性质:节点的左子树包含且只包含比它小的节点,
// 右子树包含且只包含比它大的节点。因此LCA的值一定在这两个节点之间。
//
// 1. 如果u<root && v<root,则去左子树搜索;
//
// 2. 如果u>root && v>root,则去右子树搜索;
//
// 3. 否则,root就是LCA
pNode search_LCA_BST(pNode root, pNode u, pNode v)
{
if(NULL == root)
return NULL;
if(root->key >= u->key && root->key >= v->key)
search_LCA_BST(root->left, u, v);
else if(root->key < u->key && root->key < v->key)
search_LCA_BST(root->right, u, v);
else
return root;
}
// append linklist2 to linklist1
pNode appendLL(pNode LL1, pNode LL2)
{
if(NULL == LL1)
return LL2;
if(NULL == LL2)
return LL1;
////tail
pNode tail1 = LL1->left;
pNode tail2 = LL2->left;
////join head and tail
tail1->right = LL2;
LL2->left = tail1;
tail2->right = LL1;
LL1->left = tail2;
return LL1;
}
// convert BST to sorted link list
pNode BST_to_sortedlinklist(pNode root)
{
if(NULL == root)
return NULL;
pNode leftLL = BST_to_sortedlinklist(root->left);
pNode rightLL = BST_to_sortedlinklist(root->right);
//
root->left = root;
root->right = root;
//append leftLL ,root ,rightLL
leftLL = appendLL(leftLL, root);
leftLL = appendLL(leftLL, rightLL);
return leftLL;
}
// travel link list
void list_output(pNode root)
{
if(NULL == root)
return;
pNode p = root;
cout<<p->key<<" ";
p = p->right;
while(p != root)
{
cout<<p->key<<" ";
p = p->right;
}
}
//// LCA --> RMQ
// 对树进行深度优先遍历,每当“进入”或回溯到某个结点时,
// 将这个结点的值存入数组E最后一位。同时记录结点i在数组中
// 第一次出现的位置(事实上就是进入结点i时记录的位置),记做R[i]。
// 如果结点E[i]的深度记做D[i],易见,这时求LCA(T,u,v),
// 就等价于求 E[RMQ(D,R[u],R[v])],(R[u]<R[v])。
// 例如,对于第一节的例一,求解步骤如下:
// 数列
// E[i]为:1,2,1,3,4,3,5,3,1
// R[i]为:1,2,4,5,7
// D[i]为:0,1,0,1,2,1,2,1,0
//
// 于是有:
// LCA(T,5,2) = E[RMQ(D,R[2],R[5])] = E[RMQ(D,2,7)] = E[3] = 1
// LCA(T,3,4) = E[RMQ(D,R[3],R[4])] = E[RMQ(D,4,5)] = E[4] = 3
// LCA(T,4,5) = E[RMQ(D,R[4],R[5])] = E[RMQ(D,5,7)] = E[6] = 3
void prepare_for_LCA(pNode root, int h, vector<int>& E, vector<int>& R, vector<int>& D)
{
if(NULL == root)
return;
E.push_back(root->key);
D.push_back(h);
R.push_back((int)E.size());
if(root->left != NULL)
{
prepare_for_LCA(root->left, h + 1, E, R, D);
E.push_back(root->key);
D.push_back(h);
}
if(root->right != NULL)
{
prepare_for_LCA(root->right, h + 1, E, R, D);
E.push_back(root->key);
D.push_back(h);
}
}
//// get LCA by RMQ
int get_LCA_by_RMQ(int i, int j, int* E, int* R, int* D, int n)
{
if(i > j)
swap(i, j);
////fij
st(D, n);
////
return(E[RMQ(D, R[i - 1] - 1, R[j - 1] - 1)]);
}
int _tmain(int argc, _TCHAR* argv[])
{
////BST root
pNode root_BST = NULL;
pNode p;
int A[] = {1,2,2,3,4,5,6,7,8,9};
int N = sizeof(A) / sizeof(int);
random_shuffle(A, A + N);
////construct BST tree
for(int i = 0; i < N; i++)
insert_BST(&root_BST, A[i]);
cout<<endl<<"inorder travel:"<<endl;
inorder_output(root_BST);
cout<<endl<<"layer output:"<<endl;
layer_output(root_BST);
////
pNode u = (pNode)malloc(sizeof(Node));
u->key = 9;
pNode v = (pNode)malloc(sizeof(Node));
v->key = 4;
p = search_LCA_BST(root_BST, u, v);
if(NULL == p)
cout<<cout<<endl<<"节点 "<<u->key<<" 与节点 "<<v->key<<" 无LCA节点"<<endl;
else
cout<<endl<<"节点 "<<u->key<<" 与节点 "<<v->key<<" 的LCA是节点 "<<p->key<<endl;
////
p = BST_to_sortedlinklist(root_BST);
cout<<endl<<"convert to sorted link list :/n";
list_output(p);
cout<<endl;
//// LCS --> RMQ
pNode testT = (pNode)malloc(sizeof(Node));
pNode node2 = (pNode)malloc(sizeof(Node));
pNode node3 = (pNode)malloc(sizeof(Node));
pNode node4 = (pNode)malloc(sizeof(Node));
pNode node5 = (pNode)malloc(sizeof(Node));
testT->key = 1;
node2->key = 2;
node3->key = 3;
node4->key = 4;
node5->key = 5;
testT->left = node2;
testT->right = node3;
node2->left = node2->right = NULL;
node3->left = node4;
node3->right = node5;
node4->left = node4->right = NULL;
node5->left = node5->right = NULL;
layer_output(testT);
vector<int> E, R, D;
prepare_for_LCA(testT, 0, E, R, D);
int *arrE = new int[(int)E.size()];
int *arrR = new int[(int)R.size()];
int *arrD = new int[(int)D.size()];
copy(E.begin(), E.end(), arrE);
copy(R.begin(), R.end(), arrR);
copy(D.begin(), D.end(), arrD);
cout<<endl<<get_LCA_by_RMQ(4, 5, arrE, arrR, arrD, (int)D.size())<<endl;
system("pause");
return 0;
}