【数据结构机试】树
存储 & 访问
一般的树
vector<int> v[N];
void dfs(int u) {
for(auto x : v[u]) {
...
dfs(x);
}
}
二叉树
int L[N], R[N]; // 表示左右儿子的值分别是多少
至于编号,结点 \(i\) 的左儿子 \(2i\),右儿子 \(2i+1\)
树的遍历
一般的数
分为先根(先访问根,后访问儿子)、后根(先访问儿子,后访问根)。
二叉树
先序遍历(根左右)、中序遍历(左根右)、后序遍历(左右根)。
题意
给你先序和后序遍历序列,判断二叉树形态是否唯一。
解答
首先知道先序的构成:序列第一个数是根,然后依次是根的左子树和右子树,根在后序序列中位于最后一个;
先序第二个元素,是根的左儿子(除非根没有左儿子,那么是根的右儿子);
我们可以找到在后序序列中 先序第二个元素的位置 \(p\),在先序序列中,从 \(p-1\) 到正数第二个,这些都是根的左子树, \(p + 1\) 到先序最末尾,这些都是根的右子树,我们由此递归建树。
形态不唯一的情况:对某棵子树的根来说,它只有左儿子无右儿子 或者 只有右儿子无左儿子,那么无论它到底有的是哪个儿子,你会发现,这两种情况对应的先序和后序序列一样,即无法判断,此时就是不唯一的
code
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3000 + 10;
int n, pre[N], post[N], tem[N];
int rt, L[N], R[N];
map<int, int> mp, Map;
int dfn, ans[N];
bool f = 1;
int solve(int l1, int r1, int l2, int r2) {
if(l1 > r1 || l2 > r2) return 0;
if(l1 == r1) return pre[l1];
int now_rt = pre[l1];
int now_l = pre[l1 + 1], pos = mp[now_l];
int cnt_l = pos - l2 + 1, cnt_r = r2 - pos - 1;
L[now_rt] = solve(l1 + 1, l1 + cnt_l, l2, l2 + cnt_l - 1);
R[now_rt] = solve(l1 + cnt_l + 1, r1, l2 + cnt_l, r2 - 1);
if(f && (!L[now_rt] || !R[now_rt]) && (L[now_rt] || R[now_rt])) f = 0;
return pre[l1];
}
void dfs(int rt) {
if(L[rt]) dfs(L[rt]);
ans[++dfn] = rt;
if(R[rt]) dfs(R[rt]);
}
signed main(){
ios::sync_with_stdio(false);
cin >> n;
for(int i = 1; i <= n; i++) cin >> pre[i], tem[i] = pre[i];
for(int i = 1; i <= n; i++) cin >> post[i];
sort(tem + 1, tem + n + 1);
for(int i = 1; i <= n; i++) {
int x = pre[i];
pre[i] = lower_bound(tem + 1, tem + n + 1, pre[i]) - tem;
Map[pre[i]] = x;
post[i] = lower_bound(tem + 1, tem + n + 1, post[i]) - tem;
mp[post[i]] = i;
}
if(n == 1) {
cout << "Yes" << '\n';
cout << Map[pre[1]] << endl;
return 0;
}
rt = solve(1, n, 1, n);
dfs(rt);
cout << (f ? "Yes" : "No") << endl;
for(int i = 1; i <= dfn; i++) {
ans[i] = Map[ans[i]];
if(i == 1) cout << ans[i];
else cout << ' ' << ans[i];
}cout << '\n';
return 0;
}
LCA
单次询问 \(O(n)\) 复杂度的做法不再赘述。
并查集
当我们只关心结点之间有没有关系时使用
分享一个板子:
点击查看代码
//并查集模板 亲戚
#include<cstdio>
using namespace std;
int father[5005];
//路径压缩
int f(int x){
return x == father[x] ? x : (father[x] = f(father[x]));
}
int add(int x,int y){
int fx=f(x);
int fy=f(y);
return father[fx]=fy; //x.y无序,因为不是按树的结构存储,而是按集合
}
int main(){
int n,m,p;
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=n;i++) father[i]=i;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
if(f(x)!=f(y)){
add(x,y);
}
}
for(int i=1;i<=p;i++){
int x,y;
scanf("%d%d",&x,&y);
if(f(x)==f(y)){
puts("Yes");
}
else puts("No");
}
return 0;
}
题意
\(m\) 通电话,给出通话双方和时长(注意,两人之间可能有多次通话),通话过的人属于同一帮人。找到 人数>=3 且 通话总时间(帮派中每两人之间)>=K 的帮派的人数和首领(和其他人通话最长的)
解答
并查集维护,注意要字符串转数字编号,累加两人通话总时间
code
点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2000 + 10;
int m, k;
map<string, int> mp;
string a, b;
int tem[N], dfn, ori[N];
struct node{
int u, v, t;
}peo[N];
int g[N][N];
bool vis[N][N];
int fa[N], sz[N], cnt[N];
vector<int> v[N];
int ans[N], id[N];
vector<int> prin;
inline int f(int x){
return x == fa[x] ? x : (fa[x] = f(fa[x]));
}
void add(int u, int v) {
int w = g[u][v];
u = f(u); v = f(v);
if(u != v) {
fa[u] = v;
sz[v] += sz[u] + w;
cnt[v] += cnt[u];
}
else {
sz[u] += w;
}
return;
}
int main(){
// ios::sync_with_stdio(false);
cin >> m >> k;
for(int i = 1; i <= m; i++) {
cin >> a >> b;
if(!mp[a]) mp[a] = (a[0] - 'A') * 26 * 26 + (a[1] - 'A') * 26 + (a[2] - 'A') + 1, tem[++dfn] = mp[a];
if(!mp[b]) mp[b] = (b[0] - 'A') * 26 * 26 + (b[1] - 'A') * 26 + (b[2] - 'A') + 1, tem[++dfn] = mp[b];
peo[i].u = mp[a]; peo[i].v = mp[b]; cin >> peo[i].t;
}
// cout << "dfn: " << dfn << endl;
sort(tem + 1, tem + dfn + 1);
for(int i = 1; i <= m; i++) {
int x = peo[i].u;
peo[i].u = lower_bound(tem + 1, tem + dfn + 1, peo[i].u) - tem;
ori[peo[i].u] = x;
x = peo[i].v;
peo[i].v = lower_bound(tem + 1, tem + dfn + 1, peo[i].v) - tem;
ori[peo[i].v] = x;
// cout << peo[i].u << ' ' << peo[i].v << endl; ////
g[peo[i].u][peo[i].v] += peo[i].t; g[peo[i].v][peo[i].u] += peo[i].t;
}
for(int i = 1; i <= dfn; i++) fa[i] = i, cnt[i] = 1, sz[i] = 0;
for(int i = 1; i <= m; i++) {
if(vis[peo[i].u][peo[i].v]) continue;
vis[peo[i].u][peo[i].v] = vis[peo[i].v][peo[i].u] = 1;
add(peo[i].u, peo[i].v);
// cout << peo[i].u << ' ' << peo[i].v << ' ' << g[peo[i].u][peo[i].v] << endl; ////
}
set<int> st;
for(int i = 1; i <= dfn; i++) {
int p = f(i);
// cout << p << ' ' << cnt[p] << ' ' << sz[p] << endl;
if(cnt[p] > 2 && sz[p] > k) st.insert(p), v[p].push_back(i);
}
cout << st.size() << endl;
if(st.empty()) return 0;
memset(ans, -1, sizeof(ans));
for(auto i : st) { // i: predecessor
for(auto x : v[i]) { // x: son
int res = 0;
for(int j = 1; j <= dfn; j++) {
res += g[x][j];
}
if(res > ans[i]) ans[i] = res, id[i] = x;
}
prin.push_back(id[i]);
}
sort(prin.begin(), prin.end());
for(auto i : prin) {
int tmp = ori[i] - 1;
string s;
s += (char)(tmp / (26 * 26) + 'A'); tmp %= (26 * 26);
s += (char)(tmp / (26) + 'A'); tmp %= (26);
s += (char)(tmp + 'A');
cout << s << ' ' << cnt[f(i)] << endl;
}
return 0;
}