1. LCA (lowest common ancestor)
1) Tarjan算法,参考http://hi.baidu.com/luyade1987/blog/item/5b609b016fcecbd3277fb5ac.html
#include <cstdio>
using namespace std;
const int N = 10001;
int id[N], lcs[N][N], g[N][N];
int n, e;
int get(int i)
{
if(id[i] == i)
{
return i;
}
return id[i] = get(id[i]);
}
void unin(int i, int j)
{
id[get(i)] = get(j);
}
void dfs(int rt, int n)
{
int i;
id[rt] = rt;
for(i = 0; i < n; i++)
{
if(g[rt][i] && -1 == id[i])
{
dfs(i, n);
unin(i, rt);
}
}
// after visiting rt
//printf("after %d\n", rt);
for(i = 0; i < n; i++)
{
//printf("%d ", id[i]);
if(-1 != id[i])
{
lcs[rt][i] = lcs[i][rt] = get(i);
}
}
//printf("\n");
}
void init()
{
for(int i = 0; i < n; i++)
{
id[i] = -1;
}
}
void print()
{
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
printf("%d ", lcs[i][j]);
}
printf("\n");
}
// for(int i = 0; i < n; i++)
// {
// printf("%d ", id[i]);
// }
printf("\n");
}
int main()
{
scanf("%d%d", &n, &e);
for(int i = 0; i < e; i++)
{
int s, t;
scanf("%d%d", &s, &t);
g[s][t] = g[t][s] = 1;
}
init();
dfs(0, n);
print();
return 0;
}
运行结果:
/home/a/j/nomad2:cat input_lca
8 7
0 1
1 2
1 3
3 4
3 5
0 6
6 7
0 0 0 0 0 0 0 0
0 1 1 1 1 1 0 0
0 1 2 1 1 1 0 0
0 1 1 3 3 3 0 0
0 1 1 3 4 3 0 0
0 1 1 3 3 5 0 0
0 0 0 0 0 0 6 6
0 0 0 0 0 0 6 7
2) 转化为RMQ问题,
这篇文章解释的很好,http://hi.baidu.com/fandywang_jlu/blog/item/774caecae6165883c8176853.html
(1)
/ \
(2) (7)
/ \ \
(3) (4) (8)
/ \
(5) (6)
一个nlogn 预处理,O(1)查询的算法.
Step 1:
按先序遍历整棵树,记下两个信息:结点访问顺序和结点深度.
如上图:
结点访问顺序是: 1 2 3 2 4 5 4 6 4 2 1 7 8 7 1 //共2n-1个值
结点对应深度是: 0 1 2 1 2 3 2 3 2 1 0 1 2 1 0
Step 2:
如果查询结点3与结点6的公共祖先,则考虑在访问顺序中
3第一次出现,到6第一次出现的子序列: 3 2 4 5 4 6.
这显然是由结点3到结点6的一条路径.
在这条路径中,深度最小的就是最近公共祖先(LCA). 即
结点2是3和6的LCA.
Step 3:
于是问题转化为, 给定一个数组R,及两个数字i,j,如何找出
数组R中从i位置到j位置的最小值..
如上例,就是R[]={0,1,2,1,2,3,2,3,2,1,0,1,2,1,0}.
i=2;j=7;
这个问题就是经典的RMQ问题.
这里介绍一个比较简单的方法O(nlogn)预处理,O(1)回答每个询问.
RMQ问题的预处理:
用一个数组d[i][j]表示数组R中,从i位置到i+2^j-1位置的最小值.
这个d[i][j]很容易dp得到.
d[i][j+1]=min{d[i][j],d[i+2^j][j]};
空间: O(nlogn) 时间: O(nlogn).
Step 4:
询问:
如果询问:从a到b里面最小的值..主要思路找两个长度是2^k的区间:
[a,a+2^k-1]及[b-1-2^k,b]把区间[a,b]覆盖掉. 这很容易做到的,
只要: 2^k*2>= |b-a|.
于是a到b里面的最小值 = min {d[a][k],d[b-2^k-1][k]}
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 10001;
int g[N][N];
// number of edge
int e;
// number of vertex
int n;
// vis from root
bool visited[N];
int id;
// dep: dfs' depth, E: dfs sequence
int dep[2 * N + 1], E[2 * N + 1], R[N];
void DFS(int u, int d);
int d[20], st[2 * N + 1][20];
int query(int x, int y) {
int k;
k = int(log(double(y - x + 1)) / log(2));
return dep[st[x][k]] > dep[st[y - d[k] + 1][k]] ? st[y - d[k] + 1][k] : st[x][k];
}
void answer() {
int i, Q;
scanf("%d", &Q);
for (i = 0; i < Q; i++) {
int x, y;
scanf("%d%d", &x, &y);
x = R[x];
y = R[y];
if (x > y) {
swap(x, y);
}
printf("%d\n", E[query(x, y)]);
}
}
void DFS(int u, int d) {
visited[u] = true;
R[u] = id;
E[id] = u;
dep[id++] = d;
for (int i = 0; i < n; i++) {
if (!visited[i] && g[u][i]) {
DFS(i, d + 1);
E[id] = u;
dep[id++] = d;
}
}
}
void print()
{
cout << endl;
cout << "R: ";
for(int i = 0; i < n; i++)
{
cout << R[i] << " ";
}
cout << endl;
cout << "E: ";
for(int i = 0; i < 2 * e + 1; i++)
{
cout << E[i] << " ";
}
cout << endl;
cout << "D: ";
for(int i = 0; i < 2 * e + 1; i++)
{
cout << dep[i] << " ";
}
cout << endl;
}
void initRMQ(int id) {
int i, j;
for (d[0] = 1, i = 1; i < 20; i++)
d[i] = d[i - 1] * 2;
for (i = 0; i < id; i++)
st[i][0] = i;
int k = int(log(double(id)) / log(2)) + 1;
for (j = 1; j < k; j++) {
for (int i = 0; i < id; i++) {
if (i + d[j - 1] - 1 < id) {
st[i][j]
= dep[st[i][j - 1]] > dep[st[i + d[j - 1]][j - 1]] ? st[i + d[j - 1]][j - 1]
: st[i][j - 1];
} else {
break;
}
}
}
}
int main() {
scanf("%d%d", &n, &e);
for (int i = 0; i < e; i++) {
int s, t;
scanf("%d%d", &s, &t);
g[s][t] = g[t][s] = 1;
}
DFS(0, 0);
print();
initRMQ(id);
answer();
return 0;
}
运行结果:
8 7
0 1
1 2
1 3
3 4
3 5
0 6
6 7
1
2 5
R: 0 1 2 4 5 7 11 12
E: 0 1 2 1 3 4 3 5 3 1 0 6 7 6 0
D: 0 1 2 1 2 3 2 3 2 1 0 1 2 1 0
1
2. If you have a binary tree and want to send it to a mobile device, how would you do it? Write the code to create the flattened tree and to recreate the tree once flattened.
3. 给定一个中序遍历序列,可以构造多少个二叉树?
含有n个节点的不相似的二叉树有1/(n+1) * C(2n, n)
4. 二叉搜索树变成双向链表,链表应排序
5. 二叉树遍历(前序、中序、后序、BFS/DFS)
6. 判断一个二叉树是不是二叉搜索树
7. 判断一个二叉树是否对称/互为镜像?(中序遍历为回文串)
bool IsMirror(Node * rootA, Node * rootB)
{
if ( rootA == 0 && rootB == 0 )
return true;
if ( rootA == 0 || rootB == 0)
return false;
return rootA->Value == rootB->Value
&& IsMirror(rootA->Left, rootB->Right)
&& IsMirror(rootA->Right, rootB->Left);
}
bool IsMirror(Node * root)
{
return IsMirror(root->Left, root->Right);
}
8. 在二叉树中找出和为某一值的所有路径
refer to: http://www.coder4.com/archives/3245
#include <iostream>
#include <vector>
using namespace std;
class Node
{
public:
Node *left;
Node *right;
int data;
Node(int d = 0):data(d),left(NULL),right(NULL){}
};
void print(vector<int> v, int level, int i2);
void sum(Node *head, int s, vector<int> v, int level)
{
if(head == NULL)
return;
int tmp = s;
cout << "adding " << head->data << endl;
v.push_back(head->data);
for(int i = level; i >= 0; i--)
{
tmp -= v[i];
if(tmp == 0)
{
print(v, i, level);
}
}
vector<int> v1(v);
vector<int> v2(v);
sum(head->left, s, v1, level+1);
sum(head->right, s, v2, level+1);
}
void print(vector<int> v, int level, int i2)
{
for(int i = level; i<= i2; i++)
{
cout << v[i] << " ";
}
cout << endl;
}
Node* buildTree()
{
Node* root = new Node(3);
Node* n1 = new Node(1);
Node* n2 = new Node(0);
Node* n3 = new Node(1);
Node* n4 = new Node(-1);
root->left = n2;
root->right = n1;
n2->left = n3;
n2->right = n4;
return root;
}
int main()
{
Node* r = buildTree();
vector<int> v;
sum(r, 3, v, 0);
}
9. 二叉搜索树,求第k个元素,求第二大元素
本文介绍了二叉树的基本概念及其多种应用,包括LCA算法的两种实现方式:Tarjan算法和转化成RMQ问题的方法。此外还探讨了二叉搜索树、二叉树遍历、对称性判断等主题。

1165

被折叠的 条评论
为什么被折叠?



