图论基础
前言
有关图论的更多知识点请移步作者“图论与数据结构”专栏。
专栏地址:图论与数据结构专栏
一、无根树转有根树
- 问题描述:输入一个n个结点的无根树的各条边,指定一个根节点要求转化为有根树。
- 分析:树是一种特殊的图,我们可以用vector数组来存下这个图。然后定义一个数组p[n],p[i]为i的父节点。
代码实现
#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 7;
vector<int>G[N];
int p[N];
int n;
void read()
{
int u, v;
cin >> n;
for (int i = 0; i < n - 1; i++)
{
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
}
void dfs(int u, int fa)
{
int d = G[u].size();
for (int i = 0; i < d; i++)
{
int v = G[u][i];
if (v != fa)
{
p[v] = u;
dfs(v, u);
}
}
}
void solve()
{
mem(p, - 1);
read();
dfs(1, -1);
}
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
solve();
return 0;
}
二、表达式树
- 概念介绍:
二叉树是表达式处理的常用工具,例如a+b*(c-d)-e/f
可以表示为如图的二叉树
只有叶子节点为字母,根节点为运算符号。
对于表达式树的描述,有一篇博客讲的比较详细,在这里附上这篇博客的地址
表达式树(作者:fireflylane)
这里把紫书上代码贴过来解释一下
const int maxn = 1000;
int lch[maxn], rch[maxn]; char op[maxn]; //左儿子、右儿子、后缀表达式数组
int nc = 0; //结点数
int build_tree(char* s, int x, int y) //xy代表子字符串的左右边界
{
int i, cl = -1, c2 = -1, p = 0;
int u;
if(y-x == 1) //仅一个字符,到子节点了,建立单独结点
{
u = ++nc;
lch[u] = rch[u] = 0; op[u] = s[x];
return u;
}
for(i = x; i < y; i++)
{
switch(s[i])
{
case '(': p++; break;
case ')': p--; break;
case '+': case '-': if(!p) c1 = i; break;
case '*': case '/': if(!p) c2 = i; break;
}
}
if(c1 < 0) c1 = c2; //找不到括号外的加减号,就用乘除号
if(c1 < 0) return build_tree(s, x+1, y-1); //整个表达式被一对括号括起来
u = ++nc;
lch[u] = build_tree(s, x, c1);
rch[u] = build_tree(s, c1+1, y);
op[u] = s[c1];
return u;
}
在代码的最后,我们得到了一个op[N]数组,这个数组就是运算式的后缀表达式,比如在样例中,op[]=“abcd-*+ef/-”
例题博客讲解:【解题报告】表达式求值
三、最小生成树
1.Kruskal算法
- 时间复杂度为O(m),效率很高、很好理解的算法
- 作者博客链接:Kruskal求最小生成树
2.Prim算法
- 时间复杂度:优化前O(n2),优先队列优化后O(mlogn)
- 作者博客链接:Prim求最小生成树
四、最短路问题
1.Dijkstra算法
- 适用范围:边权为正
- 时间复杂度:优化前O(n2),优先队列优化后O(mlogn)
- 作者博客链接:Dijkstra算法
2.Bellman_Ford算法
- 适用范围:可以有负权边,有步数限制时只能用Bellman_Ford算法
- 时间复杂度:O(nm)
- 作者博客链接:Bellman_Ford算法
3.Spfa算法
- 适用范围:Bellman_Ford算法的优化算法,可以处理负权边
- 时间复杂度:O(nm),但是在一般情况下快于Bellman_Ford算法
- 作者博客链接:Spfa算法
4.Floyd算法
- 适用范围:求任意两点间最短路
- 时间复杂度:O(n3),复杂度不低,使用的时候需要小心超时
- 作者博客链接:Floyd算法
五、乘法最短路
当边权为非负数的时候,处理最短路只需要把Dijkstra与Spfa算法中的加号变为乘号即可。
六、乘法最长路
1.权值在0~1之间
这个时候同样只需要直接改一下Dijkstra和Spfa算法的代码就可以了,直接把‘ + ’改为‘ * ’
2.权值非负
此时我们不能用Dijkstra来计算了,因为Dijkstra算法有贪心的思想在,但是这里局部最优解将不一定再是最后最优解。这里只能用spfa算法去解决。
作者:Avalon Demerzel,喜欢我的博客就点个赞吧,更多知识点请见作者专栏《图论与数据结构》与《紫书学习笔记》