算法精解----16、图

1、图作为一种模型来定义对象之间的关系。对象由顶点表示,而对象之间的关系则通过顶点之间的边来表示。

2、图分为有向图和无向图,有向图的边叫做弧。
这里写图片描述

3、图的表达式:G = (V, E),E包含有序对(u,v),对于无向图无所谓(u,v)和(v,u)。
如上图,有向图:V={v0, v1, v2, v3}. E={(v0,v1), (v2, v0), (v2, v3), (v3, v0)}.
无向图:V={v0, v1, v2, v3,v4}.E={(v0, v1), (v0, v3),(v1, v2), (v1, v4), (v2, v3), (v2, v4)}

4、图中两个重要关系:邻接、关联
邻接:有向图中(v0, v1)说明v1邻接v0。
完全图:一个图中每一个顶点都与其他顶点相邻接
关联:有向图中(v0, v1)说明边(v0, v1)从顶点v0关联到v1,顶点和边的关系。

5、顶点的入度:以该顶点为终点的边数。
顶点的出度:以该顶点为起点的边数。
简单路径:没有重复顶点的路径
环:路径包含相同的节点两次或以上,即从某点出发最后能返回到某点。

6、连通:每个顶点都能通过某条路径到达其他顶点。
关节点:移除某个顶点将使图或某分支失去连通性。
桥:移除某条边使得图失去连通性。

7、邻接表适用于稀疏图;邻接矩阵适用于稠密图

8、搜索方法:广度优先搜索、深度优先搜索
这里写图片描述

深度优先搜索
这里写图片描述

9、代码实现
这里写图片描述
(1)数据结构:每个顶点对应一个链表节点,节点中*data指向节点结构AdjList

//AdjList包含:顶点;与该顶点相连接的一个顶点集合。这是每个链表节点指向的数据data
typedef struct AdjList_{
    void *vertex;
    Set adjacent;
}AdjList;

//整个图的属性,包含:节点数、边数、所有节点组成的链表属性
typedef struct Graph_{
    int vcount;
    int ecount;
    int (*match)(const void *key1, const void *key2);
    void (*destroy)(void *data);
    List adjlists;
}Graph;

typedef enum VertexColor_{
    white, gray, black
}VertexColor;

(2)初始化

void graph_init(Graph *graph, int (*match)(const void *key1, const void *key2), void(*destroy)(void *data))
{
    graph->vcount = 0;
    graph->ecount = 0;
    graph->match = match;
    graph->destroy = destroy;

    list_init(graph->adjlists, 0, NULL, NULL, NULL);
    return;
}

(3)删除整个图

void graph_destroy(Graph *graph)
{
    AdjList *adjlist;
    while(list_size(&graph->adjlists) > 0)
    {
        if(list_rem_next(&graph->adjlists, NULL, (void **)&adjlist) == 0)
        {
            //删除每个顶点的邻节点集合
            set_destroy(&adjlist->adjacent);

            //删除该顶点,这两者就是链表节点指向的*data
            if(graph->destroy != NULL)
                graph->destroy(adjlist->vertex);

            //释放*data指向结构自身的空间,上面为该空间指向的内容  
            free(adjlist);
        }
    }
}

(4)插入一个顶点:申请一个AdjList,其中成员vertex为data,其边集set初始化为NULL

//这里入口参数*data对应 *adjlist中vertex 
int graph_ins_vertex(Graph *graph, const void *data)
{
    LIST_ELEMENT *element;
    AdjList *adjlist;
    int retval;
    for(element = list_tail(graph->adjlists); element != NULL; element = list_next(element))
    {
        if(graph->match(data, ((AdjList *)list_data(element))->vertex))
            return 1;
    }
    if((adjlist = (AdjList *)malloc(sizeof(AdjList))) == NULL) 
        return -1;
    adjlist->vertex = (void *)data;
    set_init(&adjlist->adjacent, graph->match, NULL);

    if((retval = list_ins_next(&graph->adjlists, list_tail(&graph->adjlists), adjlist)) != 0)
        return retval;
    graph->vcount++;
    return 0;
}

(5)插入一条边

//在图中加入一条连线 
//对于图中已存在顶点data1和data2,单向data1指向data2,就是在顶点data1的集合中加入data2 
int graph_ins_edge(Graph *graph, const void *data1, const *data2)
{
    LIST_ELEMENT *element;
    int retval;

    //先找到顶点data2和data1 
    for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
    {
        if(graph->match(data1, ((AdjList *)list_data(element))->vertex))
            break;
    }
    if(element == NULL)
        return -1;
    //向data1的集合中插入data2 
    if((retval = set_insert( &( (AdjList *)(list_data(element))->adjacent ), data2)) != 0 ) 
    {
        return retval;
    }
    graph->ecount++;
}

(6)删除顶点,顶点不能和其他任何顶点相连,即不在其他节点的集合中,自己的集合为空。

int graph_rem_vertex(Graph *graph, void **data)
{
    LIST_ELEMENT *element;
    AdjList *adjlist;
    int found;
    prev = NULL;
    found = 0;
    for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
    {
        if(set_is_mumber(&( (AdjList *)(list_data(element))->adjacent ), *data))
            return -1;
        if(graph->match(*data,&( (AdjList *)(list_data(element))->vertex ))
        {
            temp = element;
            fount =1;
        }

        //此处在for内部,如果没发现则prev一直在更新 
        if(!found)
            prev = element;
    }
    if(!found)
        return -1;
    if(set_size( &((AdjList *)list_data(temp))->adjacent )> 0)
        return -1;
    *data = adjlist->vertex;

    //此时节点集合为空可以直接释放 
    free(adjlist);
    graph->vcount--;
    return 0;
} 

(7)删除边,即删除data1集合中的元素data2

int graph_rem_edge(Graph *graph, const void *data1, const *data2)
{
    LIST_ELEMENT *element;

    //先找到顶点data1 
    for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
    {
        if(graph->match(data1, ((AdjList *)list_data(element))->vertex))
            break;
    }
    if(element == NULL)
        return -1;
    if(set_remove(&( (AdjList *)(list_data(element))->adjacent ), *data2)) != 0)
        return -1;
    graph->ecount--;
    return 0;
}

(8)找到某个顶点对应的结构adjlist

//data对应的结构返回到adjlist 
int graph_adjlist(const Graph *graph, const void *data, AdjList **adjlist)
{
        LIST_ELEMENT *element, *prev;
        prev = NULL;
        for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
        {
            if(graph->match(*data,&( (AdjList *)(list_data(element))->vertex ))
            {
                break;
            }
            prev = element;

        }
        if(element == NULL)
            return -1;
        *adjlist = list_data(element);
        return 0;
}

(9)判断顶点data1和data2是否有邻接关系,即判断data2是否存在于data1的集合中

int graph_is_adjacent(const Graph *graph, const void *data1, const void *data2)
{
        LIST_ELEMENT *element, *prev;
        prev = NULL;
        for(element = list_head(graph->adjlists); element != NULL; element = list_next(element))
        {
            if(graph->match(*data,&( (AdjList *)(list_data(element))->vertex ))
            {
                break;
            }
            prev = element;

        }
        if(element == NULL)
            return -1;

        return set_is_mumber(&( (AdjList *)(list_data(element))->adjacent ), data2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值