图
相关概念
图: 由顶点V(G1)={v1,v2,v3}和边E(G1)={<v1,v2>,<v2,v1>,<v2,v3>}组成,其中边中<v1,v2>,v1为起点,v2终点
有向完全图: 边的数量n(n-1)
无向完全图: 边的数量n(n-1)/2
邻接点: 一个顶点周围的顶点(只要有边连接),都是它的邻接点
度: 进度和出度之分,就是边的方向
根: 在有向图中,一条路径的起点称为根
连通图: 若任意两个不同的顶点都是连通的(有路径,不管路径长度是多少),则此图称为连通图
极大连通分量: 显然连通图的极大连通分量只有一个,即本身
有向图: 每条边都是有方向的
图的储存
用邻接矩阵表示法,比如一个图五个顶点,那么将存在5*5的矩阵,1来表示两个点之间有连接,0表示没有
如果图是网络,那么1变成权值
邻接矩阵表示法:
/*
* 邻接矩阵表示法
*/
#include<iostream>
using namespace std;
#define n 4//图的顶点数
#define e 5//图的边数
typedef char vextype;//顶点的数据类型
typedef float adjtype;//权值类型
typedef struct
{
vextype vexs[n];
adjtype arcs[n][n];
}grath;
void createGrath(grath *g)//建立无向网络图,先确定顶点下来,初始化,然后在边上加上权值就好
{
for(int i=0;i<n;i++)cin>>g->vexs[i];
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
g->arcs[i][j]=0;
}
}
for(int i=0;i<e;i++)
{
int i1,i2;
adjtype w;
cin>>i1>>i2>>w;
g->arcs[i1][i2]=w;
g->arcs[i2][i1]=w;
}
}
void display(grath *g)
{
for(int i=0;i<e;i++)//输出边字符
{
cout<<g->vexs[i]<<"\t";
}
cout<<endl;
for(int i=0;i<n;i++)//输出矩阵
{
for(int j=0;j<n;j++)
{
cout<<g->arcs[i][j]<<"\t";
}
cout<<endl;
}
}
static int visit[n];//深度遍历时需要
void DFS(grath *g,int i)//深度遍历,输出节点的信息,i为出发的节点
{
cout<<"node:"<<g->vexs[i]<<endl;
visit[i]=1;//表示已经访问过
for(int j=0;j<n;j++)
if(!visit[j]==1&&g->arcs[i][j]==1)
DFS(g,j);
}
int main()
{
grath g;
createGrath(&g);
display(&g);
DFS(&g,1);
return 0;
}
邻接表表示法:
/*
* 邻接表表示法(顶点表->边表)
*
*/
#include<stdlib.h>
#include<string>
#include<sstream>
#include<iostream>
using namespace std;
#define n 4
#define e 5
typedef char vextype;
typedef struct node
{
int adjvex;
struct node* next;
}edgenode;//边表节点
typedef struct
{
vextype vertex;
edgenode *link;
}vexnode;//顶点表节点
vexnode gg[n];
void creatGrath(vexnode ga[])
{
int i, j, k;;
edgenode *s=NULL;
cout << "input top node information:\n";
for (i = 0;i < n;i++) //读入顶点信息,建立了顶点表节点
{
cin >> ga[i].vertex;//top node information
ga[i].link = NULL;
}
//cout << "node and node relation:\n";
for (k = 0;k < e;k++)//建立边表(主要的方法:将边表节点记录节点的序号,然后然后会找到两个相应的顶点节点进行插入)
{ //就好比节点1和节点2有边,然后输入的值为1,2,那会在顶点节点1进行2节点插入
//在顶点节点2进行节点1的插入
/*注意:因为储存时输入的边是不算0的,但是这里面顶点从0开始*/
cin >> i>>j;
s =new edgenode;
s->adjvex = j;//邻节点
s->next = ga[i-1].link;//链
ga[i-1].link = s;
s = new edgenode;
s->adjvex = i;
s->next = ga[j-1].link;
ga[j-1].link = s;
}
}
/*
* 显示建立的邻接表,方法:创建n的字符串,存放顶点和节点的信息
*/
string str[n];
void displayGrath(vexnode ga[])
{
edgenode *p;//保证了next的不变性
for(int i=0;i<n;i++)
{
p=ga[i].link;
str[i]=ga[i].vertex;
str[i]+=":";
while(p!=NULL)
{
str[i]+="-> ";
str[i]+='0'+p->adjvex;
p=p->next;
}
str[i]+="-> NULL";
cout<<str[i]<<endl;
}
}
/*
* 深度遍历
* 遍历顶点,从顶点节点开始找,下一个如果存在节点,且标志的下一个位置没有访问过,那就出发到节点标志的顶点。
* 如果下一个节点为空或者已经访问过,递归返回上层。
* --i是因为顶点从0存储,但是i是从1开始
*/
int visit[n];
void DFSL(vexnode ga[],int i)
{
--i;
edgenode *p;
p=ga[i].link;
cout<<"node:"<<ga[i].vertex<<endl;
visit[i]=1;
while(p!=NULL)
{
if(!visit[p->adjvex-1]){DFSL(ga,p->adjvex);}
p=p->next;
}
}
int main()
{
vexnode ga[n];
creatGrath(ga);
displayGrath(ga);
displayGrath(ga);
DFSL(ga,1);
return 0;
}
最小生成树
生成树是极小连通图,边数为n-1,如果多一条边就会变成回路,少一条边就不是连通图
把生成树各边的值的权的总和称为生成树的权,权值最小时称为最小生成树。
/*
* 实现最小生成树算法
* 1.创建grath,由邻接矩阵储存
* 2.最终的结果是连接的顺序出现,如1-2-3,表示1和2相连,2和3相连
* 算法介绍:
* 1.任意选取其中一个点,生成了n-1个边,如果他们之间没有边则权值为0
* 2.找到权值最小的边,那就找到了下一个节点所在地,下一个节点再寻找周围的边,如果存在和上一个节点连通的边,
* 比较大小,如果小,则去掉上一个节点与所找节点的边,寻找最小边进入下一个节点,t[k]里面k++。
* 3.最终的结果存放在t里面(n-1条边的信息,即最小生成树)
*/
/*
* 例子:6个顶点,10条边
* 邻接矩阵图
* 0 6 1 5 0 0
* 6 0 5 0 3 0
* 1 5 0 7 5 4
* 5 0 7 0 0 2
* 0 3 5 0 0 6
* 0 0 4 2 6 0
* 输入的数据:
* 1 2 3 4 5 6
* 0 1 6
* 0 2 1
* 0 3 5
* 1 4 3
* 1 2 5
* 2 4 5
* 2 3 7
* 3 5 2
* 2 5 4
* 4 5 6
*
* 结果:
*创建图,1 2 3 4 5 6为节点信息,其余为边如0 1 2表示从节点0到节点1的边的权为
* 1 2 3 4 5 6
* 0 1 6
* 0 2 1
* 0 3 5
* 1 4 3
* 1 2 5
* 2 4 5
* 2 3 7
* 3 5 2
* 2 5 4
* 4 5 6
* 最小生成树算法开始
* 结果是(邻接矩阵表示),后面加上了边的信息:
* 0 0 1 0 0 0
* 0 0 0 0 3 0
* 1 0 0 0 5 4
* 0 0 0 0 0 2
* 0 3 5 0 0 0
* 0 0 4 2 0 0
* 021
* 254
* 532
* 245
* 413
*/
#include<iostream>
using namespace std;
#define n 6//图的顶点数
#define e 10//图的边数
typedef char vextype;//顶点的数据类型
typedef float adjtype;//权值类型
typedef struct
{
vextype vexs[n];
adjtype arcs[n][n];
}grath;
void createGrath(grath *g)//建立无向网络图,先确定顶点下来,初始化,然后在边上加上权值就好
{
for(int i=0;i<n;i++)cin>>g->vexs[i];
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
g->arcs[i][j]=101;
}
}
for(int i=0;i<e;i++)
{
int i1,i2;
adjtype w;
cin>>i1>>i2>>w;
g->arcs[i1][i2]=w;
g->arcs[i2][i1]=w;
}
}
void display(grath *g)
{
for(int i=0;i<e;i++)//输出边字符
{
cout<<g->vexs[i]<<"\t";
}
cout<<endl;
for(int i=0;i<n;i++)//输出矩阵
{
for(int j=0;j<n;j++)
{
cout<<g->arcs[i][j]<<"\t";
}
cout<<endl;
}
}
/*
* 定义一种数据结构,存放边的值<v1,v2,w>
*
*/
struct edgenode
{
int i,j;//行列,也就是v1,v2的位置
float w;//权值
};
edgenode t[n-1];
void PRIM(grath *g)
{
//1.建立n-1个边edgenode,T[n-1]存放节点和权值
for(int i=0;i<n-1;i++)
{
t[i].i=0;
t[i].j=i+1;
t[i].w=g->arcs[0][i+1];
}
//2.修改t[n-1]得值,将找到的合适的节点存放下来,然后k++
for(int k=0;k<n-1;k++)
{
//2.1找到t里面最小的权值,记录下位置
int min=100;
int location=0;
for(int j=k;j<n-1;j++)
{
if(t[j].w==0)continue;
if(t[j].w<min)
{
min=t[j].w;
location=j;
}
}//现在t[location]是最短边
//2.2最短边进入t里面保存不变
edgenode ed=t[location];t[location]=t[k];t[k]=ed;//找到最小的那个,自然存放在t里面,故交换位置,那就可以t下标位置跳过这个位置
int v=t[k].j;//现在的节点的位置
//2.3将t更新,t的路径大就用小的取代
for(int j=k+1;j<n-1;j++)
{
float d=g->arcs[v][t[j].j];
if(d<t[j].w)
{
t[j].w=d;
t[j].i=v;//由于两个边通向那个节点,所以改变那个节点的i值,指向同一个,那就相当于删去一个路径,只剩一条了
}
}
}
}
/*
* 输出最小生成树的邻接矩阵
*/
void displayPRIM(grath *g,edgenode t[])
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
g->arcs[i][j]=0;
}
}
for(int i=0;i<n-1;i++)
{
g->arcs[t[i].i][t[i].j]=t[i].w;
g->arcs[t[i].j][t[i].i]=t[i].w;
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
cout<<g->arcs[i][j]<<"\t";
}
cout<<endl;
}
for(int i=0;i<n-1;i++)
cout<<t[i].i<<t[i].j<<t[i].w<<endl;
}
int main()
{
grath g;
cout<<"创建图,1 2 3 4 5 6为节点信息,其余为边如0 1 2表示从节点0到节点1的边的权为\n";
createGrath(&g);
cout<<"最小生成树算法开始\n";
PRIM(&g);
cout<<"结果是(邻接矩阵表示),后面加上了边的信息:\n";
displayPRIM(&g,t);
return 0;
}