图的学习
1、图的定义
图是一个由有穷非空集合和顶点之间边的集合组成的,表示为G(V,E)
G表示一个图,V表示图G中顶点的集合,E 是图G中边的集合
2.概念
无向边,Vi到Vj之间的边没有方向,表示:(Vi,Vj)
有向边,Vi到Vj的边有反向,弧
A表示弧尾,D表示弧头,<A,D>表示弧
连通图,V到V‘有路径,则连通,任意都符合
连通分量:无向图中的极大连通子图
强连通图:Vi到Vj和Vj到Vi都存在路径
强连通分量:有向图中极大强大连通子图
生成树:一个连通树的生成树是一个极小的连通子图,它含有图中全部的N个结点,但只有足以构成一棵树的N-1条边
图顶点与边间的关系
有向图中顶点入度为0,其余顶点入度为1的为有向树,一个有向图由若干棵有向树构成生成森林
3.存储结构
邻接矩阵是由 V x V 顶点组成的二维数组,每一行和每一列代表一个顶点。
邻接表数组的索引表示一个顶点,其链表中的每个元素表示与该顶点形成边的其他顶点。
4.图的基本操作
增删改查!四个基本操作
代码实现
板子:
``#include<iostream>`
`#include<queue>`
`using namespace std;`
`const int INF = 0x7fffffff;`
`const int MaxVnum = 100; //顶点最大个数`
`int visist[MaxVnum] = {0};`
`typedef char VexType;//顶点的数据类型`
`typedef int EdgeType;//顶点的边权;若不带权值则用0和1表示其连通性`
`typedef struct {`
`VexType Vex[MaxVnum];//顶点信息`
`EdgeType Edge[MaxVnum][MaxVnum];//邻接矩阵`
`int vexnum, edgenum; //顶点数和边数`
`} Gragh;`
`void dfs(Gragh G, int v) { //假设已经创建了一个图的邻接矩阵G ,v是开始访问的顶点`
`int w;//w是邻接点`
`cout << G.Vex[v] << endl;`
`for (w = 0; w < G.vexnum; w++) { //遍历所有顶点`
`visist[v] = 1; //访问过了标记为真`
`if (G.Edge[v][w] && !visist[w]) dfs(G, w); //邻接且未被访问过,则递归访问其邻接点`
`}`
`}`
`void bfs(Gragh G, int v) {`
`int u, w;`
`queue<int>Q;`
`visist[v] = 1;`
`cout << G.Vex[v] << endl;`
`Q.push(v);//顶点v入队`
`while (Q.empty() == 0) { //如果队列不空`
`u = Q.front(); //队列的头元素出列赋给u`
`Q.pop();//队列头元素出队`
`for (w = 0; w < G.vexnum; w++) {`
`if (G.Edge[u][w] && !visist[w]) { //u,w邻接且w未被访问过`
`cout << G.Vex[w] << endl;`
`visist[w] = 1;`
`Q.push(w);//领接点加入队列`
`}`
`}`
`}`
`}`
`int main(){`
`Gragh g;`
`cout << "请输入此图的顶点数和边数" << endl;`
`cin >> g.vexnum >> g.edgenum;`
`getchar();`
`cout << "请输入此图的顶点" << endl;`
`for(int i = 0; i < g.vexnum; i++){`
`cin >> g.Vex[i];`
`}`
`cout << "请输入关于此图的边信息" << endl;`
`for(int i = 0; i < g.vexnum; i++){`
`for(int j = 0; j < g.vexnum; j++){`
`cin >> g.Edge[i][j];`
`}`
`}`
``
`// dfs(g,0);`
``
`// bfs(g,0);`
``
return 0;
`}``
#### 5.dfs/bfs
这个上次在,最短路径问题以及提及,在此不做多余赘述
板子:Prim算法(选点)和Kruskal算法(选边)
#include<stdio.h>
int map[5010];
int dis[5010];
int max=1999999;
int maps[5010][5010];
long long sum;
long long min;
int hh=0;
int next;
void beg(int a){
for(int hg=1;hg<=a;hg++)
{
for(int jk=1;jk<=a;jk++){
maps[hg][jk]=max;
maps[jk][hg]=max;
}
}
}
int main(){
int n,m;//点和边的数量
int a,b,c;//输入的数据
int jl;//就是一个用于循环的变量
scanf("%d%d",&n,&m);
beg(n);
for(int h=1;h<=m;h++){
scanf("%d%d%d",&a,&b,&c);
if(c<maps[a][b]){
maps[a][b]=c;
maps[b][a]=c;
}
}
for(int lp=1;lp<=n;lp++)
dis[lp]=maps[1][lp];//1默认为起点
map[1]=1;//起点为用过的
for(int i=1;i<n;i++){
min=max;
for(jl=1;jl<=n;jl++)
{
if(dis[jl]<min&&map[jl]==0)
{
min=dis[jl];
next=jl;
}
}
if(min==max){
printf("orz");
hh++;
break;
}
map[next]=1;
sum=sum+dis[next];//加上最短的路程
for(int hj=1;hj<=n;hj++)//对每个点到树的最小的距离进行更新
{
if(map[hj]==0&&maps[next][hj]<dis[hj]&&i<n-1)//i<n-1最后一次就不用更新了
{
dis[hj]=maps[next][hj];
}
}
}
if(hh==0)
printf("%lld",sum);
return 0;
}
#include<stdio.h>
int map[1001000]={0};//为0的就是T标号
long long dis[1001000];
long long max=2147483647;
void bg(int u){//赋初值函数
for(int h=1;h<=u;h++){
dis[h]=max;
}
}
struct node{
int to;
int chang;
int next;//上一条是边数据的下标
}nodes[1001000];
long long min(long long jl,long long jk){
if(jl<jk){
return jl;
}
else{
return jk;
}
}
int cin;//表示结构体数组的下标(就是一个一个数组的用无实际的意义)
int first[1001000]={0};//保存头的数组(下标就是头!)值的含义是在结构体中保存的边对应的下标
//值表示以这个头开始的最后一条线
void add(int a,int b,int c){
cin++;//用新的来存
nodes[cin].to=b;//to表示到那去(边的终点)
nodes[cin].chang=c;//chang表示边的长度
nodes[cin].next=first[a];//next表示找到上一以a为起点的上以条边的数组的下标
first[a]=cin;//现在a到b变成了以a为起点的最后一条边就改一下first[a]的值(这个值是储存这条边的数组的下标
}
int main()
{
int n,m,k;
int jj,kk,ll;
scanf("%d%d%d",&n,&m,&k);
bg(n);
for(int j=1;j<=m;j++){
scanf("%d%d%d",&jj,&kk,&ll);
add(jj,kk,ll);
}
dis[k]=0;
int pos=k;
while(map[pos]==0){
map[pos]=1;
for(int i=first[pos];i!=0;i=nodes[i].next){
if(map[nodes[i].to]==0){
dis[nodes[i].to]=min(dis[nodes[i].to],dis[pos]+nodes[i].chang);
}
}
long long mine=max;
for(int y=1;y<=n;y++){
if(dis[y]<mine&&map[y]==0)
{
mine=dis[y];
pos=y;
}
}
}
for(int y=1;y<=n;y++){
printf("%lld ",dis[y]);
}
return 0;
}