数据结构之图

1.定义:图是一种非线性数据结构,形式化描述为

Graph = {V,R};

其中,V={Vi|Vi属于datatype,i=0,1,2,……n-1}是图中元素Vi(称为顶点vertex)的集合,当n=0时,V为空集。

R = {<Vi,Vj> | Vi,Vj,属于V,且Vi,Vj之间存在路径,0<=i,j<=n-1}是顶点之间的关系集,<Vi,Vj>为顶点Vi,

Vj之间是否存在路径的判定条件,即若Vi,Vj之间的路径存在,则关系<Vi,Vj>属于R。

 

有向图:弧 无向图:边

 

网:若在图的关系<Vi,Vj>或者是<Vj,Vi>上面附加一个值W,那么w就为弧或边的权。带权的图就称为网{权w的具体含义视图在不同领域

的应用而定。如顶点表示城市,权表示两个城市之间的距离}

 

顶点的度:顶点的边或者是弧的条数

 

图的表示方式

数组表示法(邻接矩阵),邻接表,十字链表 邻接多重表

1.数组表示法

G=(V,R)。

可以用两个数组来存储图G,一个一维数组存储顶点集V,一个二维数组存储G中顶点间的关系R,该二维数组就是所谓的邻接矩阵

typedef char Vtype;//顶点的数据类型

typedef int Adjtype;//邻接矩阵的类型,其值就是边的权值的类型

typedef struct

{

Vtype V[MAX];//顶点的集合

Adjtype A[MAX][MAX];//边的集合,二维数组表示

int Vexnum;//顶点的个数

int Adjnum;//边的个数

}mgraph;

 

2.邻接表

将图中的每一个顶点V和由V出发的弧或边构成一个单链表,节点中保留他所指向的节点的下标,邻接表是图的一种链式存储结构

struct adj

{

int terminder;//该弧所指向的顶点的位置

char name;//指向的顶点的名字

struct adj *next;//指向下一条依附该顶点的弧的指针

}

struct Vertex

{

Vtype data;顶点信息

struct adj *first;指向第一条依附该顶点的弧的指针。

}

struct Vertex graph[MAX];

 

图的遍历

图的遍历是树的遍历的推广,是按照某种规则或者是次序访问图中各顶点一次且仅一次的操作,亦是将网状结构按照某种规则线性化的过程。对图的遍历常用的有“深度优先搜索”和“广度优先搜索”,二者是人工智能(AI)的基础.

1.深度优先搜索(DFS:Depth First Search)

若初始化时,图中的各顶点均未被访问,从图中某个节点出发,然后又按照深度优先搜索的方式搜索下去,直到能够访问的顶点

都能够访问完毕为止。

 

2.广度优先搜索(BFS:BreadthFirst Search)

类似于树的按层次遍历,初始时,图中各顶点均未被访问,从图中某顶点(v0)出发,访问之,并以此访问其余的各邻接点。然后,

分别从这些被访问的顶点出发,仍按照广度优先搜索的顺序访问,直到被访问的点都访问完毕为止。

下面的矩形图为邻接矩阵:

 

 

深度优先搜索:

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#define MAX 100

#define VERYBIG 65535

typedef char Vtype;//顶点的数据类型

typedef int Adjtype;//邻接矩阵的类型,其值就是边的权值的类型

typedef struct

{

Vtype V[MAX];//顶点的集合

Adjtype A[MAX][MAX];//边的集合,二维数组表示

int Vexnum;//顶点的个数

int Adjnum;//边的个数

}Graph;

 

/*

从g的顶点集合中寻找ch,如果找到了返回ch的下标,没有找到返回-1;

*/

int my_index(Graph *g,char ch)

{

int i;

for(i = 0;i < g->Vexnum;i++)

{

if(g->V[i] == ch)

return i;

}

return -1;

}

Graph *CreateGraph()

{

int i,j;

//先新建一张空图

Graph *g = (Graph *)malloc(sizeof(*g));

g->Vexnum = g->Adjnum = 0;

 

//确定顶点个数

scanf("%d",&g->Vexnum);

getchar(); //接收回车符

 

//输入每一个顶点的名称

for(i = 0;i < g->Vexnum;i++)

scanf("%c",&g->V[i]);

 

//把邻接矩阵全部置为无穷大

for(i = 0;i < g->Vexnum;i++)

{

for(j = 0;j < g->Vexnum;j++)

{

g->A[i][j] = VERYBIG;

}

}

 

//循环从键盘输入起点,终点,权值

Vtype from,to;

Adjtype weight;

while(1)

{

getchar();

scanf("%c%c%d",&from,&to,&weight);

if(from == '#')

break;

 

i = my_index(g,from);

j = my_index(g,to);

 

if(i != -1 && j != -1)

{

g->A[i][j] = weight;

g->Adjnum++;

}

}

return g;

}

//1.深度优先搜索(DFS:Depth First Search)

//找第row行的第一个邻接点

int First(Graph *g, int row)

{

int i;

for(i = 0;i < g->Vexnum;i++)

{

if(g->A[row][i] != VERYBIG)

{

return i;

}

}

return -1;

}

//找第row行的column+1列开始的下一个邻接点

int Next(Graph *g,int row,int column)

{

int i;

for(i = column+1;i < g->Vexnum;i++)

{

if(g->A[row][i] != VERYBIG)

{

return i;

}

}

return -1;

}

 

int visited[MAX];//0表示未被访问过,1表示已经被访问过

void DFS(Graph *g,int n)

{

visited[n] = 1;

printf("%c",g->V[n]);

int i;

for(i = First(g,n); i != -1;i = Next(g,n,i))

{

if(visited[i] == 0)

{

DFS(g,i);

}

}

}

void DFSVisit(Graph *g)

{

int i;

//将所有顶点都置为未被访问过

for(i = 0;i < g->Vexnum;i++)

{

visited[i] = 0;

}

 

//循环是为了防止孤立顶点未被访问

for(i = 0;i < g->Vexnum;i++)

{

if(visited[i] == 0)

{

DFS(g,i);//访问i节点

}

}

printf("\n");

}

//注意遍历部分打印出来的是abecfdg,打印部分打印出来的是图的那张表,是分开的两步;

void PrintGraph(Graph *g)

{

int i,j;

for(i = 0;i <g->Vexnum;i++)

printf("%c\t",g->V[i]);

printf("\n");

 

for(i = 0;i < g->Vexnum;i++)

{

for(j = 0;j < g->Vexnum;j++)

{

if(g->A[i][j] == VERYBIG)

{

printf("%c\t",42); //打印无穷大,用*代替

}

else

{

printf("%d\t",g->A[i][j]);

}

}

printf("\n");

}

}

 

最短路径问题

解决带权有向图中两个顶点间最短路径问题。

有两个经典算法;

Dijkstra(迪杰斯特拉)算法,Floyd(弗洛依德)算法

Dijkstra(迪杰斯特拉)算法;是解决从网络中任意顶点(源点)出发,求它到其他顶点(终点)的最短路径

Dijkstra(迪杰斯特拉)算法需要用到三个辅助变量

1)向量s[n],

s[i] = 1 表示从源点V到该点Vi的最短路径已经求出来了,

s[0] = 0 表示从源点V到该点Vi的最短路径没有求出来,

初始时,s[v] = 1,其他s[i] = 0;

2)向量dist[n]

dist[i]存放从源点v到Vi这个点(当前的)最短路径的长度

初始化时,

dist[i] = <V,Vi>的权w,当v可以到达vi时;

dist[i] = 无穷大,当V不可以直接到Vi;

 

3)向量path[n]

path[i]存放从源点v到v[i]的(当前)最短路径

 

算法思路:

1)显然,从源点V到其他各顶点的第一条最短路径长度dist[u];

dist[u] = min{dist[w]|w =0,1,2,3……且S[w] = 0};

表示在所有未求出的当前路径中找出一条最短的,其长度作为当前求出的最短路径。

2)对s[w] = 0 的w,如果dist[u] + <u,w>  < dist[w];

那么dist[w]被更新为 dist[w] = dist[u] + <u,w>;

 

然后重复步骤1,2,直到所有的s[n]都等于1为止

 

 

 

 

//求最短路径部分;

int s[MAX];

int s[MAX];

int dist[MAX];

char path[MAX][MAX];

void Dijkstra(Graph *g, int v)

{

int i,j;

for(i = 0;i < g->Vexnum;i++)

{

s[i] = 0;

dist[i] = g->A[v][i];

path[i][0] = g->V[v];

}

s[v] = 1;

 

int min;

int index;

for(i = 0;i < g->Vexnum-1;i++)

{

min = VERYBIG + 1;

index = -1;

for(j = 0;j < g->Vexnum;j++)

{

if(s[j] == 0 && dist[j] < min)

{

min = dist[j];

index = j;

}

}

//printf("%d:%d\n",i,min);

s[index] = 1;

path[index][strlen(path[index])] = g->V[index];

 

for(j = 0;j < g->Vexnum;j++)

{

if(dist[index] + g->A[index][j] < dist[j])

{

if(s[j] == 0)

{

dist[j] = min + g->A[index][j];

strcpy(path[j],path[index]);

}

}

}

}

 

path[v][1] = g->V[v];

for(i = 0;i < g->Vexnum;i++)

{

for(j = 0;j < strlen(path[i]);j++)

{

printf("%c->",path[i][j]);

}

printf("\b\b:%d\n",dist[i]);

}

 

}

int main(int argc,char *argv[])

{

Graph *g = CreateGraph();

PrintGraph(g);

DFSVisit(g);

Dijkstra(g,0);

return 0;

}

 

 

 

 

 

图的乌帮图输入输出结果:

7

abcdefg

ab15

ac2

ad12

be6

cf4

dg3

fg10

fd5

gb4

###

a b c d e f g

* 15 2 12 * * *

* * * * 6 * *

* * * * * 4 *

* * * * * * 3

* * * * * * *

* * * 5 * * 10

* 4 * * * * *

abecfdg

a->a:65535

a->b:15

a->c:2

a->c->f->d:11

a->b->e:21

a->c->f:6

a->c->f->d->g:14(这个输出结果是没有增加那两条边的结果)

7

abcdefg

ab15

ac2

ad12

be6

cf4

dg3

fg10

fd5

gb4

ce8

ef9

###

a b c d e f g

* 15 2 12 * * *

* * * * 6 * *

* * * * 8 4 *

* * * * * * 3

* * * * * 9 *

* * * 5 * * 10

* 4 * * * * *

abefdgc

a->a:65535

a->b:15

a->c:2

a->c->f->d:11

a->c->e:10

a->c->f:6

a->c->f->d->g:14(增加了两条边长的输入输出结果)

 

 

 

 

图的第二种遍历(广度优先搜素,不加ce、ef的值)

#include<stdio.h>

#include<stdlib.h>

 

#define MAX 100

#define VERYBIG 65535

typedef char Vtype;//顶点的数据类型

typedef int Adjtype;//邻接矩阵的类型,其值就是边的权值的类型

typedef struct

{

Vtype V[MAX];//顶点的集合

Adjtype A[MAX][MAX];//边的集合,二维数组表示

int Vexnum;//顶点的个数

int Adjnum;//边的个数

}Graph;

typedef struct node

{

int num;

struct node *next;

}Node;

 

typedef struct list

{

Node *first;

Node *last;

int count;

}List;

void Ruidui(List *l,int ch)

{

if(l == NULL)

return ;

Node *p = (Node *)malloc(sizeof(Node));

p->num = ch;

p->next = NULL;

if(l->first == NULL)

{

l->first = l->last = p;

}

else

{

l->last->next = p;

l->last = p;

}

l->count++;

}

Node *Chudui(List *l)

{

if(l == NULL)

return NULL;

Node *p = l->first;

if(l->first == l->last)

{

l->first = l->last = NULL;

}

else

{

l->first = p->next;

p->next = NULL;

}

l->count--;

return p;

}

List * Chuangjian(void)

{

List *l = (List*)malloc(sizeof(List));

l->first = l->last = NULL;

l->count = 0;

return l;

}

int visited[MAX];

int my_index(Graph *g,char ch)

{

int i;

for(i = 0;i < g->Vexnum;i++)

{

if(g->V[i] == ch)

return i;

}

return -1;

}

int First(Graph *g, int row)

{

int i;

for(i = 0;i < g->Vexnum;i++)

{

if(g->A[row][i] != VERYBIG)

{

return i;

}

}

return -1;

}

int Next(Graph *g,int row,int column)

{

int i;

for(i = column+1;i < g->Vexnum;i++)

{

if(g->A[row][i] != VERYBIG)

{

return i;

}

}

return -1;

}

void PrintGraph(Graph *g)

{

int i,j;

for(i = 0;i <g->Vexnum;i++)

printf("%c\t",g->V[i]);

printf("\n");

 

for(i = 0;i < g->Vexnum;i++)

{

for(j = 0;j < g->Vexnum;j++)

{

if(g->A[i][j] == VERYBIG)

{

printf("%c\t",42);

}

else

{

printf("%d\t",g->A[i][j]);

}

}

printf("\n");

}

}

Graph *CreateGraph()

{

int i,j;

Graph *g = (Graph *)malloc(sizeof(*g));

g->Vexnum = g->Adjnum = 0;

 

scanf("%d",&g->Vexnum);

getchar();

 

for(i = 0;i < g->Vexnum;i++)

scanf("%c",&g->V[i]);

for(i = 0;i < g->Vexnum;i++)

{

for(j = 0;j < g->Vexnum;j++)

{

g->A[i][j] = VERYBIG;

}

}

Vtype from,to;

Adjtype weight;

while(1)

{

getchar();

scanf("%c%c%d",&from,&to,&weight);

if(from == '#')

break;

 

i = my_index(g,from);

j = my_index(g,to);

 

if(i != -1 && j != -1)

{

g->A[i][j] = weight;

g->Adjnum++;

}

}

return g;

}

 

 

int k=1,x=-1;

void DFS(List* l,Graph *g,int n)

{

int i;

for(i = First(g,n); i != -1;i = Next(g,n,i))

{

if(visited[i] == 0)

{

while(k>0)

{

x=i;

k=0;

}

visited[i]=1;

Ruidui(l,i);

}

}

}

void Jincheng(List *l,Graph *g)

{

Node *p=NULL;

int i;

for(i=0;i<g->Vexnum;i++)

{

visited[i]=0;

}

Ruidui(l,0);

visited[0]=1;

while(l->count > 0)

{

p=Chudui(l);

i=p->num;

if(x == i)

{

printf("\n");

k=1;

}

printf("%c\t",g->V[i]);

DFS(l,g,i);

}

for(i = 0;i < g->Vexnum;i++)

{

if(visited[i] == 0)

{

printf("%c\t",g->V[i]);

}

}

printf("\n");

}

int main(int argc,char *argv[])

{

List *l = Chuangjian();

Graph *g = CreateGraph();

PrintGraph(g);

Jincheng(l,g);

return 0;

}

 

输入输出:

7

abcdefg

ab15

ac2

ad12

be6

cf4

dg3

fg10

fd5

gb4

###

a b c d e f g

* 15 2 12 * * *

* * * * 6 * *

* * * * * 4 *

* * * * * * 3

* * * * * * *

* * * 5 * * 10

* 4 * * * * *

a

b c d

e f g数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
>