为了使用vector,需要在C++源文件添加
#include<vector>
using namespace std;
用来表示一条边
struct Edge{
int nextNode; //下一个结点编号
int cost;// 该边的权重
};
为每个结点都创建一个单链表
vector<Edge> edge[N];
为这些单链表添加和删除信息
for(int i=0;i<N;i++)
{
edge[i].clear();
}
Edge tmp;
tmp.nextNode = 3;
tmp.cost = 38;
edge[1].push_back(tmp);
for(int i=0;i<edge[2].size();i++)
{
int nextNode = edge[2][i].nextNode; //类似于对二维数组的访问
int cost = edge[2][i].cost;
}
edge[1].erase(edge[1].begin()+i,edge[1].begin()+i+1);
在图论中经常用到的一种数据结构:集合—并查集
使用双亲节点表示法来表示一棵树
①判断两个数字是否在一个集合中: 若两个元素所处的树的根节点相同,则再一个集合中
②合并两个集合:其中一棵树变成另一树的子树—其中一棵树的根节点的双亲节点变为另一棵树的根节点—-要加上约束
路径压缩,在查找某个特定结点的根节点的同时将其与根节点之间的所有节点都直接指向根节点
int Tree[N]; //用Tree[i]表示节点i的双亲节点,-1表示该节点没有双亲节点--根节点
关键:查找结点X所在树的根节点
int findRoot(int x)
{
if(Tree[x]==-1) return x;
else
{
int tmp = findRoot(Tree[x]);
Tree[x] = tmp; //将当前节点的双亲节点设置为查找返回的根节点编号
return tmp;
}
}
题目一:
还需要建设多少条道路?
思路:在一个图上查找连通分量的个数
①初始时,每个结点都是独立的连通分量
②读入已经建成的边,将边的两个顶点所在集合合并……重复
③ 最后计算所有的结点被保存在几个集合中
#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
using namespace std;
#define N 1000
int Tree[N];
int findRoot(int x)
{
//查找某个结点所在树的根节点
if (Tree[x] == -1)
return x;
else
{
int tmp = findRoot(Tree[x]);
Tree[x] = tmp;
return tmp;
}
}
int main()
{
int n, m;
while (scanf("%d%d", &n, &m) != EOF)
{
if (n == 0) break;
for (int i = 0; i < n; i++)
{
Tree[i] = -1; //n个结点
}
while (m--)
{
int a, b;
scanf("%d%d", &a, &b);
a = findRoot(a);
b = findRoot(b);
if (a != b) Tree[a] = b;
}
int ans = 0;
for (int i = 0; i < n; i++)
{
if (Tree[i] == -1) ans++; //统计根节点个数--集合数目
}
printf("%d\n", ans-1);
}
return 0;
}
题目二:
10000000个小朋友,之中有N对好朋友,给出这N对朋友关系后,要求找出最大的集合,该集合中任意两人都是朋友
思路: 在每个集合的树的根节点记录该集合所包含的元素个数,在合并时累加被合并的两个集合包含的元素个数。最后找出集合中所包含元素最多的集合
#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
using namespace std;
#define N 10000001
int Tree[N];
int findRoot(int x)
{
if (Tree[x] == -1)
return x;
else
{
int tmp = findRoot(Tree[x]);
Tree[x] = tmp;
return tmp;
}
}
int sum[N]; //表示以结点i为根的树的结点个数
int main()
{
int n;
while (scanf("%d", &n) != EOF)
{
for (int i = 0; i < N; i++)
{
Tree[i] = -1;
sum[i] = 1;
}
while (n--)
{
int a, b;
scanf("%d%d", &a, &b);
a = findRoot(a);
b = findRoot(b);
if (a != b)
{
Tree[a] = b;
sum[b] += sum[a];
}
}
int ans = 1;
for (int i = 0; i < N; i++)
{
if (Tree[i] = -1 && sum[i] > ans)
ans = sum[i];
}
printf("%d\n", ans);
}
return 0;
}
最小生成树
生成树:在一个无相连通图中,如果存在一个连通子图包含原图中所有顶点和不分辨,且这个子图不存在回路
最小生成树: 所有生成树中边权的和最小的那棵
Kruskal算法:
①初始化为所有节点都是独立的
② 按照边权递增的顺序遍历所有边,若遍历到的边的两个顶点属于不同集合,则确定该边为最小生成树上的一条边,并将这两个顶点分属的集合合并
③遍历完所有边,所有节点应该属于统一集合—否则原图不连通
题目一:要求铺设的公路总长度最短