【数据结构机试】树

存储 & 访问

一般的树

vector<int> v[N];

void dfs(int u) {
  for(auto x : v[u]) {
    ...
    dfs(x);
  }
}

二叉树

int L[N], R[N];  // 表示左右儿子的值分别是多少

至于编号,结点 \(i\) 的左儿子 \(2i\),右儿子 \(2i+1\)

树的遍历

一般的数

分为先根(先访问根,后访问儿子)、后根(先访问儿子,后访问根)。

二叉树

先序遍历(根左右)、中序遍历(左根右)、后序遍历(左右根)。

例1
PTA 1119 Pre- and Post-order Traversals

题意

给你先序和后序遍历序列,判断二叉树形态是否唯一。

解答

首先知道先序的构成:序列第一个数是根,然后依次是根的左子树和右子树,根在后序序列中位于最后一个;
先序第二个元素,是根的左儿子(除非根没有左儿子,那么是根的右儿子);
我们可以找到在后序序列中 先序第二个元素的位置 \(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;
}

例1
PTA 1034 Head of a Gang

题意

\(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;
} 

AVL树(平衡二叉树)

posted @ 2023-08-27 13:53  starlightlmy  阅读(8)  评论(0编辑  收藏  举报