算法导论(九)——图

算法导论(九)——图

 

广度优先搜索(Breadth First Search):从一个顶点开始,搜索所有可到达的顶点。它将邻接未访问点都加入队列,在对当前访问点进行邻接搜索。从图的某个顶点v0出发,在访问v0之后,依次搜索访问v0的各个未被访问过的邻接点w1w2,…。然后顺序搜索访问w1的各未被访问过的邻接点,w2的各未被访问过的邻接点,…。——》使用队列

【类似二叉树的层次遍历】

virtual void bfs(int v,int reach[],int label) {

       arrayQueue<int>q(10);

       q.push(v);

       reach[v]= label;//from v

       while(!q.empty()) {

       //delet ewhich had been labeled

              int w = q.front();

              q.pop();

              //adjacent vertex

              vertex Iterator<T>*iw = iterator(w);

              int u;

              while((u=iw->next())!=0){

                     if(reach[u] == 0) {

                            q.push(u);

                            reach[u]= label;

                     }

      }

              delete iw;

       }

}

在实际操作时,针对邻接加权有向图 (a[w][u])或链接图(u=aList[w].firstNode,u=u->next),采用定制版的代码能提高效率,主要改动是搜索邻接w的未访问的顶点部分。参考P409.

深度优先搜索(Depth First Search):它将邻接未访问点当作候选,从某个点开始搜索,终止时返回上一级搜索。

【类似二叉树的前序遍历】

void dfs(int v, int reach[], int label) {

       graph<T>::reach= reach;

       graph<T>::label= label;

       rDfs(v);

}

void rDfs(int v){

       reach[v]= label;

       vetexIterator<T>*iv = iterator(v);

       intu;

       while((u = iv->next) != 0) {

              rDfs(u);

       }

       deleteiv;

}

详细代码见:http://blog.chinaunix.net/uid-26548237-id-3483650.html

比较:两种方法具有相同的时间和空间复杂性,不过它们的最好和最坏情况恰好相对。深度优先搜索效率更高用得更多。

 

最短路径算法,

1.    单源路径——Dijkstra Alg【最短路径的子路径也是最短路径】局限于边的权值非负的情况

 
//http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html//初始顶点v0,图的初始信息存于邻接矩阵adjMat中
#define NUM 8#define MAXINT 32767void AdjMatrix(int adjMat[][NUM]) {//邻接矩阵表示法 for (int i = 0;i < NUM;i++) {  for (int j = 0;j < NUM;j++) {   adjMat[i][j] = MAXINT;  } } adjMat[0][1] = 6; adjMat[0][2] = 1; adjMat[0][3] = 5; adjMat[1][0] = 6; adjMat[1][2] = 5; adjMat[1][4] = 3; adjMat[2][0] = 1; adjMat[2][1] = 5; adjMat[2][3] = 5; adjMat[2][4] = 6; adjMat[2][5] = 4; adjMat[3][0] = 5; adjMat[3][2] = 5; adjMat[3][5] = 2; adjMat[4][1] = 3; adjMat[4][2] = 6; adjMat[4][5] = 6; adjMat[5][2] = 4; adjMat[5][3] = 2; adjMat[5][4] = 6;}
void Dijkstra(const int v0) { int d[NUM];//距离 int p[NUM];//前连接点 bool v[NUM];//访问标记 int adjMat[NUM][NUM] = { 0 }; for (int i = 0;i < NUM;i++) {  AdjMatrix(adjMat);  d[i] = adjMat[v0][i];  v[i] = false;  if (d[i] == MAXINT)   p[i] = -1;  else   p[i] = v0; } d[v0] = 0; v[v0] = true; for (int j = 2;j < NUM;j++) {  int mind = MAXINT;  int mini = v0;  for (int i = 0;i < NUM;i++) {   if ((!v[i])&&d[i] < mind) {    mind = d[i];    mini = i;   }  }  v[mini] = true;  for (int i = 0;i < NUM;i++) {   if ((!v[i]) && adjMat[mini][i] < MAXINT) {    if (d[mini] + adjMat[mini][i] < d[i]) {     d[i]=d[mini] + adjMat[mini][i];     p[i] = mini;    }   }  }  }
}

Floyd-Warshall算法:是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。【十字交叉法】

http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html

差分约束系统:http://blog.youkuaiyun.com/consciousman/article/details/53812818

Johnson算法:能在O(V2lgV+VE)的时间内解决所有结点对最短路径问题,对于大型稀疏图来说这是一个很好的算法

总结:http://blog.youkuaiyun.com/gqtcgq/article/details/45621591

面试常见问题:

1.    判断图是否存在环G=(V,E)

无向图:

  i)如果E>=V,则根据图论知识可直接判断存在环路。(证明:如果没有环路,则该图必然是k棵树 k>=1。根据树的性质,E=V-k             

    ii)如果E<V,

1.求出图中所有顶点的度,

2.删除图中所有度<=1的顶点以及与该顶点相关的边,把与这些边相关的顶点的度减一

3.如果还有度<=1的顶点重复步骤2

4.最后如果还存在未被删除的顶点,则表示有环;否则没有环   复杂度OE+V),

注:初始时刻统计所有顶点的度的时候,复杂度为(V + E),最差的复杂度就为O(EV)了。

 

②遍历整图,找到连通域P个,若 E + P > V,则原图有环,否则无环

 

DFS搜索图,在遍历的过程中,发现某个节点有一条边指向已经访问过的节点,并且这个已访问过的节点不是当前节点的父节点,则表示存在环。该算法的复杂度为O(V)

为防止环重复计算,将节点分为白、灰、黑三种状态。

http://blog.youkuaiyun.com/u010040029/article/details/52861473

 

有向图:

拓扑排序,若不能完成则含有环

计算图中所有点的入度,把入度为0的点加入栈;如果栈非空:( 取出栈顶顶点a,输出该顶点值,删除该顶点,从图中删除所有以a为起始点的边,如果删除的边的另一个顶点入度为0,则把它入栈);如果图中还存在顶点,则表示图中存在环;否则输出的顶点就是一个拓扑排序序列

 

强连通域(对于一个图的某个子图,该子图中的任意u->v,必有v->u

 

③改进的DFS。将深搜的点加一个特殊标记,如果从当前的点往下搜的时候,发现了这个特殊标记,立刻判定有环。【http://blog.youkuaiyun.com/panhe1992/article/details/8366466

或者是每个节点标记成白、灰、黑三种状态,按照节点标记为黑色的时间,越先标记为黑色,在拓扑序列中越靠后(栈) 【http://blog.youkuaiyun.com/u010040029/article/details/52861473

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值