图的拓扑排序的非递归和递归的c++实现

最近研究了一下图的深度优先遍历,尝试写了一下递归和非递归的算法,从而得到一个有向无环图的拓扑序列, 在教材书中,很多都是利用的递归方法来完成图的深度优先遍历,这里可以给大家介绍一下非递归的遍历实现。
对于有向图的深度优先遍历和拓扑序,需要明确下面几点:
1)只有有向无环图才可以生成拓扑序,如果有环,无法生成拓扑序
2)深度优先遍历可以检查出是否有环
3)深度优先遍历的非递归实现应该使用栈来实现。
4)拓扑序如果存在,可能不止一个,这里只要求出一个就可以了。

下面就简单利用C++来实现递归和非递归的基于深度优先的拓扑序。

  • 前置条件: 不失一般性,我们可以用0…n-1 来表示n个顶点,其使用vector<int> verticalState来表示顶点的访问状态 (0, 表示未访问,1 表示正在访问,2 表示访问完成,实际的项目中我们更应该用枚举来实现)。可以利用map<int, vector> adjList 的map数据结构来表示邻接表,map的 key值代表的是顶点的id, value代表的是顶点的邻接表,这样我们就可以表示一张有向图了。
  • 递归实现, 递归实现相对来说简单一点,代码如下:
// 生成拓扑序,如果没有,就生成空的Vector
// n 表示顶点个数,adjList 表示图的邻接表
vector<int> generateOrder (int n, map<int, vector<int>> &adjList)
{
   //初始化顶点的状态
   vector<VerticalState> verticalState (n, NoneVisit);
   vector<int> empty;  // 返回值当图为有环图的时候
   vector<int> ans;
   // 一个一个的顶点进行访问
   for ( int i = 0; i < n; i++ ){
      bool success = dfs (adjList, i, verticalState, ans);
      if ( !success ){ return empty; } // 有环就直接返回空
   }

   // 处理哪些无边的顶点
   for ( int i = 0; i < n; i++ ){
      if ( std::find (ans.begin (), ans.end (), i) == ans.end () ){
         ans.push_back (i);
      }
   }
   return ans;
}

// 递归函数用于访问当前节点的深度优先遍历图
// 返回false如果有环,有环的唯一情况是:深度优先遍历时候,访问到的邻接点是正在访问状态
// 无环的访问true
// adjList 表示图的邻接表
// verticalId 表示当前的顶点id
// verticalState 表示顶点的访问状态
// order 表示顶点访问完成的顺序,用于生成拓扑序
bool dfs (map<int, vector<int>> &adjList, int verticalId, vector<VerticalState> &verticalState, vector<int>&order)
{
   if ( verticalState[verticalId] == NoneVisit ){
      bool success = true;
      verticalState[verticalId] = Visiting; // is visiting
      vector<int> afterVec = adjList[verticalId];
      for ( int index = 0; index < afterVec.size (); index++ ){
         if ( verticalState[afterVec[index]] == Visiting ) {
            return false;
         }
         else if ( verticalState[afterVec[index]] == NoneVisit)
         {
            success = success && dfs (adjList, afterVec[index], verticalState, order);
            if ( !success ) return false;
         }
      }
      // 顶点访问完成,可以将其放入完成队列中
      verticalState[verticalId] = Visited; 
      order.push_back (verticalId);
   }

   return true;
}
  • 非递归实现: 和通常的树的访问一样,深度优先遍历的非递归实现需要用栈来实现。
enum VerticalState
{
   NoneVisit, 
   Visiting,
   Visited,
};

// 生成拓扑序,如果没有,就生成空的Vector
// n 表示顶点个数,adjList 表示图的邻接表
vector<int> generateOrder (int n, map<int, vector<int>> &adjList)
{
   //初始化顶点的状态
   vector<VerticalState> verticalState (n, NoneVisit);

   vector<int> empty;  // 返回值当图为有环图的时候
   vector<int> ans;
   // 一个一个的顶点进行访问
   for ( int i = 0; i < n; i++ ){
      ***if ( verticalState[i] != NoneVisit ) { continue; } // 顶点已经访问过了,无需访问
      stack<int> verticalStack;
      verticalStack.push (i);
      while ( !verticalStack.empty () ){
         int verticalId = verticalStack.top ();
         if (verticalState[verticalId] == Visiting ) //这是顶点弹出,应该是访问已经完成
         {
            verticalState[verticalId] = Visited; // 节点访问完成
            verticalStack.pop ();
            ans.push_back (verticalId);
            continue;
         }
         verticalState[verticalId] = Visiting; // 设置顶点访问标志
         bool finished = true; // 是否有未访问到的临接点,如果没有,表示该节点已经访问完成。
         vector<int> afterVec = adjList[verticalId];
         for ( int index = 0; index< afterVec.size (); index++ ){
            // 有环,直接返回
            if (verticalState[afterVec[index]] == Visited) {
               return empty;
            }
            // 代表有临接点,压栈
            else if (verticalState[afterVec[index]] == NoneVisit )
            {
               verticalStack.push (afterVec[index]);
               finished = false;
            }
         }
         if (finished ){
            verticalState[verticalId] = Visited; // 顶点返回完成
            verticalStack.pop ();
            ans.push_back (verticalId);
         }
      }***
   }

   // 处理哪些无边的顶点
   for ( int i = 0; i < n; i++ ){
      if ( std::find (ans.begin (), ans.end (), i) == ans.end () ){
         ans.push_back (i);
      }
   }
   return ans;
}

上面的map可以换成hashmap以提高访问的效率。

    原文作者:拓扑排序
    原文地址: https://blog.csdn.net/leoleocs/article/details/47035419
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞