数据结构–图
图是研究数据元素之间的多对多的关系。在这种结构中,任意两个元素之间可能存在关系。即结点之间的关系可以是任意的,图中任意元素之间都可能相关。图的应用极为广泛,已渗入到诸如语言学、逻辑学、物理、化学、电讯、计算机科学以及数学的其它分支。
图的定义
一个图(G)定义为一个偶对(V,E) ,记为G=(V,E) 。其中: V是顶(Vertex)的非空有限集合,记为V(G);E是无序集V&V的一个子集,记为E(G) ,其元素是图的弧(Arc)。将顶点集合为空的图称为空图。其形式化定义为:G=(V ,E),V={v|v in data object},
E={(v,w)| v,w in V and p(v,w)},P(v,w)表示从顶点v到顶点w有一条直接通路。
图的结构
图的存储结构比较复杂,其复杂性主要表现在:
任意顶点之间可能存在联系,无法以数据元素在存储区中的物理位置来表示元素之间的关系。
图中顶点的度不一样,有的可能相差很大,若按度数最大的顶点设计结构,则会浪费很多存储单元,反之按每个顶点自己的度设计不同的结构,又会影响操作。
图的常用的存储结构有:邻接矩阵、邻接链表、十字链表、邻接多重表和边表。
在图的邻接链表表示中,所有顶点结点用一个向量 以顺序结构形式存储,可以随机访问任意顶点的链表,该向量称为表头向量,向量的下标指示顶点的序号。
用邻接链表存储图时,对无向图,其邻接链表是唯一的,对有向图,其邻接链表有两种形式。
图的遍历
深度优先搜索(DFS):深度优先搜索(Depth First Search–DFS)遍历类似树的先序遍历,是树的先序遍历的推广。
设初始状态时图中的所有顶点未被访问,则:
⑴ :从图中某个顶点vi出发,访问vi;然后找到vi的一个邻接顶点vi1 ;
⑵:从vi1出发,深度优先搜索访问和vi1相邻接且未被访问的所有顶点;
⑶:转⑴ ,直到和vi相邻接的所有顶点都被访问为止
⑷ :继续选取图中未被访问顶点vj作为起始顶点,转(1),直到图中所有顶点都被访问为止。
广度优先搜索(BFS):广度优先搜索(Breadth First Search–BFS)遍历类似树的按层次遍历的过程。
设初始状态时图中的所有顶点未被访问,则:
⑴ :从图中某个顶点vi出发,访问vi;
⑵:访问vi的所有相邻接且未被访问的所有顶点vi1,vi2,…,vim;
⑶:以vi1,vi2, …,vim的次序,以vij(1≦j≦m)依此作为vi ,转⑴;
⑷ :继续选取图中未被访问顶点vk作为起始顶点,转⑴,直到图中所有顶点都被访问为止。
代码实现–C语言实现
Graph.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "Queue.h"
#define MAX_VERTEX_NUM 10
#define INFINITY 32768
typedef enum{DG,DN,UDG,UDN} GraphKind;
#define ERROR 0
#define TRUE 1
typedef int status;
typedef char ElemType;
typedef struct MNode{
ElemType vertex[MAX_VERTEX_NUM]; /*顶点*/
int Arc[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; /*弧*/
int vexnum,arcnum; /*弧个数,顶点个数*/
GraphKind kind;
}GraphNode,*Graph;
int IsRead[MAX_VERTEX_NUM]; //标记顶点是否被遍历过
/*初始化*/
status Init(Graph *G){
(*G) = (GraphNode *)malloc(sizeof(GraphNode));
(*G)->vexnum = 0;
(*G)->arcnum = 0;
if((*G))return TRUE;
else{
printf("初始化出错...\n");
}
return ERROR;
}
/*查找位置*/
void FindPos(Graph G,char a,char b,int *pos1,int *pos2){
int i = 0;
*pos1 = -1; *pos2 = -1; //初值
for(i = 0; i<G->vexnum;i++){
if(G->vertex[i]==a){
(*pos1) = i;
continue;
}else if(G->vertex[i] == b){
(*pos2) = i;
continue;
}
}
}
/*建立图*/
void BuildGraph(Graph G){
int choice = 0,num = 0,pos1,pos2,i,j,weight;
char a,b,ch;
printf("请选择建立的图的类型:1:有向图,2:有向网,3:无向图,无向网:\n");
scanf("%d",&choice);
getchar();
printf("\n"); //下一行
if(choice == 1){ //有向图
for(i = 0;i<MAX_VERTEX_NUM;i++){ //初始化弧
for(j = 0;j<MAX_VERTEX_NUM;j++){
G->Arc[i][j] = 0;
}
}
G->kind = DG; //设置图的类型
printf("请输入顶点(不超过10个,以#结束):\n");
scanf("%c",&ch);
while(ch!='#' && num <10){
G->vertex[num] = ch;
scanf("%c",&ch);
num++;
}
G->vexnum = num; //顶点个数
getchar(); //清除键盘缓冲区
printf("请输入对应的弧以a->b格式输入(以#->#结束):\n");
scanf("%c->%c",&a,&b);
while(a!='#' && b!='#'){
printf("%c,%c",a,b);
FindPos(G,a,b,&pos1,&pos2);
printf("位置a:%d,位置b:%d\n",pos1,pos2);
if(pos1!= -1 && pos2!= -1){ //忽略不存在的顶点
G->Arc[pos1][pos2] = 1;
G->arcnum++;
}
scanf("%c->%c",&a,&b);
}
getchar(); //清空
}
else if(choice==2){ //有向网
num = 0; //个数初始化
for(i = 0;i < MAX_VERTEX_NUM; i++){ //初始化弧
for(j = 0;j<MAX_VERTEX_NUM; j++){
G->Arc[i][j] = INFINITY;
}
}
G->kind = DN; //设置图的类型
printf("请输入顶点(不超过10个,以#结束):\n");
scanf("%c",&ch);
while(ch!='#' && num <10){
G->vertex[num] = ch;
scanf("%c",&ch);
num++;
}
G->vexnum = num; //顶点个数
getchar(); //清除键盘缓冲区
printf("请输入对应的弧以a->b:weight格式输入(以#->#:0结束):\n");
scanf("%c->%c:%d",&a,&b,&weight);
while(a!='#' && b!='#'){
printf("%c,%c",a,b);
FindPos(G,a,b,&pos1,&pos2);
printf("位置a:%d,位置b:%d\n",pos1,pos2);
if(pos1!= -1 && pos2!= -1){ //忽略不存在的顶点
G->Arc[pos1][pos2] = weight;
G->arcnum++;
}
scanf("%c->%c:%d",&a,&b,&weight);
}
getchar(); //清空
}
else if(choice == 3){//无向图
num = 0;
for(i = 0;i<MAX_VERTEX_NUM;i++){ //初始化弧
for(j = 0;j<MAX_VERTEX_NUM;j++){
G->Arc[i][j] = 0;
}
}
G->kind = UDG; //设置图的类型
printf("请输入顶点(不超过10个,以#结束):\n");
scanf("%c",&ch);
while(ch!='#' && num <10){
G->vertex[num] = ch;
scanf("%c",&ch);
num++;
}
G->vexnum = num; //顶点个数
getchar(); //清除键盘缓冲区
printf("请输入对应的弧以a-b格式输入(以#-#结束):\n");
scanf("%c-%c",&a,&b);
while(a!='#' && b!='#'){
printf("%c,%c",a,b);
FindPos(G,a,b,&pos1,&pos2);
printf("位置a:%d,位置b:%d\n",pos1,pos2);
if(pos1!= -1 && pos2!= -1){ //忽略不存在的顶点
G->Arc[pos1][pos2] = 1;
G->Arc[pos2][pos1] = 1;
G->arcnum++;
}
scanf("%c-%c",&a,&b);
}
getchar(); //清空
}
else if(choice == 4){ //无向网
num = 0; //个数初始化
for(i = 0;i < MAX_VERTEX_NUM; i++){ //初始化弧
for(j = 0;j<MAX_VERTEX_NUM; j++){
G->Arc[i][j] = INFINITY;
}
}
G->kind = DN; //设置图的类型
printf("请输入顶点(不超过10个,以#结束):\n");
scanf("%c",&ch);
while(ch!='#' && num <10){
G->vertex[num] = ch;
scanf("%c",&ch);
num++;
}
G->vexnum = num; //顶点个数
getchar(); //清除键盘缓冲区
printf("请输入对应的弧以a-b:weight格式输入(以#-#:0结束):\n");
scanf("%c->%c:%d",&a,&b,&weight);
while(a!='#' && b!='#'){
printf("%c,%c",a,b);
FindPos(G,a,b,&pos1,&pos2);
printf("位置a:%d,位置b:%d\n",pos1,pos2);
if(pos1!= -1 && pos2!= -1){ //忽略不存在的顶点
G->Arc[pos1][pos2] = weight;
G->Arc[pos2][pos1] = weight;
G->arcnum++;
}
scanf("%c-%c:%d",&a,&b,&weight);
}
getchar(); //清空
}
else { //非法输入的选择
printf("输入非法,请输入正确的数字!!\n");
return;
}
}
void DepthFS(Graph G,int pos){ /*深度优先搜索for图*/
int i = 0,j = 0;
if(IsRead[pos] == 0){
IsRead[pos] = 1; //从第一个开始
printf("遍历顶点%c\n",G->vertex[pos]);
}
for(i = 0;i<G->vexnum;i++){
if(G->Arc[pos][i] == 1 && IsRead[i] ==0){ //存在弧
DepthFS(G,i);
}
}
}
void DepthFS1(Graph G,int pos){ /*深度优先搜索for网*/
int i = 0,j = 0;
if(IsRead[pos] == 0){
IsRead[pos] = 1; //从第一个开始
printf("遍历顶点%c\n",G->vertex[pos]);
}
for(i = 0;i<G->vexnum;i++){
if(G->Arc[pos][i] !=INFINITY && IsRead[i] ==0){ //存在弧且未被遍历
DepthFS1(G,i);
}
}
}
/*深度优先搜索*/
void DFS(Graph G){
if(G->kind == DG || G->kind == UDG){ //图
DepthFS(G,0);
}else if(G->kind == DN || G->kind == UDN){ //网
DepthFS1(G,0);
}
}
void BFS1(Graph G,int pos){ //广度优先搜索for图
int i = 0,temp;
Queue Q;
InitQueue(&Q);
for(i = 0; i <G->vexnum;i++){ //清零
IsRead[i] = 0;
}
if(IsRead[pos] ==0){
IsRead[pos] = 1;
printf("遍历顶点:%c\n",G->vertex[pos]);
}
EnterQueue(Q,pos);
while(!isEmpty(Q)){//当队列不为空
OutQueue(Q,&temp);
for(i = 0; i< G->vexnum;i++){
if(G->Arc[temp][i] == 1 && IsRead[i] == 0){
IsRead[i] = 1;
printf("遍历顶点:%c\n",G->vertex[i]);
EnterQueue(Q,i);
}
}
}
free(Q);
}
void BFS2(Graph G,int pos){ //广度优先搜索for图
int i = 0,temp;
Queue Q;
InitQueue(&Q);
for(i = 0; i <G->vexnum;i++){ //清零
IsRead[i] = 0;
}
if(IsRead[pos] ==0){
IsRead[pos] = 1;
printf("遍历顶点:%c\n",G->vertex[pos]);
}
EnterQueue(Q,pos);
while(!isEmpty(Q)){//当队列不为空
OutQueue(Q,&temp);
for(i = 0; i< G->vexnum;i++){
if(G->Arc[temp][i] != INFINITY && IsRead[i] == 0){
IsRead[i] = 1;
printf("遍历顶点:%c\n",G->vertex[i]);
EnterQueue(Q,i);
}
}
}
free(Q);
}
void BFS(Graph G){
if(G->kind == DG || G->kind == UDG){ //图
BFS1(G,0);
}else if(G->kind == DN || G->kind == UDN){ //网
BFS2(G,0);
}
}
void main(){
int i = 0;
Graph G = NULL;
Init(&G);
BuildGraph(G);
for(i = 0; i<MAX_VERTEX_NUM; i++){
IsRead[i] = 0; //未被遍历
}
printf("\n深度优先搜索:\n");
DFS(G,0);
printf("\n广度优先搜索:\n");
BFS(G,0);
}
Queue.h
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define ERROR 0
#define MAX_SIZE 20
typedef int status;
/*定义队列*/
typedef struct QNode{
int Qarr[MAX_SIZE];
int tail,head;
int size;
}QNode,*Queue;
void InitQueue(Queue *Q){
(*Q) = (QNode *)malloc(sizeof(QNode));
if(*Q){
(*Q)->size = 0;
(*Q)->head = 0;
(*Q)->tail = 0;
}
}
/*入队列*/
void EnterQueue(Queue Q, int data){
if((Q->tail +1) % MAX_SIZE == Q->head){
printf("队列已经满!!\n");
return;
}
Q->Qarr[Q->tail] = data;
Q->size++;
Q->tail = (Q->tail +1) % MAX_SIZE;
return;
}
void OutQueue(Queue Q, int *data){
if(Q->head == Q->tail){
printf("队列为空!!\n");
return;
}
(*data) = Q->Qarr[Q->head];
Q->size--;
Q->head = (Q->head +1) % MAX_SIZE;
return;
}
int isEmpty(Queue Q){
if(Q->head == Q->tail)return 1;
return 0;
}