A binary tree is a tree data structure where each node has at most two children which are referred to as left child and right child. A binary search tree is a binary tree where each node has a comparable key and must satisfy the following conditions:
- The left sub-tree of a node contains only nodes with keys less than the node’s key.
- The right sub-tree of a node contains only nodes with keys more than the node’s key.
- The left and the right sub-tree are also binary search tree.
Given a binary tree, your task is to choose a maximal set S of connected nodes in the given tree so that these nodes and the relations (left child, right child, or ancestor) among them form a binary search tree.
The first line of input is the number T (T ≤ 20).
Then T tests follow:
- The first line of each test is the number N (1 ≤ N ≤ 105) - the number of node of the given binary tree
- Then N lines follow. The ith line describes node numbered i (nodes are numbered 1 to N). Each node is described by 3 integers: the index of its left child, the index of its right child, and the comparable key in that node. The value of a key will not exceed 106. If a node doesn’t have a left child and/or a right child, they will be represented as 0. The root node is numbered 1.
For each test, print the maximal size of S.
2 8 2 3 10 4 5 5 6 7 15 0 0 3 0 0 7 0 0 17 0 8 13 0 0 14 3 0 2 5 0 3 6 0 0 7
5 3
The example test case correspond to the following figure:
题意是给定一棵二叉树,问这个二叉树最大能构成几个节点的二叉索引树。
一开始想简单了,以为只要左儿子的权值小于父节点就能添加整棵左子树。其实左儿子的右
子树中有可能存在比父节点权值大的节点,我们需要把以这个节点为根的子树删除。查找这
样的矛盾节点可以通过暴力查找。
对于一个父亲节点,以他为根的子树,如果他的左子树中存在一个比他权值大的,只有可能
出现在他左子树的右路径上(比如样例1中对于10这个父亲节点他的左儿子的右路径就是
5->7),现在正确性有了保证我们需要证明复杂度。
这个方法的复杂度其实是线性的。对于某一个节点,除了第一次递归经过他以外,在使用上
述方法时最多经过2次,也就是这个点最多处一次左儿子的右路径或者右儿子的左路径,所以
这个算法的复杂度是O(n),优于题解的O(nlgn)。
<span style="font-size:12px;">#include <bits/stdc++.h>
using namespace std;
#define maxn 111111
#define maxm 211111
#define find Find
struct node {
int from, to, next;
}edge1[maxm], edge2[maxn];
int head1[maxn], head2[maxn], a[maxn];
int cnt1, cnt2;
int n;
int b[maxn][3];
int degree[maxn]; //入度
int ans, dp[maxn];
bool vis[maxn];
void add_edge (int from, int to, int id) { //id=0表示左儿子 1表示右儿子
if (id == 0) {
edge1[cnt1].from = from, edge1[cnt1].to = to, edge1[cnt1].next = head1[from], head1[from] = cnt1++;
}
else if (id == 1) {
edge2[cnt2].from = from, edge2[cnt2].to = to, edge2[cnt2].next = head2[from], head2[from] = cnt2++;
}
}
int find (int u, int id, int val) { //id = 0 表示左路 1表示右路
if (vis[u])
return -1;
if (id == 0) {
int i = head1[u];
if (i == -1)
return -1;
int lson = edge1[i].to;
if (a[lson] < val) {
vis[lson] = 1;
return lson;
}
else
return find (lson, id, val);
}
else if (id == 1) {
int i = head2[u];
if (i == -1)
return -1;
int rson = edge2[i].to;
if (a[rson] > val) {
vis[rson] = 1;
return rson;
}
else
return find (rson, id, val);
}
}
void dfs (int u) { //cout << "u:" << u << endl;
dp[u] = 1;
int i = head1[u], j = head2[u];
if (i != -1) {
int lson = edge1[i].to;
dfs (lson);
if (a[u] > a[lson])
dp[u] += dp[lson];
else vis[lson] = 1;
int bug = find (lson, 1, a[u]); //从左儿子的右路找到第一个比a[u]大的节点编号
if (bug != -1) {
dp[u] -= dp[bug];
}
}
if (j != -1) {
int rson = edge2[j].to;
dfs (rson);
if (a[u] < a[rson])
dp[u] += dp[rson];
else vis[rson] = 1;
int bug = find (rson, 0, a[u]);
if (bug != -1) {
dp[u] -= dp[bug];
}
}
ans = max (ans, dp[u]);
}
int main () {
//freopen ("in", "r", stdin);
int t;
scanf ("%d", &t);
while (t--) {
scanf ("%d", &n);
for (int i = 1; i <= n; i++) {
scanf ("%d%d%d", &b[i][0], &b[i][1], &b[i][2]);
}
memset (degree, 0, sizeof degree);
memset (head1, -1, sizeof head1);
memset (head2, -1, sizeof head2);
cnt1 = cnt2 = 0;
for (int i = 1; i <= n; i++) {
a[i] = b[i][2];
if (b[i][0] != 0) {
add_edge (i, b[i][0], 0);
degree[b[i][0]]++;
}
if (b[i][1] != 0) {
add_edge (i, b[i][1], 1);
degree[b[i][1]]++;
}
}
int root;
for (int i = 1; i <= n; i++) {
if (!degree[i]) {
root = i;
break;
}
}
//cout << root << endl;
ans = 0;
memset (dp, 0, sizeof dp);
memset (vis, 0, sizeof vis);
dfs (root);
cout << ans << endl;
}
return 0;
}</span>