拓扑排序

  指针对于有向图,可用顶点表示活动,用弧表示活动间的优先关系的有向无环图称为顶点表示活动的网,即AOV-网。

特点:

  1. 先行关系具有传递性
  2. AOV-网的拓扑排序(图中顶点的线性顺序称为拓扑排序)不唯一

拓扑排序的基本思想

因为存储方式不同,拓扑排序算法也不同,但基本思想相同

  1. 从有向无环图中选一个无前驱的结点输出
  2. 将此节点和以它为起始结点的边删除
  3. 重复1,2直到不存在无前驱的结点
  4. 若此时输出的结点个数小于图中的结点个数,说明图中存在回路(环),否则输出的顶点顺序就是一个拓扑排序

基于邻接矩阵的拓扑排序

  1. 选取1作为第一个新序号
  2. 找到一个未新编号的值为0的列j,若找到执行3否则,若所有的列都被编过号,排序结束,若有列未曾编号,图中有回路
  3. 输出列号对应的顶点j,把新序号赋值给新找到的列
  4. 将矩阵中的j对应的行全部置零
  5. 新序号加1转向2
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

class Graph
{
    private:
        int num;
        int e;
        bool flag;
        vector<vector<int> > arr;
        vector<bool> visit;//标识该结点是否已经输出 
        vector<int> top_logical_sort;//保存拓扑排序的结果序列 
    public:
        Graph();
        void t_logical_sort();
};
Graph::Graph()
{
    cout<<" num"<<endl;
    cin>>num;
    cout<<" e"<<endl;
    cin>>e;
    
    flag=false;
    visit.resize(num,false);
    
    arr.resize(num);
    for(int i=0;i<num;++i)
        arr.at(i).resize(num,0);
    
    cout<<" 输入边的两端点"<<endl; 
    pair<int,int> p;
    for(int i=0;i<e;++i)
    {
        cin>>p.first>>p.second;
        arr.at(p.first-1).at(p.second-1)=1;
    }
}
void Graph::t_logical_sort()
{
    for(int i=0;i<num;++i)//依次检查每个点,因为在进行一次拓扑排序后会设置该行的状态,所以要进行num次检查 
    {
        for(int t=0;t<num;++t) 
        {
            for(int j=0;j<num;++j)//此for检查有没有不符合条件的值 
            {
                if(t==j)//1.遇到自身 
                    continue;
                else if(visit.at(t)||arr.at(j).at(t))//2.该结点被访问过或该结点有入度(即检查该列是不是全为0) 
                {
                    flag=true;
                    break;
                }
            }
            if(flag)
            {
                flag=false;
                continue;
            }
            else
            {
                top_logical_sort.push_back(t);
                visit.at(t)=true;    
                for(int check=0;check<num;++check)//删除以t为起始点(出度)所有的边
                    arr.at(t).at(check)=0;
                break;
            }
        }
        //flag=false;
    }
    for(auto i=top_logical_sort.begin();i<top_logical_sort.end();++i)
        cout<<*i+1<<"	";
    cout<<endl;
}
int main()
{
    Graph g;
    g.t_logical_sort();
    return 0;
}

 基于邻接表的拓扑排序

  设一个用于存放入度为0的顶点即没有前驱的结点的数组indegree[],查找入度为0的结点也就是找indegree[i]为0的i,删除以i为起点的所有的邻接顶点k,也就是对链在顶点i后面的顶点k将对应的indegree[k]减一操作。为避免重复检测入度为0的顶点,可以在设置一个辅助栈,若某一顶点的入度为0,则将它入栈,当某一顶点输出时,便将他从栈中删除。

基本思想

  1. 首先求出各顶点的入度,并将入度为0的顶点入栈
  2. 只要栈不为空,重复一下处理
    • 将栈顶顶点打印并出栈
    • 将顶点i的每一个邻接点k的入度减一,如果顶点k的入度为0,便将他入栈
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
#define max 100
typedef struct Edgenode                                                //边表结构体 
{
    int adjvex;                                                        //储存边表结点 
    int wight;                                                        //储存顶点间权值(可能用不上) 
    Edgenode *next;                                                    //连接边表结点的指针 
}Edgenode;
 
typedef struct                                                         //顶点表结构体 
{
    Edgenode *firstedge;                                            //顶点表与边表的连接指针 
    bool x;                                                            //判断某一顶点是否已经输出,输出了为ture,判断时自动跳过 
    int data;                                                        //储存顶点值 
}Vertexnode;
typedef Vertexnode AdjList[max];                                    //顶点表数组,储存顶点
 
typedef struct                                                        //图结构体 
{
    int vertexnum;                                                    //储存顶点个数 
    int edgenum;                                                    //储存边的条数 
    AdjList adjlist;                                                //创建adjlist数组,用来保存顶点表的顶点 
}Graph;
 
void create_graph(Graph *G)
{
    cout<<"input the vertexnum and edgenum"<<endl;                    //输入顶点数与边数 
    cin>>G->vertexnum>>G->edgenum;
    int i,j;
    Edgenode *e;                    
    for(i=0;i<G->vertexnum;i++)                                        //初始化 
    {
        G->adjlist[i].data=i;
        G->adjlist[i].firstedge=NULL;
        G->adjlist[i].x=false;
    }
    
    while(G->edgenum--)                                                //初始化输入顶点间关系 
    {
        cout<<"input the two points which be connected by a line"<<endl;
        cin>>i>>j;
        e=new Edgenode;
        e->adjvex=i;
        e->next=G->adjlist[j].firstedge;
        G->adjlist[j].firstedge=e;
    }
}
 
void order(Graph *G)                                                //拓扑排序函数 
{
    
    int count,Count=0;                                                //count用于大循环,检查顶点表数组的每一个已保存顶点,Count用于每次输出一个顶点加一,最后检查count与Count是否相等 ,若相等,说明有环 
    for(count=0;count<G->vertexnum;count++)
    {
        int count1=0;
        if(!G->adjlist[count].x&&!G->adjlist[count].firstedge)        //如果被检测顶点还未输出(x为flase)并且入度为零(firstedge后无结点),则输出并删去其它顶点里拥有它的节点 
        {
            cout<<G->adjlist[count].data;
            G->adjlist[count].x=true;                                //将其设置为已输出 
            Count++;
            
            Edgenode *e;
            for(;count1<G->vertexnum;count1++)                        //检查其它顶点里是否有已输出顶点的相关节点 
            {
                if(count1==count||G->adjlist[count1].x)                //如果是输出的顶点或已输出的顶点,则跳过(因为已经输出了) 
                    continue;
                e=G->adjlist[count1].firstedge;
                while(e)                                            //循环检查一个顶点的边表 
                {    
                    if(e==G->adjlist[count1].firstedge&&e->adjvex==G->adjlist[count].data)//如果边表第一个便是已输出顶点的节点 
                    {
                        G->adjlist[count1].firstedge=e->next;
                        delete e;                                    //删去 
                        e=NULL;                                        //此边表已经不会再出现第二个已输出顶点的节点,while循环结束 
                        continue;
                    }
                    if(e->next&&e->next->adjvex==G->adjlist[count].data)//如果已输出节点在边表中第二个或后面的 
                    {
                        Edgenode *e1;
                        e1=e->next;
                        e->next=e1->next;                            //删去 
                        delete e1;
                    }
                    e=e->next;                                        //往下检查 
                }
                
            }
        }
        
    }
}
 
void show(Graph *G)                                                    //显示函数,显示每个顶点的邻接表(但此邻接表储存的是每个顶点的入度节点,不是出度节点) 
{
    int count;
    Edgenode *e;
    for(count=0;count<G->vertexnum;count++)
    {
        e=G->adjlist[count].firstedge;
        cout<<G->adjlist[count].data<<'	'<<'	';
        while(e)
        {
            cout<<e->adjvex<<'	';
            e=e->next;
        }
        cout<<endl;
    }
}
 
int main()
{
    Graph *G;
    G=new Graph;
    create_graph(G);
    show(G);
    cout<<endl<<"begin to order:"<<endl;
    order(G);
    cout<<endl<<"after ordering:<<endl";
    show(G);
    return 0;
}
原文地址:https://www.cnblogs.com/tianzeng/p/10458674.html