图应用之拓扑排序(Topological Sort)

这一篇写有向无环图及其它的应用:

 

清楚概念:

有向无环图(DAG):一个无环的有向图。通俗的讲就是从一个点沿着有向边出发,无论怎么遍历都不会回到出发点上。

有向无环图是描述一项工程或者系统的进行过程的有效工具,比如办公室,到工商局里面注册的时候,他会提示你一个流程,这个流程就是一个有向无环图。

第一步不做,第二步就做不了。

在其期间关心两个问题:

1.工程是否顺利?(拓扑排序)

2.估算整个工程所必须的最短时间。(关键路径)

 

拓扑排序:

数学语言:某个集合上的一个偏序得到该集合上的一个全序的操作过程。

百度百科:

拓扑序列

通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。离散数学中关于偏序和全序的定义:

若集合X上的关系是R,且R是自反的、反对称的和传递的,则称R是集合X上的偏序关系。

设R是集合X上的偏序(Partial Order),如果对每个x,y属于X必有xRy 或 yRx,则称R是集合X上的全序关系。

比较简单的理解:偏序是指集合中只有部分成员可以比较,全序是指集合中所有的成员之间均可以比较。

注意:

①若将图中顶点按拓扑次序排成一行,则图中所有的有向边均是从左指向右的。

②若图中存在有向环,则不可能使顶点满足拓扑次序。

③一个DAG的拓扑序列通常表示某种方案切实可行。

看下面一幅图:

这是一个偏序,1到5,可以 1-3-5 也可以是 1-2-5

2和3没有先后,我们认为的加上 2先于3 或者 3先于2,这样的过程就将该偏序图(1,2,3,5)变成了全序图。

我们定义1-2-3-5这个序列称为拓扑有序。而这个输出的过程就是拓扑排序。拓扑排序结果不止一种。这个图可以看到

1-2-3-5 和1-3-2-5都是排序的结果。

拓扑排序:就是对一个有向图构造拓扑有序的过程。

 

我们把每个顶点看作是一个子过程,边表示子过程的优先顺序,这样的图我们可以定义为AOV。AOV(Activity On Vertex Network)

 

如果拓扑排序:

(1)在有向图中选一个没有前驱的顶点且输出之

(2)从图中删除该顶点和所有以它为尾的弧

重复上述步骤,直至全部点输出,或者当图中的顶点不存在前驱为止(有环)。

 

涉及到了有向图,有前驱和后驱,这里的数据结构也要跟着改变。

之前使用过邻接表,这里需要在邻接表的顶点信息里面添加一个入度的信息即可。

#define MAXVEX 100
#define IFY 65535


typedef char VertexType;
typedef int  EdgeType;
typedef int  IdxType;
typedef int QueueType;
typedef int StackType;

///---------------------------------------
//边节点
typedef struct EdgeNode{
    IdxType idx;
    struct EdgeNode* next;
}eNode;

//顶点节点
typedef struct VexNode{
    int numIn;        //入度数量
    IdxType idx;
    eNode *fitstedge;
}vNode;

//图的集合:包含了一个顶点数组
typedef struct {
    vNode adjList[MAXVEX];
    int numVextexs,numEdges;
}GraphAdjList;

拓扑排序的代码:

int TopplogicalSort(GraphAdjList *g)
{
    int count=0;
    eNode *e=NULL;
    StackType *stack=NULL;
    StackType top=0;
    stack = (StackType *)malloc((*g).numVextexs*sizeof(StackType));
    int i;

    for (i=0;i<(*g).numVextexs;i++)
    {
        if (!(*g).adjList[i].numIn)
        {
            stack[++top] = i;
    //        printf("init no In is %c\n",g_init_vexs[i]);
        }
    }
    

    while(top)
    {
        int geter = stack[top];
        top--;

        printf("%c -> ",g_init_vexs[(*g).adjList[geter].idx]);
        count++;

        //获取当前点出度的点,对出度的点的入度减一(当前点要出图)。
        //获取当前顶点的出度点表
        e = (*g).adjList[geter].fitstedge;
        while(e)
        {
            //选取的出度点的入度减一
            int crntIN = --(*g).adjList[e->idx].numIn;
            if (crntIN == 0)
            {
                //如果为0,则说明该顶点没有入度了,是下一轮的输出点。
                stack[++top] = e->idx;
        //        printf("running the vex is %c\n",g_init_vexs[e->idx]);
            }
            e = e->next;
        }
    }
    if (count < (*g).numVextexs)//如果图本身就是一个大环,或者图中含有环,这样有环的顶点不会进栈而被打印出来。
    {
        return false;
    }
    else
    {
        printf("finish\n");
        return true;
    }
    
}

以下图为例做测试:

找出入度为0的顶点:A、G 

 

 

 

源代码:

// grp-top-sort.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdlib.h>


#define MAXVEX 100
#define IFY 65535


typedef char VertexType;
typedef int  EdgeType;
typedef int  IdxType;
typedef int QueueType;
typedef int StackType;

///---------------------------------------
//边节点
typedef struct EdgeNode{
    IdxType idx;
    struct EdgeNode* next;
}eNode;

//顶点节点
typedef struct VexNode{
    int numIn;        //入度数量
    IdxType idx;
    eNode *fitstedge;
}vNode;

//图的集合:包含了一个顶点数组
typedef struct {
    vNode adjList[MAXVEX];
    int numVextexs,numEdges;
}GraphAdjList;

///-----------------------------------
VertexType g_init_vexs[MAXVEX] = {'A','B','C','D','E','F','G','H','I','J','K','L'};

char *g_input[] = {
    "A->B->C->D",
    "B->E",
    "C->F->I->J",
    "D->E->I->J",
    "E",
    "F->K",
    "G->F->H->K",
    "H->I",
    "I->J->L",
    "J->E->K",
    "K->L",
    "L"
};

//===============================================================
//队列

//队列节点
typedef struct Node {
    QueueType data;
    struct Node *next;
}QNode,*qQNode;

//队列指示
typedef struct {
    int length;
    qQNode frnt,rear;    
}spQueue;

void init_Queue(spQueue *Q)
{
    (*Q).frnt = NULL;
    (*Q).rear = NULL;
    (*Q).length = 0;
}
bool isEmptyQueue(spQueue Q)
{
    if (Q.length == 0)
    {
        return true;
    }
    return false;
}
//进队
void unshiftQueue(spQueue *Q,QueueType elem)
{
    //队列空
    if (isEmptyQueue(*Q))
    {
        qQNode n = (qQNode)malloc(sizeof(QNode));
        n->data = elem;
        n->next = NULL;

        (*Q).frnt = n;
        (*Q).rear = n;
        (*Q).length = 1;
    }
    else
    {
        qQNode n = (qQNode)malloc(sizeof(QNode));
        n->data = elem;
        n->next = NULL;

        (*Q).rear->next = n;

        (*Q).rear = n;
        (*Q).length++;
    }
}

//出队
QueueType shiftQueue(spQueue *Q)
{
    if (isEmptyQueue(*Q))
    {
        printf("Warning:Queue is empty!!!\n");
        return NULL;
    }
    if ((*Q).length == 1)
    {
        QueueType sh = (*Q).frnt->data;
        (*Q).frnt = NULL;
        (*Q).rear = NULL;
        (*Q).length = 0;
        return sh;
    }
    QueueType sh = (*Q).frnt->data;
    (*Q).frnt = (*Q).frnt->next;
    (*Q).length--;

    return sh;
}

//打印队列
void prt_que(spQueue que)
{
    if (isEmptyQueue(que))
    {
        return ;
    }
    qQNode pos = que.frnt;
    while(que.rear->next != pos && pos != NULL)
    {
        printf(" %d ",pos->data);
        pos = pos->next;
    }
    printf("\n");
}
//===============================================================

///-------
//由节点找节点的序号
IdxType strFindIdx(char ch)
{
    int i=0;
    VertexType *p = g_init_vexs;
    while(p != NULL)
    {
        if(*p == ch)
        {
            return i;
        }
        p++;
        i++;
    }
    return i;
}

//由序号找节点
VertexType idxFindStr(IdxType i)
{
    return g_init_vexs[i];
}

void prt_strings(char *p)
{
    char *pos = p;
    while (NULL != *pos)
    {
        printf("%c",*pos);
        pos++;
    }
    printf("\n");
}

void prt_strArrays(char *p[])
{
    char **pos = p; 
    while( *pos != NULL)
    {
        prt_strings(*pos);
        pos++;
    }
}

//我规定:顶点只能是大写。
bool isVexter(char p)
{
    if (p>='A' && p<='Z')
    {
        return true;
    }
    return false;
}



void init_GrapAdjList(GraphAdjList *g,char **str)
{
    char **pos = str;
    int cnt=0;

    //入度清零
    int i;
    for (i=0;i<MAXVEX;i++)
    {
        (*g).adjList[i].numIn = 0;
    }

    while (*pos != NULL) //g_input的每行的首指针
    {
        int i=0;
        while(**pos != NULL) //g_input的每行字母
        {
            if(isVexter(**pos)) //判断是否为顶点(我规定‘A’-‘Z’之间为顶点标志)
            {
                if (i == 0) //建立顶点的节点
                {
                    (*g).adjList[cnt].idx = strFindIdx(**pos);
                    (*g).adjList[cnt].fitstedge = NULL;
                    
                    i=1;
                }
                else if(i == 1) //建立第一个边的节点
                {
                    eNode* n = (eNode*)malloc(sizeof(eNode));
                    n->idx = strFindIdx(**pos);
                    n->next = NULL;

                    (*g).adjList[cnt].fitstedge = n;
                    i=2;
                    
                    //添加入度
                    int iidx = strFindIdx(**pos);
                    (*g).adjList[iidx].numIn++;
                }
                else //边节点连接到前一个边节点上
                {    
                    eNode* n = (eNode*)malloc(sizeof(eNode));
                    n->idx = strFindIdx(**pos);
                    n->next = NULL;

                    //first splist
                    eNode *r = (*g).adjList[cnt].fitstedge;
                    while (r->next != NULL)
                    {
                        r = r->next;
                    }
                    r->next = n;

                    //添加入度
                    int iidx = strFindIdx(**pos);
                    (*g).adjList[iidx].numIn++;
                }
            }
            (*pos)++; 
        }
        

        pos++;
        cnt++;
    }
    (*g).numVextexs = cnt;
}

int TopplogicalSort(GraphAdjList *g)
{
    int count=0;
    eNode *e=NULL;
    StackType *stack=NULL;
    StackType top=0;
    stack = (StackType *)malloc((*g).numVextexs*sizeof(StackType));
    int i;

    for (i=0;i<(*g).numVextexs;i++)
    {
        if (!(*g).adjList[i].numIn)
        {
            stack[++top] = i;
    //        printf("init no In is %c\n",g_init_vexs[i]);
        }
    }
    

    while(top)
    {
        int geter = stack[top];
        top--;

        printf("%c -> ",g_init_vexs[(*g).adjList[geter].idx]);
        count++;

        //获取当前点出度的点,对出度的点的入度减一(当前点要出图)。
        //获取当前顶点的出度点表
        e = (*g).adjList[geter].fitstedge;
        while(e)
        {
            //选取的出度点的入度减一
            int crntIN = --(*g).adjList[e->idx].numIn;
            if (crntIN == 0)
            {
                //如果为0,则说明该顶点没有入度了,是下一轮的输出点。
                stack[++top] = e->idx;
        //        printf("running the vex is %c\n",g_init_vexs[e->idx]);
            }
            e = e->next;
        }
    }
    if (count < (*g).numVextexs)//如果图本身就是一个大环,或者图中含有环,这样有环的顶点不会进栈而被打印出来。
    {
        return false;
    }
    else
    {
        printf("finish\n");
        return true;
    }
    
}

int _tmain(int argc, _TCHAR* argv[])
{
    GraphAdjList grp;
    prt_strArrays(g_input);
    init_GrapAdjList(&grp,g_input);
    if (!TopplogicalSort(&grp))
    {
        printf("grp wrong!\n");
    }
    
    getchar();
    return 0;
}

运行结果:

 

 

转载于:https://my.oschina.net/ZhenyuanLiu/blog/1832894

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值