搜索问题的定义
根据问题的时间情况,不断寻找可利用知识,从而构造一条代价最小的推理路线,使问题得以解决的过程称为搜索
适用情况
- 难以获得所求解所需的全部信息
 - 没有现成的算法可供使用
 
图的遍历(重点)
定义
从已给的连通图的某个点出发,沿着一些边访遍所有的顶点,且使每个顶点紧被访问一次,就叫做图的遍历。
深度优先搜索 DFS
邻接表实现 DFS 算法
时间复杂度空间复杂度
DFS算法是一一个递归算法,需要借助一个递归工作栈,故它的空间复杂度为O ( N ) O(N)O(N)。
遍历图的过程实质上是对每个顶点查找其邻接点的过程,其耗费的时间取决于所采用结构。
邻接表表示时,查找所有顶点的邻接点所需时间为O ( E ) O(E)O(E),访问顶点的邻接点所花时间为O ( N ) O(N)O(N),此时,总的时间复杂度为O ( N + E ) O(N+E)O(N+E)。
邻接矩阵表示时,查找每个顶点的邻接点所需时间为O ( N ) O(N)O(N),要查找整个矩阵,故总的时间度为O ( N 2 ) O(N^2)O(N2)。
广度优先搜索 BFS
DFS 算法 VS BFS 算法
最小生成树(重点)
在连通网的所有生成树中,所有边的代价和最小的生成树
Kruskal 算法(克鲁斯卡尔算法)
此算法可以称为“加边法”,初始最小生成树边数为 0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。


Prim 算法(普里姆算法)
此算法可以称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点 s 开始,逐渐长大覆盖整个连通网的所有顶点。


拓扑排序
一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。
且该序列必须满足下面两个条件:
- 每个顶点出现且只出现一次。
 - 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
 
有向无环图(DAG)才有拓扑排序,非 DAG 图没有拓扑排序一说。
例如,下面这个图:
它是一个 DAG 图,那么如何写出它的拓扑排序呢?
这里说一种比较常用的方法:
- 从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。
 - 从图中删除该顶点和所有以它为起点的有向边。
 - 重复 1 和 2 直到当前的 DAG 图为空或当前图中不存在无前驱的顶点为止。后一种情况说明有向图中必然存在环。
 
于是,得到拓扑排序后的结果是 { 1, 2, 4, 3, 5 }。
通常,一个有向无环图可以有一个或多个拓扑排序序列。
最短路径(难点)
Floyd 算法(弗洛伊德算法)— 所有顶点间的最短路径

#include <stdio.h>int main(){int e[10][10],k,i,j,n,m,t1,t2,t3;int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值//读入n和m,n表示顶点个数,m表示边的条数scanf("%d %d",&n,&m);//初始化for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(i==j) e[i][j]=0;else e[i][j]=inf;//读入边for(i=1;i<=m;i++){scanf("%d %d %d",&t1,&t2,&t3);e[t1][t2]=t3;}//Floyd-Warshall算法核心语句for(k=1;k<=n;k++)for(i=1;i<=n;i++)for(j=1;j<=n;j++)if(e[i][j]>e[i][k]+e[k][j] )e[i][j]=e[i][k]+e[k][j];//输出最终的结果for(i=1;i<=n;i++){for(j=1;j<=n;j++){printf("%10d",e[i][j]);}printf("\n");}return 0;}
Dijkstra 算法(迪杰斯特拉算法)— 单源最短路径

#include <stdio.h>
    int main()
    {
        int e[10][10],dis[10],book[10],i,j,n,m,t1,t2,t3,u,v,min;
        int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值
        //读入n和m,n表示顶点个数,m表示边的条数
        scanf("%d %d",&n,&m);
        //初始化
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                if(i==j) e[i][j]=0;
                  else e[i][j]=inf;
        //读入边
        for(i=1;i<=m;i++)
        {
            scanf("%d %d %d",&t1,&t2,&t3);
            e[t1][t2]=t3;
        }
        //初始化dis数组,这里是1号顶点到其余各个顶点的初始路程
        for(i=1;i<=n;i++)
            dis[i]=e[1][i];
        //book数组初始化
        for(i=1;i<=n;i++)
            book[i]=0;
        book[1]=1;
        //Dijkstra算法核心语句
        for(i=1;i<=n-1;i++)
        {
            //找到离1号顶点最近的顶点
            min=inf;
            for(j=1;j<=n;j++)
            {
                if(book[j]==0 && dis[j]<min)
                {
                    min=dis[j];
                    u=j;
                }
            }
            book[u]=1;
            for(v=1;v<=n;v++)
            {
                if(e[u][v]<inf)
                {
                    if(dis[v]>dis[u]+e[u][v])
                        dis[v]=dis[u]+e[u][v];
                }
            }
        }
        //输出最终的结果
        for(i=1;i<=n;i++)
            printf("%d ",dis[i]);
        getchar();
        getchar();
        return 0;
    }
AOV 网
- 顶点表示活动,弧表示活动间的优先关系的有向图。
 - AOV 网是有向无环图,即不应该带有回路,因为若带有回路,则会陷入死循环。
 - 所有活动可排列成一个线性序列,使得每个活动的所有前驱都排在该活动的前面,我们把此序列叫做拓扑序列。
 - AOV 网的拓扑序列不是唯一的。
 - 只有完成了所有前驱事件后才可以进行后续事件。
 
AOE 网


关键路径、关键活动









