一、预备知识
1.表示图的2种数据结构:邻接矩阵、邻接链表。
①邻接矩阵:用一个二维数组来表示图的相关信息,表示的图为稠密图,且频繁用于判断某个特点的节点对是否相邻时,使用
邻接矩阵较合适。若遍历某节点相邻的所有节点时,则比较麻烦。
②邻接链表:为图中的每个节点建立一个单链表,第i个链表保存与结点vi相邻的所有链表。所以用于大量遍历某个结点的所有
邻接结点时,使用邻接链表较合适。
2.如何快速使用邻接链表?推荐使用STL中的标准模板std::vector。
①定义一个结构体用来表示一条边。
struct Edge {
int nextNode; //下一个结点编号
int cost; //边的权值
};
②为每个结点都建立一个单链表用来保存与其相邻的结点及权值。
vector<Edge> edge[N]
建立了一个大小为N的数组,数组中的每个元素保存的是vector对象。为每个结点都建立了一个vector对象。
③对单链表进行操作
为了使用vector需要在头部加入相应的头文件:
#include <vector>
using namespace std;
对单链表进行初始化,即 利用vector::clear()操作清空单链表:
for(int i = 0;i < N;i++)
edge[i].clear();
利用vector::push_back(Edge)添加信息:
Edge tmp;
tmp.nextNode = 3;
tmp.cost = 38;
edge[i].push_back(tmp);
需要查询某个结点的所有邻接结点时,对vector进行遍历:
for(int i=0;i<edge[2].size();i++) {
int nextNode = edge[2][i].nextNode;
int cost = edge[2][i].cost;
}
删除结点1的单链表中edge[1][i]所对应的边信息时:
edge[1].erase(edge[1].begin()+i,edge[1].begin+i+1);
需要记住以上vector的基本用法,对单链表进行清空、添加、删除、遍历等操作。
二、并查集
1.图论中经常要用到的数据结构----集合,还有它的一个相关操作----并查集。
这种数据结构用来表示集合信息,用以实现如确定某个集合中是否有哪些元素、判断某两个元素是否在同一个集合中、求集合中元素数量等。
2.可以通过不断的求双亲结点来找到该结点所在树的根结点,若2个结点的根节点相同,则可以判定它们在同一棵树上,它们属于一个结合。
3.合并两树的过程中,若仅仅简单的将两树合并在一起,则树高可能会逐渐增加,极端情况下会变成一个单链表,这样查找的耗时就增加了。
为了避免这种情况,我们可以在查找某个结点的根节点的时候,同时将其与根节点之间所有的结点都直接指向根节点,这个过程称为路径压缩。
4.代码实现。
定义一个数组,用双亲法表示树,tree[i]表示i的双亲结点,若tree[i] = -1表示该结点为根节点。
int tree[N];
递归版本:
int findRoot(int x) {
if(tree[x] == -1)
return x;
else return(tree[x]);
}
非递归版本:
int findRoot(int x) {
int ret;
while(tree[x] != -1)
x = tree[x];
ret = x;
return ret;
}
加上路径压缩的优化:
int findRoot(int x) {
if(tree[x] == -1) return x;
else {
int tmp = findRoot(tree[x]);
tree[x] = tmp;
return tmp;
}