DFS序与欧拉序的区别

DFS序与欧拉序的区别

dfs序:是指将一棵树被dfs时所经过的节点顺序(不绕回原点)。
欧拉序:就是从根结点出发,按dfs的顺序在绕回原点所经过所有点的顺序。

作用

通过dfs序判断v节点的时间区间是否在u节点的时间区间内。
通过欧拉序求u和v的最近公共祖先。

用图说话

dfs序:A-B-D-E-G-C-F-H

欧拉序:A-B-D-D-E-G-G-E-B-C-F-H-H-F-C-A

 

时间戳

 

时间戳我们有两个标记,第一个是第一次访问的时候记录一下,然后是在最后一次访问时再标记一下。 

时间戳的性质:我们可以直接通过时间戳来判断一个节点是否是另一个节点的子节点。时间戳之后,那么树的每个节点就具有了区间的性质。 

 

DFS序

DFS序核心代码

const int maxn = 1e5+5;
vector<int> g[maxn]; //存放节点
int s[maxn], e[maxn];//s[maxn]存放“入时间戳”,e[maxn]存放“出时间戳”;
int n,id,len;
int dfsxu[20000];//存放dfs序

void dfs(int u, int fa) {
	s[u] = ++id;
	dfsxu[++len]=u;
	for(int i = 0; i < g[u].size(); ++i) {
		int v = g[u][i];
		if(v == fa) continue;
		dfs(v, u);
	}
	e[u] = id;
}

 DFS序完整代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
vector<int> g[maxn]; //存放节点
int s[maxn], e[maxn];//s[maxn]存放“入时间戳”,e[maxn]存放“出时间戳”;
int n,id,len;
int dfsxu[20000];//存放dfs序

void dfs(int u, int fa) {
	s[u] = ++id;
	dfsxu[++len]=u;
	for(int i = 0; i < g[u].size(); ++i) {
		int v = g[u][i];
		if(v == fa) continue;
		dfs(v, u);
	}
	e[u] = id;
}
int main() {
    memset(s,0,sizeof(s));
    memset(e,0,sizeof(e));
    memset(dfsxu,0,sizeof(dfsxu));
	ios::sync_with_stdio(0);
	cin >> n;
	for(int i = 1; i < n; ++i) {
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	id = 0;
	dfs(1, -1);
    cout<<"DFS序:";
    for(int i=1;s[i]!=0;i++)
    {
        cout<<dfsxu[i];
    }
    cout<<endl;
    cout<<"入时间戳:";
	for(int i=1;s[i]!=0;i++)
    {
        cout<<s[i];
    }
    cout<<endl;
    cout<<"出时间戳:";
    for(int i=1;e[i]!=0;i++)
    {
        cout<<e[i];
    }
    cout<<endl;

	return 0;
}

运行结果:

通过dfs序判断v节点的时间区间是否在u节点的时间区间内

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
vector<int> g[maxn];
int s[maxn], e[maxn];
int n, q, id;
void dfs(int u, int fa) {
	s[u] = ++id;
	for(int i = 0; i < g[u].size(); ++i) {
		int v = g[u][i];
		if(v == fa) continue;
		dfs(v, u);
	}
	e[u] = id;
}
int main() {
	ios::sync_with_stdio(0);
	cin >> n >> q;
	for(int i = 1; i < n; ++i) {
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	id = 0;
	dfs(1, -1);
	for(int i = 1; i <= q; ++i) {
		int u, v;
		cin >> u >> v;
		if(s[u] <= s[v] && e[v] <= e[u]) {
			cout << "YES" << endl;
		}
		else {
			cout << "NO" << endl;
		}
	}
	return 0;
}

 

欧拉序

核心代码:

vector<int> g[40010]; //存放节点
int oulaxu[80020]; //存放欧拉序在,在欧拉序中第一次出现为“入时间戳”,第二次出现为“出时间戳”。
int len;

void dfs(int u,int fa)
{
    oulaxu[++len]=u;
    int sz=g[u].size();
    for(int i=0; i<sz; i++)
    {
        if(g[u][i]!=fa)
            dfs(g[u][i],u);
        }

    oulaxu[++len]=u;
}

完整代码:

#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

vector<int> g[40010]; //存放节点
int oulaxu[80020]; //存放欧拉序在,在欧拉序中第一次出现为“入时间戳”,第二次出现为“出时间戳”。
int len;

void dfs(int u,int fa)
{
    oulaxu[++len]=u;
    int sz=g[u].size();
    for(int i=0; i<sz; i++)
    {
        if(g[u][i]!=fa)
            dfs(g[u][i],u);
        }

    oulaxu[++len]=u;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        len=0;
        memset(oulaxu,0,sizeof(oulaxu));
        scanf("%d",&n);
        for(int i=1; i<=n; i++)
        {
            g[i].clear();
        }
        for(int i=1; i<=n-1; i++)
        {
            int from,to;
            scanf("%d%d",&from,&to);
            g[from].push_back(to);
            g[to].push_back(from);
        }
        dfs(1,0);
        for(int i=1;i<=len;i++)
        {
            printf("%d ",oulaxu[i]);
        }
    }

}

 运行结果

通过欧拉序求u和v的最近公共祖先 

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
vector<int> g[maxn];
int E[maxn*2], R[maxn], dep[maxn];
int f[maxn*2][20];
int n, q, cnt;
void dfs(int u, int fa, int deps) {
	E[++cnt] = u;
	R[u] = cnt;
	dep[u] = deps;
	for(int i = 0; i < g[u].size(); ++i) {
		int v = g[u][i];
		if(v == fa) continue;
		dfs(v, u, deps+1);
		E[++cnt] = u;
	}
}
void rmq_init() {
	for(int i = 1; i <= cnt; ++i) {
		f[i][0] = E[i];
	}
	for(int j = 1; (1<<j) <= cnt; ++j) {
		for(int i = 1; i+(1<<j)-1 <= cnt; ++i) {
			int u = f[i][j-1], v = f[i+(1<<(j-1))][j-1];
			f[i][j] = dep[u] < dep[v] ? u : v;
		}
	}
}
int rmq(int i, int j) {
	int k = log2(j-i+1);
	int u = f[i][k], v = f[j-(1<<k)+1][k];
	return dep[u] < dep[v] ? u : v;
}
int lca(int u, int v) {
	return rmq(min(R[u], R[v]), max(R[u], R[v]));
}
int main() {
	ios::sync_with_stdio(0);
	cin >> n >> q;
	for(int i = 1; i < n; ++i) {
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	cnt = 0;
	dfs(1, -1, 1);
	rmq_init();
	for(int i = 1; i <= q; ++i) {
		int u, v;
		cin >> u >> v;
		cout<<lca(u,v)<<endl;
	}
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值