图有三种表示方法:
**1.邻接矩阵:**利用一个n*n大小的方阵,
A[i][j] = 1表示顶点 i 和 j 是相邻的,即 i 和 j 之间有一条边
A[i][j] = 0表示顶点 i 和 j 是不相邻的,即 i 和 j 之间有没有边
#include<iostream>
#define max 10
using namespace std;
int V;//顶点个数
int E;//边的个数
int adjm[max][max] = {0};
void initE(int x,int y, int w);//初始化边,w为坐标
int main(){
for(int i = 0; i < 10; i++){//输出邻接矩阵
for(int j = 0; j < 10; j++){
cout << adjm[i][j] << " ";
}
cout << endl;
}
return 0;
}
void initE(int x,int y, int w){ //w为坐标
adjm[x][y] = w;
}
邻接矩阵的实现代码比较简单,下面分析一下其时间和空间的复杂度。
空间复杂度:
O(V^2) 即节点个数的平方,如果有3000个节点,那么需要开出的空间为9x10^6,需要改进
时间复杂度:
建图: O(E) 有E条边,那么E条边的信息都需要存储,这个时间复杂度无法继续提升
查询两点是否相邻: O(1) 即判断a[i][j]是否为1即可,这个时间复杂度也无法提升
**查询某一顶点的邻居:**O(V) 判断a[i]这个数组中有多少个元素为1
其空间复杂度能否修改为O(V+E)呢,即V个节点和E条边的信息存储空间大小为O(V+E)
邻接表解决了这个问题:
邻接表的大致思想如上图所示,这里不再用文字叙述
代码实现(无向有权图):
typedef struct AdjNode{
int N;//Adjvex是该邻接点自己在顶点表中的位
//置(顶点表中的数组索引值),而不是与其相连的顶点在顶点表中的位置,更不是该节点保存的数据。
int w;//权重
AdjNode* next;
}AdjN;
typedef struct VNode{
int data;
AdjN* first;
}VN;
typedef struct Graph{
int v;//顶点个数
int e;//边的个数
VN ag[max];
}G;
void initG(G* g);
void printG(G* g);//打印节点信息
void printLink(G* g);//打印链表信息
bool hasedge();//暂未实现 快速查重
int nei_v();//暂未实现
int main(){
G* g = (G*)malloc(sizeof(G));
initG(g);
printG(g);
printLink(g);
return 0;
}
void initG(G* g){
cout<<"输入顶点和边的个数:";
int v,e;
cin >> v >> e;
g->v = v;
g->e = e;
cout<<"请输入个顶点数据:" ;
int data;
for(int i = 0; i < g->v; i++){
cin >> data;
g->ag[i].data = data;
g->ag[i].first = NULL;
}
for(int i = 0; i < g->e; i++){
int p1, p2, w;
cout<<"输入边:";
cin >> p1 >> p2 >> w;
AdjN* node1 = (AdjN*)malloc(sizeof(AdjN));
node1->N = p2;
node1->w = w;
AdjN* temp = g->ag[p1].first;
g->ag[p1].first = node1;
node1->next = temp;
//双向
AdjN* node2 = (AdjN*)malloc(sizeof(AdjN));
node2->N = p1;
node2->w = w;
temp = g->ag[p2].first;
g->ag[p2].first = node2;
node2->next = temp;
}
}
void printG(G* g){//打印节点信息
for(int j = 0; j < g->v; j++){
cout << j << " " << g->ag[j].data <<endl;
}
}
void printLink(G* g){//打印链表信息
for(int i = 0; i < g->v; i++){
cout << i <<" ";
AdjN * Node = g->ag[i].first;
while(Node != NULL){
cout<< Node->N << "(" << Node->w << ") ";
Node = Node->next;
}
cout << endl;
}
}
空间复杂度分析:O(V+E)已经达到最优
时间复杂度:主要分析其查重能力,即查看一个顶点与另外一个顶点是否存在一条边时:O(degree(V)),取决于该顶点的度。
邻接表的问题
在快速查重的问题(任给两个节点,判断这两个节点之间是否有边)上的时间复杂度为O(degree(V))
而在V比较大时,其表现性能并不是那么完美
解决方法
针对其主要问题,
若使用链表,其查询的时间复杂度必然为线性的
若使用哈希表,其查询时间复杂度为O(1)
若使用红黑树,其查询时间复杂度为O(logV)
采用不同的数据结构,可降低其查询时间复杂度
下面为邻接表的升级版代码:
typedef struct VNode{
int data;
map<int,int> mp;//map key 是节点号,即该节点到key之间有一条边,权重是value
}VN;
typedef struct Graph{
int v,e;
VN ag[max];
}G;
void initG(G* g);
void printG(G* g);//打印节点信息
void printLink(G* g);//打印链表信息
int main(){
G* g = new G;
initG(g);
printG(g);
printLink(g);
return 0;
}
void initG(G* g){
cout<< "Input the amount of Node and Edge: ";
cin >> g->v >> g->e ;
//Input the data of Node
cout<< "Input Data:" << endl;
for(int i = 0; i < g->v; i++){
cin >> g->ag[i].data ;
}
//Input the edge and weight
cout<< "Input Edge and Weight:"<<endl;
for(int i = 0; i < g->e; i++){
int node1, node2, w;
cin >> node1 >> node2 >> w;
g->ag[node1].mp[node2] = w;
g->ag[node2].mp[node1] = w; //无向图
}
}
void printG(G* g){//打印节点信息
cout<<"Output The Info of Node:"<<endl;
for(int i = 0; i < g->v; i++){
cout<< i <<" "<< g->ag[i].data << endl;
}
}
void printLink(G* g){//打印链表信息
cout << "OutPut The Info of NLink:"<<endl;
for(int i = 0; i < g->v; i++){
cout << i << " ";
for(map<int,int>::iterator it = g->ag[i].mp.begin(); it != g->ag[i].mp.end(); it++){
cout << it->first << "(" << it->second << ")" << " ";
}
cout << endl;
}
}
在输出中可发现,每个顶点都是按顺序输出的,虽然我用的时hash_map,但是在STL中其map是用红黑树实现的,其在内部会自动排序,为以后的算法提高了效率。