图
一.概念
无向图:G = (V , E)(Vertex顶点,Edge边)
无向完全图:任意2个节点相连 Cn2条 = n(n-1) / 2
连通图:任意两个节点之间有路径课通。
极大连通子图:G` = (V` , E`) V` € V , E` € E, G` € G
极大连通分量:几个极大连通子图
度:最大为n-1
边:(v1,v5) (v5,v1) 依附于顶点v1,v5 是同一个意思
有向图:G = <V , E> <顶点,弧> 弧包括弧头和弧尾
有向完全图;任意两个顶点之间都要两条弧 An2 n(n-1)
度:入度(被指向) , 出度(指出去) 二者相加为度
n个顶点的无向图 ,边数多少可能为连通图, n-1
n个顶点的无向图 ,边数多少一定为连通图, (n-1)(n-2)/2+1
二.图的创建:
1.邻接矩阵 无向图关于对角线对称
2.邻接接表
边多用矩阵,边少用邻接链表
矩阵唯一,链表不唯一(无序)
代码:对于二维数组的申请,二维数组本质上是以为数组,所以我们申请一个长度为v*v的以为数组,通过下标来定位行和列。
或者申请一个装着指针,长度为v的一维数组,再给每一个指针申请长度为v的int型数组。这样做可以实现二维数组的功能。但是,每一行的地址不是连续的,不是真正意义上的二维数组。
typedef struct node { int nVertex; int nEdge; int *p; }Graph; Graph* create() { /*int **arr =(int**) malloc(sizeof(int*)*n); for(int i = 0 ; i < n; i++) arr[i] = (int*)malloc(sizeof(int)*n); for(int i = 0; i < n; i++) for(int j = 0; j < n; j++) arr[i][j] = 0;*/ Graph* pGraph = (Graph*)malloc(sizeof(Graph)); int v,e; printf("请输入图的顶点和边数\n"); scanf("%d%d",&v,&e); pGraph->nVertex = v; pGraph->nEdge = e; pGraph->p =(int*) malloc(sizeof(int)*v*v); memset(pGraph->p,0,sizeof(int)*v*v); int v1,v2; for(int i = 0; i < e;i++) { printf("请输入两个顶点确定一条边\n"); scanf("%d%d",&v1,&v2); if(v1>=1 && v2>=1 && v1<=v && v2<=v && v1!=v2 && pGraph->p[(v1-1)*v+v2-1] == 0) { pGraph->p[(v1-1)*v+(v2-1)] = 1; pGraph->p[(v2-1)*v+(v1-1)] = 1; } else { i--; } } return pGraph; }
三.图的遍历:
深度优先遍历DFS:
测试数据:6 9 1 2 ; 1 3 ;1 4;2 3; 2 4;2 5;3 4;3 6;4 5;
则矩阵为:
0 1 1 1 0 0
1 0 1 1 1 0
1 1 0 1 0 1
1 1 1 0 1 0
0 1 0 1 0 0
0 0 1 0 0 0
思路:假设从顶点1开始,
第一行:v2为1且没有标记过,则跳转至第二行。
第二行:v1标记过,v3为1且没被标记过,则跳至第三行。
第三行:v1,v2为1但被标记过,v4为1且没被标记过,则跳转至第4行。
第四行:v1,v2,v3为1但被标记过,v5为1且没被标记过,则跳转至第5行。
第五行:v2,v4为1但被标记过。查看第五行是由第四行跳转来的,则返回原来位置继续查找。
第四行:第四行没有能跳转的节点,则返回地三行的原来位置。
第三行:v6为1且没被标记过,则跳转至第6行。
第六行:v3为1但被标记过。则返回第三行的原来位置。
第三行:没有节点跳转。则返回第二行原来位置。
第二行:没有v4,v5为1且被标记过。则返回第一行原来的位置。
第一行:v3,v4为1但被标记过。结束。
所以要申请额外的标记数组:
void DFS(Graph* pGraph,int n,int* arr) { printf("%d ",n); arr[n] = 1; for(int i = 1; i <= pGraph->nVertex; i++) { if(pGraph->p[(n-1)*pGraph->nVertex+i-1] == 1 && arr[i] == 0) { DFS(pGraph,i,arr); } } } void MyDFS(Graph* pGraph,int n) { int* arr = (int*)malloc(sizeof(int) * (pGraph->nVertex)); memset(arr,0,sizeof(arr)); DFS(pGraph,n,arr); }
广度优先遍历BFS:
思路:与树的BFS十分相似,需要借助辅助队列,
假设从第一个节点开始,则将第一行为1的节点标记后,压入队列。然后再将队列中的第一个元素所在行,为1的节点标记后压入队列。
void BFS(Graph* pGraph,int begin) { Queue* myqueue = Init(); int* arr = (int*) malloc(sizeof(int)*pGraph->nVertex); memset(arr,0,sizeof(arr)*pGraph->nVertex); arr[begin-1] =1; Push(myqueue,begin); while(!IsEmpty(myqueue)) { begin = Pop(myqueue); printf("%d ",begin); for(int i = 0; i < pGraph->nVertex; i++) { if(pGraph->p[(begin-1)*pGraph->nVertex + i] == 1 && arr[i] == 0 ) { arr[i] = 1; Push(myqueue,i+1); } } } }