八.图论
今年408考研数据结构算法题破天荒的考了图的算法,大批学子,也包括我吃了大亏,图的算法不能停留在原理理解上,算法也必须要掌握。
(1)简单并查集的一些操作:
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN = 1000;
int father[MAXN];
void Initial(int n){
for(int i = 0;i < n;i++){
father[i] = i;
}
return;//初始化只有一个结点
}
int Find(int x){ //查询
if(x != father[x]){
return Find(father[x]);
}
return father[x];
}
void Union(int x,int y){ //合并
x = Find{x};
y = Find(y);
if(x != y)
father[x] = y;
}
(2)查找:路径压缩
是为了更快速的查找一个点的根节点。对于一个集合树来说,它的根节点下面可以依附着许多的节点,因此,我们可以尝试在 find 的过程中,从底向上,如果此时访问的节点不是根节点的话,那么我们可以把这个节点尽量的往上挪一挪,减少数的层数,这个过程就叫做路径压缩。
如下图中,find(4) 的过程就可以路径压缩,让树的层数更少。
节点 4 往上寻找根节点时,压缩第一步,树的层数就减少了一层:
最终得到:
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN = 1000;
int father[MAXN];
int height[MAXN];
void Initial(int n){
for(int i = 0;i < n;i++){
father[i] = i;
height[i] = 0;
}
return;//初始化只有一个结点
}
int Find(int x){ //查询
if(x != father[x]){
return Find(father[x]);
}
return father[x];
}
void Union(int x,int y){ //合并
x = Find{x};
y = Find(y);
if(x != y){
if(height[x] < height[y]){
father[x] = y;
}else if(height[x] > height[y]){
father[y] = x;
else{
father[y] = x;//father[x] = y也可以
height[x] +=1;
}
}
}
(3)连通图
无向图-极大连通子图
前面代码同上
int main(){
int n,m;//n为顶点,m为边
while(scanf("%d%d",&n,&m) != EOF){
if(n == 0 && m ==0){
break;
}
Initial(n);//初始化循环内为<=n
while(m--){
int x,y;
scanf("%d%d",&x,&y);
Union(x,y);
}
int conmponrt = 0;
for(int i = 1;i <= n;++i){
if(i == Find(i)){
component++;
}
}
if(component == 1)
printf("YES\n");
else printf("NO\n");
}
(4)最小生成树(MST)
生成树:连通图的极小连通子图
MST:带权图所有生成树中边权值之和最小的那一颗
问题10:某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府目标是使全省任何两个村庄间都可以实现公路交通,并要求铺设的公路总长度最短,请计算最小的公路总长度。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100;
struct Edge{
int from;
int to;
int length;
bool operator <(const Edge &e)const{
return length < e.length;
}
};
Edge edge[MAXN*MAXN];
int father[MAXN];
int height[MAXN];
void Initial(int n){
for(int i = 0;i < n;i++){
father[i] = i;
height[i] = 0;
}
return;//初始化只有一个结点
}
int Find(int x){ //查询
if(x != father[x]){
return Find(father[x]);
}
return father[x];
}
void Union(int x,int y){ //合并
x = Find{x};
y = Find(y);
if(x != y){
if(height[x] < height[y]){
father[x] = y;
}else if(height[x] > height[y]){
father[y] = x;
else{
father[y] = x;//father[x] = y也可以
height[x] ++;
}
}
return;
}
int Kruskal(int n,int edgeNumber){
Initial(n);
srot(edge,edge+edgeNumber);
int sum = 0;
for(int i = 0;i < edgeNumber;++i){
Edge current = edge[i];
if(Find(current.from != Find(current.to)){
Union(current.from,current.to);
sum += current.length;
}
}
return sum;
}
int main(){
int n;
while(scanf("%d",&n) != EOF){
if(n == 0) break;
int edgeNUmber = n*(n-1)/2;
for(int i = 0;i < edgeNumber;++i){
scanf("%d%d",&edge[i].from,&edge[i].to,&edge[i].length);
}
int answer = Kruskal(n,edgeNumber);
printf("%d\n",answer);
}
return 0;
}
无论哪种,prim+heap都是性能最佳的算法,但实现起来较复杂
时间复杂度:kruskal:O(ElogE)
prim:O(V^2)