算法(10)图

图的表示:

              稀疏图:邻接表(邻接链表数组) (e.g A[2]里面放的是一个链表,是和节点2相连的节点)

              稠密图:邻接矩阵 (n个接点,nxn个矩阵,i-j相连,A[i][j], A[j][i]  为true)

DFS(深度优先搜索):

基本思路: 不撞南墙不回头

                     寻找离起点最远的顶点,只在碰到死胡同时才回来

                     用了一个下压栈结构,获取下一个顶点的方式是最晚加入的顶点

非递归伪代码:

                  

用途:无向图/有向图找连通分量,拓扑排序(优先级限制下的调度问题),寻找有向环

BFS(广度优先搜索)

基本思路:一圈一圈的扫荡

首先扫荡距离起点为1的点,然后扫荡距离起点为2的点,按照与起点的距离的顺序来遍历所有的顶点

基本实现:用了一个队列,先把起点0入队,标记0 mark[0]=1,然后循环条件 当队列不为空的时候, 先出队一个v, 遍历它的邻接点w,假如没被标记过,标记,设置edge[w]=v,入队到队尾

用途:找最短路径

void DFS_nr(int start) {
    visited[start]= true;
    cout<< "visit: "<< start<< endl;
    stack<int> s;
    s.push(start);
    bool is_push;
    while (!s.empty()) {
        is_push= false;
        int top= s.top();
        for (int i= 0; i< N; i++) {
            if (!visited[i]&& mat[top][i]) {
                visited[i]= true;
                cout<< "visit: "<< i<< endl;
                s.push(i);
                is_push= true;
                break;
            }
        }
        if (!is_push) {
            s.pop();
        }
    }
}

无向图

表示:邻接表/邻接矩阵

可达性: DFS (BFS也可)

单点最短路径:BFS

连通分量: DFS (BFS  并查集)

有向图:

表示:邻接表/邻接矩阵

可达性: DFS (BFS也可)

单点最短有向路径:BFS

检查有无环:DFS

拓扑排序:DFS(逆后序)

强连通分量: kosaraju(实际用了两次DFS)

加权无向图:(一般求最小生成树)

表示:邻接表  但链表节点是edge

prim算法和kruskal算法

加权有向图(一般目的就是求最小路径树)

表示:邻接表,链表节点是有向edge

权重非负:Dijkstra算法 (将distTo[] 最小的非树顶点放松,并加入树中)

无环(权值可以负):按拓扑顺序 放松顶点

一般情况(权值可以负,可以有环,但别有负权重环,存在负权重环,最短路径树就不存在了):Bellman-Ford算法

https://blog.csdn.net/a60782885/article/details/72781811

拓扑排序

必须是无环图 才有拓扑排序

有向无环图 把所有顶点排序,保证所有的有向边都是排在前面的顶点指向后面的顶点

1)用队列实现,入度为0的顶点入队, 然后进行循环,条件队列不为空,第一个元素出队,同时设为已访问且放进一个数组A里面,对这个元素的所有领接点入度减1,假如入度为0 了,就入队,

退出循环之后,遍历所有点,看有没有没访问的,有就是有环,也就没有拓扑排序,没有那就输出数组A,就是拓扑排序

2)用DFS的逆后序,这个方法首先需要检查有没有环

检查的方法是,加一个bool数组,size为节点个数的,Onstack,还有个int * cycle 放找到的第一个有向环 ,然后对所有的节点,没有被mark的v,挨个做DFS,具体是,把这个onstack[v]和mark[v]设为true,然后遍历它的邻接点w,假如有cycle不等于nullptr,也就是已经找到环了,直接return,假如没有,再检查有没有被mark过else if,没有就,edge[w] = v,再递归做dfs,  再检查w有没有在栈里面 else if (onstack[w] )  在就是找到环了(因为意味着前面存在w到v,然后现在v到w,所以是个环),然后从v开始 沿着edgeto 直到w 放进cycle,最后再放v。

最后把onstack[v] 重新变回false

检查如果没环,这时候再用dfs,这时候,每个递归完之后,把这个v压入栈里面,最后把栈弹光就是图的拓扑排序、

强连通分量

 kosaraju算法:有向图找强连通分量

1) G^R 求拓扑排序

2)按上面的排序 对G 进行DFS

最小生成树

只有连通图(从图的任意一个顶点能到任意另一个顶点)才存在最小生成树

生成树是图的一颗含有其所有顶点的无环连通子图,最小生成树,就是权值和最小的生成树

我们讨论的最小生成树是在一副加权连通无向图中的,(权值相不相同,无所谓,权值是正是负也无所谓)

两个算法不能处理有向图,所以一般最小生成树都是针对加权无向图而言的都基于两个基础

1)图的任意切分,横切边权重最小者必定属于最小生成树

2)贪心算法:命题k p392  每次找到一种切分,它的横切边 均不为黑色(黑色代表MST的一个边),把最小权值的横切边涂成黑色,重复,直到有 顶点数-1个边为止(最小生成树的边数量一定是顶点数-1)

kruskal算法:

基本思路,把所有边由小到大排序,每次取最小的边,假如不会和已经在MST中的边形成一个环,那就加进来,下一个,假如会形成环,抛弃,下一个。循环直到有 v-1个边

具体方法:最小堆把边排序,并查集识别会形成环的边,队列保存MST结果

时空复杂度:v个顶点,E条边

空间: E

时间: ElogE (最坏)

prim算法:
从节点0开始 每次把   有且只有一个节点在MST里面的边,且这个边是符合条件的最小权值,这个边加到MST里面,直到有V-1个边

lazy实现   时空复杂度:v个顶点,E条边  空间: E   时间: ElogE (最坏)

eager实现: 空间:v  时间ElogE(最坏)

猜你喜欢

转载自blog.csdn.net/speargod/article/details/89165530
今日推荐