嵌入式学习一阶段——C语言:数据结构(7)

图的基本概念

定义:图(graph)是一种非线性结构,形式化的描述:
    graph  = (V,R)
其中,V = {vi | vi 属于某种数据类型 i=0,1,2,3,4,5.....}是图中的元素,vi就是图中的顶点,V就是顶点一个集合!!!
    就是图中的顶点,V就是顶点的集合,n代表 图中的顶点个数
        如果n = 0 , 就说明是一个空集
其中,R代表V集合中任意两个顶点之间的关系。 p(vi,vj)标识顶点vi到vj的路径
    如果两个顶点之间存在路径,我们说关系(vi,vj)是属于R

图的分类

有向图:
    两个顶点之间的路径,“弧”
​
无向图:
    如P(vi,vj)存在,则一定P(vj,vi)也存在
    两点之间的关系,”边“
​
网:
    如果给图的关系<vi,vj>加上一个值w(权值),称w为弧或边上的权值。带权的图我们叫网
    权值w的具体含义:根据图在不同的领域的应用而定
    如:我们建立一个网。网中每一个顶点代表一个城市,权值就是代表每个城市之间的距离!!
​
顶点度:
    顶点的边或者弧的数量
    有向图: 入度和出度

连通图/非连通图
    在无向图中,若从顶点vi到顶点vj有路径存在,则称vi和vj是连通
    若图G中任意两个顶点都是连通的,我们则可以说G是连通图,否则非连通图
​
路径:
    从某个顶点到另外一个顶点是否存在一个路径
    直接路径,间接路径

图的存储方式

”数组表示法“:邻接矩阵
”链接法“:邻接表
”十字链表“
”邻接多重表“

邻接矩阵(数组表示法)

G(V,R); // V表示的是顶点的集合,R表示的关系的集合
可以用两个数组来存储一个图!!
一个数组用来存储V 存每一个顶点
一个数组用来存储R 存储两个顶点之间的关系,两个顶点之间的权值

int data 100; //用来存储两个顶点之间的关系 char v[6] = {'A','B','C','D','E','F'}; //图中的顶点集合 <v0,v1> data0 = 6; ...... 第i行,表示从那个顶点出发(边的起始点) 第j列,表示一个路径上的终点

创建一个图:
    #define MAX 100
    struct graph
    {
            char v[MAX];   //表示这个图里面最多可以存储100个顶点
            int data[MAX][MAX]; //表示两个顶点之间的权值
            int num;   //表示图中顶点的个数
    };

邻接表

用链式结构去存储一个图!! 所谓的邻接表,就是将图中的每一个顶点V与由V发出的边或者弧构成一个单链表

数据类型:

    终点元素结构体
    struct Endnode
    {
        int stop_index; //终点元素的下标
        int w;   //代表边上的权值
        struct Endnode * next;   //指向下一条边
    }
​
    顶点元素的结构体
    struct vertex
    {
        char data ;   //顶点元素的数据值
        struct * first;   //指向终点元素的第一条边
    }
​

图的遍历

广度优先搜索和深度优先搜索 图的遍历就是树的遍历的推广,是按照某种规则(或次序)访问图中各个顶点一次,有且仅有一次的操作,也是将网状解决按照某种规则线性化的过程 对图的遍历通常有两种“深度优先”和”广度优先“,这两者都是人工智能(AI)的基础 int visit[]; //用来表示图中的一个顶点还没有被访问 visit[i] == 0; //表示i对应的顶点还没有被访问 visit[i] == 1; //表示i对应的顶点已经被访问过了

深度优先搜索(DFS:depth first search)

初始时,图中的各个顶点都还没有被访问过,假设以图中V0这个顶点出发,先访问V0,在visit数组中标识V0这个顶点已经被访问过了。接下来要去找V0的邻接点vi(先判断vi有没有被访问过),如果被访问过,访问vi,在visit数组中标识vi这个顶点已经被访问了,再去找vi的邻接点...........如果vi的所有的邻接点都被访问了,返回v0,找到v0下一个邻接点

判断这个点能不能去(如果没有路径就不能去吧) 判断这个点该不该去(如果有路径但是被访问过了,就不能去吧)

算法是如何实现的: int visit[p->num] ={0} ; //表示所有顶点都没有被访问过 1: 表示被访问了 0: 表示没有被访问 DFS(p,vo) //p表示图,v0顶点的下标 (1)、先去访问v0,并标记v0 printf p->v[v0]; visit[v0] = 1;

(2)、找v0的下一个邻接点vi
for(vi=0;vi<p->num;vi++)
{
    p->data[v0][vi]
    判断这个点能不能去(如果没有路径就不能去吧)
    判断这个点该不该去(如果有路径但是被访问过了,就不能去吧)
    DFS(p,vi);
​
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 100
#define VER_BIG 999999999

int visit[MAX];//表示顶点是否被访问,
//0:未被访问,1:已被访问
//图类型
typedef struct graph
{
    char v[MAX];//表示这个图里面最多可以存放MAX个顶点
    int data[MAX][MAX];//表示两个顶点之间的权值
    int num;//图里面的顶点个数
}graph;

//找到字符x的下标
int find_i(graph* g,char x)
{
    if(g==NULL)
    {
        return -1;
    }
    int i;
    for(i=0;i<g->num;i++)
    {
        if(g->v[i]==x)
        {
            return i;
        }
    }
    return -1;
}


//创建一个邻接矩阵
graph* initgraph(void)
{ 
    int i,j,d,n;
    graph* g=malloc(sizeof(graph));
    g->num=0;
    printf("输入顶点\n");
    char ch;
    while(1)
    {
       scanf("%c",&ch);
       
       if(ch=='#')
       {
            break;
       }    
        g->v[g->num]=ch; //把顶点存储到一维数组里面去
        g->num+=1;   
    }
    getchar();
    n=g->num;
    for(i=0;i<g->num;i++)
    {
        visit[i]=0;
    }
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
        {          
        g->data[i][j]=VER_BIG;
                       
        }
    }

    char start,stop;
    int w;
    int start_i,stop_i;
    while(1)
    {
        printf("输入边<a,b>及边权\n");
        scanf("%c%c%d",&start,&stop,&w);        
        if(start=='#'||stop=='#'||w==0)
        {
            break;
        }
        getchar();
        start_i=find_i(g,start);
        stop_i=find_i(g,stop);
        if(stop_i==-1||start_i==-1)
        {
            continue;
        }
        //无向图
        g->data[start_i][stop_i]=w;
        g->data[stop_i][start_i]=w;
        
    }

    return g;
}



//输出邻接矩阵
void print_graph(graph* g)
{
    printf("------邻接矩阵------\n");
    int i,j;
    printf("  ");
    for(i=0;i<g->num;i++)
    {
        printf("%c ",g->v[i]);
    }
    printf("\n");
    for(i=0;i<g->num;i++)
    {
        printf("%c ",g->v[i]);
        for(j=0;j<g->num;j++)
        {
            if(g->data[i][j]==VER_BIG)
            {
                printf("# ");
            }
            else
            {
                printf("%d ",g->data[i][j]);
            }
           
        }
        printf("\n");
    }
    printf("------------\n");
}
//深度优先搜索
//i:顶点下标
void DFS(graph* g,int i)
{
    //访问vi
    printf("%c",g->v[i]);
    visit[i]=1; 

    //访问下一个邻接点
    int j;
    for(j=0;j<g->num;j++)
    {
        //(1)判断这个点能不能去(是否有路径)
        //(2)判断这个点该不该去(是否已访问)
        if(g->data[i][j]!=VER_BIG&&visit[j]==0)
        {
            printf("->");
            DFS(g,j);
        }
    }
}


int main()
{  
    graph* g=initgraph(); 
    print_graph(g);
    DFS(g,0);
}

广度优先搜索(BFS:Breadth first search)

广度优先搜索类似于树的层次遍历,初始时图中的各个顶点都没有被访问 从图中的一个顶点v0出发,先让v0入队 while(队列不为空) { 让队首元素出队 接下来就是找队首元素的所有邻接点 { 判断这个点能不能去(如果没有路径就不能去吧) 判断这个点该不该去(如果有路径但是被访问过了,就不能去吧) 如果这两个条件都满足的话,将这个点进行入队操作 } }

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 100
#define VER_BIG 999999999

int visit[MAX];//表示顶点是否被访问,
//0:未被访问,1:已被访问
int q[MAX];//队列,先进先出

//图类型
typedef struct graph
{
    char v[MAX];//表示这个图里面最多可以存放MAX个顶点
    int data[MAX][MAX];//表示两个顶点之间的权值
    int num;//图里面的顶点个数
}graph;

//找到字符x的下标
int find_i(graph* g,char x)
{
    if(g==NULL)
    {
        return -1;
    }
    int i;
    for(i=0;i<g->num;i++)
    {
        if(g->v[i]==x)
        {
            return i;
        }
    }
    return -1;
}


//创建一个邻接矩阵
graph* initgraph(void)
{ 
    int i,j;
    graph* g=malloc(sizeof(graph));
    g->num=0;
    printf("输入顶点\n");
    char ch;
    while(1)
    {
       scanf("%c",&ch);     
       if(ch=='#')
       {
            break;
       }    
        g->v[g->num]=ch; //把顶点存储到一维数组里面去
        g->num+=1;   
    }
    getchar();
   
    for(i=0;i<g->num;i++)
    {
        visit[i]=0;
    }
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
        {          
        g->data[i][j]=VER_BIG;
                       
        }
    }

    char start,stop;
    int w;
    int start_i,stop_i;
    while(1)
    {
        printf("输入边<a,b>及边权\n");
        scanf("%c%c%d",&start,&stop,&w);        
        if(start=='#'||stop=='#'||w==0)
        {
            break;
        }
        getchar();
        start_i=find_i(g,start);
        stop_i=find_i(g,stop);
        if(stop_i==-1||start_i==-1)
        {
            continue;
        }
        g->data[start_i][stop_i]=w;
        g->data[stop_i][start_i]=w;
        
    }

    return g;
}



//输出邻接矩阵
void print_graph(graph* g)
{
    printf("------邻接矩阵------\n");
    int i,j;
    printf("  ");
    for(i=0;i<g->num;i++)
    {
        printf("%c ",g->v[i]);
    }
    printf("\n");
    for(i=0;i<g->num;i++)
    {
        printf("%c ",g->v[i]);
        for(j=0;j<g->num;j++)
        {
            if(g->data[i][j]==VER_BIG)
            {
                printf("# ");
            }
            else
            {
                printf("%d ",g->data[i][j]);
            }
           
        }
        printf("\n");
    }
    printf("------------\n");
}


//广度优先搜索
//i:顶点下标
void BFS(graph* g,int i)
{
    //q[MAX] //队列,先进先出
    int head;//队首元素
    //访问vi
    visit[i]=1; 
    int front=0,rear=0,sum=0;//队头,队尾以及队列元素个数
    q[front]=i;//入队
    sum+=1;
    int ans;
    while(sum>0)
    {
        head=q[front];//访问队头
         //访问下一个邻接点
        int j;
        for(j=0;j<g->num;j++)
        {
            //(1)判断这个点能不能去(是否有路径)
            //(2)判断这个点该不该去(是否已访问)
            if(g->data[head][j]!=VER_BIG&&visit[j]==0)
            {
               q[++rear]=j;//入队
               sum++;//队列元素+1
               visit[j]=1;//标记已被访问
            }
        }
        ans=q[front++];//出队
        sum-=1;//队列元素-1
        if(sum==0)//队列的最后一个元素
        {
            printf("%c",g->v[ans]);
        }
        else
        {
            printf("%c->",g->v[ans]);
        }
        
    }
   
}


int main()
{  
    graph* g=initgraph(); //创建邻接矩阵
    print_graph(g);//输出邻接矩阵
    BFS(g,0);//广度优先搜索,从下标0开始
}

最短路径问题

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 100
#define VER_BIG 9999999
//图类型
typedef struct graph
{
    char v[MAX];//表示这个图里面最多可以存放MAX个顶点
    int data[MAX][MAX];//表示两个顶点之间的权值
    int num;//图里面的顶点个数
}graph;

//找到字符x的下标
int find_i(graph* g,char x)
{
    if(g==NULL)
    {
        return -1;
    }
    int i;
    for(i=0;i<g->num;i++)
    {
        if(g->v[i]==x)
        {
            return i;
        }
    }
    return -1;
}


//创建一个邻接矩阵
graph* initgraph(void)
{ 
    int i,j;
    graph* g=malloc(sizeof(graph));
    g->num=0;
    printf("输入顶点\n");
    char ch;
    while(1)
    {
       scanf("%c",&ch);     
       if(ch=='#')
       {
            break;
       }    
        g->v[g->num]=ch; //把顶点存储到一维数组里面去
        g->num+=1;   
    }
    getchar();
    for(i=0;i<g->num;i++)
    {
        for(j=0;j<g->num;j++)
        {          
        g->data[i][j]=VER_BIG;
                       
        }
    }

    char start,stop;
    int w;
    int start_i,stop_i;
    while(1)
    {
        printf("输入边<a,b>及边权\n");
        scanf("%c%c%d",&start,&stop,&w);        
        if(start=='#'||stop=='#'||w==0)
        {
            break;
        }
        getchar();
        start_i=find_i(g,start);//获取顶点下标,弧的头顶点
        stop_i=find_i(g,stop);//获取顶点下标,弧的尾顶点
        if(stop_i==-1||start_i==-1)//没有找到顶点位置
        {
            continue;
        }
        g->data[start_i][stop_i]=w;//存放有向图邻接关系
        //g->data[stop_i][start_i]=w;
        
    }

    return g;
}

//输出邻接矩阵
void print_graph(graph* g)
{
    printf("------邻接矩阵------\n");
    int i,j;
    printf("  ");
    for(i=0;i<g->num;i++)
    {
        printf("%c ",g->v[i]);
    }
    printf("\n");
    for(i=0;i<g->num;i++)
    {
        printf("%c ",g->v[i]);
        for(j=0;j<g->num;j++)
        {
            if(g->data[i][j]==VER_BIG)
            {
                printf("# ");
            }
            else
            {
                printf("%d ",g->data[i][j]);
            }
           
        }
        printf("\n");
    }
    printf("------------\n");
}

//Dijkstra 有三个辅助向量
int S[MAX]; 
/*
    s[i]=0  源点V 到 Vi的最短路径没有求出
    s[i]=1  源点V 到 Vi的最短路径已经求出
*/
int Dist[MAX];             //保存的是v到vi的最短路径的长度

char Path[MAX][MAX];  
//Path[i]   保存是从源点V0 到 Vi 的最短路径上所经历的“顶点”
//Path[i][0] = 'A'
//Path[i][1] = 'B'
//Path[i][2] = 'C'
//.......
//Path[i][n] = 'vi'
//Path[i][n+1] = '\0'
//求最短路径,Dijsktra算法
void Dijkstra(graph* g,int v0)
{
    int i,j;
    int n=1;//已经找到的边数
    //1.初始化,三个辅助数组
    for (i=0;i<g->num;i++)
    {
        S[i]=(i==v0?1:0);

        Dist[i]=g->data[v0][i];//从源点v0到vi的距离(初始时不借助其他点)
        Path[i][0]=g->v[v0];//从源点v0出发
        if(Dist[i]!=VER_BIG)//如果源点v0可以直接到达点vi
        {
            Path[i][1]=g->v[i];
            Path[i][2]='\0';//字符串末尾加一个'\0'标识结束
        }
        else//如果源点v0不可以直接到达点vi
        {
            Path[i][1]='\0';
        }
    }
    //有g->num的点,找g->num-1条边
    while(n++<g->num)
    {
         //2.找到S[i]==0的Dist[i]的最小值
        int Dist_min=VER_BIG;//Dist[i]最小值
        int w;//记录Dist最小值的下标
        for(i=0;i<g->num;i++)
        {
            if(S[i]==0)//最短路径未被求出
            {
                if(Dist[i]<Dist_min)
                {
                    Dist_min=Dist[i];
                    w=i;//当前最短路径的记录下标
                }
            }
        }
        S[w]=1;//w最短路径已被找到
        //3.更新,如果Dist[w]+<w,u><Dist[u];
        //即通过w点可以有一条更短的路径到达目标点vu;
        for(int u=0;u<g->num;u++)
        {
            if(S[u]==0)//点u未找到最短路径
            {   //检查源点到点u的当前路径长度
            //与源点到当前最短路径的点的长度+该点到点u的路径长度的大小关系
                if(Dist[w]+g->data[w][u]<Dist[u])
                {
                    Dist[u]=Dist[w]+g->data[w][u];
                    //path[u]---->path[w]+u
                    strcpy(Path[u],Path[w]);
                    int l=strlen(Path[u]);
                    Path[u][l]=g->v[u];
                    Path[u][l+1]='\0';
                }
            }
        }
    }
    //输出最短路径
    for(i=0;i<g->num;i++)
    {
        printf("%c -> %c=%d :",g->v[v0],g->v[i],Dist[i]);
        for(j=0;Path[i][j]!='\0';j++)
        {        
            printf("%c ",Path[i][j]);
        }
        printf("\n");
    }
       
}



int main()
{  
    graph* g=initgraph(); //创建邻接矩阵
    print_graph(g);//输出邻接矩阵    
    Dijkstra(g,0);//输出最短路径
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值