【问题标题】:How do you implement Knuth's Toposort in C?你如何在 C 中实现 Knuth 的拓扑排序?
【发布时间】:2021-04-20 06:22:27
【问题描述】:

我正在尝试在 C 中实现 Knuth 的拓扑排序算法。当我搜索在线资源时,我看到的都是 Kahn 算法的实现,这让我很困惑。他们都一样吗?还是他们不同?这是基于我研究的实现。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define MAX 1000

void create_graph();
void add(int vertex);
int del();
int isEmpty();
int find_indegree_of_vertex(int vertex);

int total_vertices;
int adjacent_matrix[MAX][MAX];
int queue[MAX];
int front = -1;
int rear = -1;

int main()
{
      int i, vertex, count, topological_sort[MAX], indegree[MAX];
      create_graph();
      for(i = 1; i <= total_vertices; i++)
      {
            indegree[i] = find_indegree_of_vertex(i);
            if(indegree[i] == 0)
            {
                  add(i);
            }
      }
      count = 0;
      while(!isEmpty() && count < total_vertices)
      {
            vertex = del();
            topological_sort[++count] = vertex;
            for(i = 1; i <= total_vertices; i++)
            {
                  if(adjacent_matrix[vertex][i] == 1)
                  {
                        adjacent_matrix[vertex][i] = 0;
                        indegree[i] = indegree[i] - 1;
                        if(indegree[i] == 0)
                        {
                              add(i);
                        }
                  }
            }
      }
      for(i = 1; i <= count; i++)
      {

           printf("%d ", topological_sort[i]);

      }
      printf("\n");
      return 0;
}

void add(int vertex)
{
      if(!(rear == MAX - 1))
      {
            if(front == -1)
            {
                  front = 0;
            }
            rear = rear + 1;
            queue[rear] = vertex ;
      }
}

int isEmpty()
{
      if(front == -1 || front > rear)
      {
            return 1;
      }
      else
      {
            return 0;
      }
}

int del()
{
      int element;
      if(front == -1 || front > rear)
      {
            exit(1);
      }
      else
      {
            element = queue[front];
            front = front + 1;
            return element;
      }
}

int find_indegree_of_vertex(int vertex)
{
      int count, total_indegree = 0;
      for(count = 0; count < total_vertices; count++)
      {
            if(adjacent_matrix[count][vertex] == 1)
            {
                  total_indegree++;
            }
      }
      return total_indegree;
}

void create_graph()
{
      int count, maximum_edges, origin_vertex, destination_vertex;
      char v1[1000], v2[1000];
      char temp[10];
      scanf("%d\n", &total_vertices);
      maximum_edges = total_vertices * (total_vertices - 1);
      for(count = 1; count <= maximum_edges; count++)
      {
            fgets(temp, sizeof(temp), stdin);;
            char * splitter;
            splitter = strtok(temp, " ");
            strncpy(v1, splitter, strlen(splitter)+1);
            splitter = strtok(NULL, " ");
            strncpy(v2, splitter, strlen(splitter)+1);
            origin_vertex = atoi(v1);
            destination_vertex = atoi(v2);
            if((origin_vertex == 0) && (destination_vertex == 0))
            {
                  break;
            }
            else
                  adjacent_matrix[origin_vertex][destination_vertex] = 1;
      }
}

示例输入:

15 (Number of vertices)
1 2
2 3
4 5
5 1
5 12
5 6
7 6
8 9
10 11
12 10
12 13
13 14
13 9
14 15
0 0 (End of entries, not a part of the adjacency matrix.)

输出:

4 7 8 5 1 6 12 2 10 13 3 11 9 14 15

预期输出(来自我们的课堂活动):

4 7 8 5 6 12 1 13 10 2 9 14 11 3 15 (Notice the difference!)

我的代码接受对的输入,并在应用拓扑排序后返回顺序。为简单起见,我假设该条目是拓扑排序的有效图。

【问题讨论】:

  • 嗯...对我来说,不清楚你在问什么。代码有问题吗?
  • 对不起。我的问题是,Kahn 的算法与 Knuth 的算法不同吗?如果是这样,Knuth 的算法究竟是如何实现的?我有自己的实现,但这是基于卡恩的。
  • 你从哪里得到 Knuth 算法的规范?淘宝?别的地方?你在哪里找过卡恩的算法?
  • 对于卡恩算法,我从 geeksforgeeks.org/topological-sorting-indegree-based-solution/ 和 codezclub.com/c-topological-sorting-algorithm-example/ 获得了它,我对遵循我的意见。对于 Knuth,我还没有看到,但是有 Knuth 的 Toposort 的文章。
  • 好吧,如果你真的读过 TAOCP(计算机编程的艺术,第 1 卷,第 3 版中的第 2.2.3 节),你会发现 Knuth 的“算法 T(拓扑排序)”以及评论:一种类似于算法 T 的拓扑排序技术(但没有队列链接的重要特征)由 A. B. Kahn, CACM 5 (1962), 558-562 首次发表。。这表明 Knuth 的算法 T 与 Kahn 的算法不同。如果您还没有自己的 TAOCP 副本,那么现在可能是进行投资的好时机。做不到这一点,是时候突袭图书馆了。

标签: c sorting graph directed-acyclic-graphs knuth


【解决方案1】:

如果您阅读 Knuth 的 TAOCP(计算机编程艺术)第 1 卷,第 3 版中的第 2.2.3 节,您会发现 Knuth 的“算法 T(拓扑排序)”以及评论:

一种类似于算法 T 的拓扑排序技术(但没有队列链接的重要特征)由 A. B. Kahn 首次发表,CACM 5 (1962), 558-562。

这表明 Knuth 的算法 T 与 Kahn 的算法不同。

【讨论】:

    【解决方案2】:

    可以这样实现:

    #include <stdio.h>
    
    #define MAX 200
    
    int n,adj[MAX][MAX];
    
    int front = -1,rear = -1,queue[MAX];
    
    void main() {
    
        int i,j = 0,k;
        int topsort[MAX],indeg[MAX];
        create_graph();
        print("The adjacency matrix is:\n");
        display();
        for (i=1;i<+n;i++) {
            indeg[i]=indegree(i);
            if(indeg[i]==0)
               insert_queue(i);
        }
        while(front<=rear) {
            k=delete_queue();
            topsort[j++]=k;
            for (i=1;i<=n;i++) {
                if(adj[k][i]==1) {
                    adj[k][i]=0;
                    indeg[i]=indeg[i]-1;
                    if(indeg[i]==0)
                         insert_queue(i);
                }
            }
        }
        printf("Nodes after topological sorting are:\n");
        
        for (i=0;i<=n;i++)
        {   
              printf("%d",topsort[i]);
              printf("\n");
        }
    }
    
    create_graph() 
    {
    
        int i,max_edges,origin,destin;
        printf("\n Enter number of vertices:");
        scanf("%d",&n);
        max_edges = n * (n - 1);
    
        for (i = 1; i <= max_edges; i++)
        { 
             printf("\n Enter edge %d (00 to quit):",i);
             scanf("%d%d",&origin,&destin);
        
             if ((origin == 0) && (destin == 0)) {
                  printf("Invalid edge!!\n");
                  i–;
                } 
             else
                adj[origin][destin] = 1;
        }
        
        return;
    }
    
    display() 
    {
        
        int i,j;
        for (i = 0;i <= n;i++) {
            for (j = 1;jrear) {
                printf("Queue Underflow");
                return;
            } else {
                del_item = queue[front];
                front = front + 1;
                return del_item;
            }
        }
    }
    
    int indegree(int node) {
        int i,in_deg = 0;
        for (i = 1;i <= n;i++)
           if(adj[i][node] == 1)
              in_deg++;
        return in_deg;
    }
    

    【讨论】:

    • 这段代码不能在微弱的现代编译器下编译——任何使用 C99、C11 或 C18 的东西都不会接受它。代码不完整:函数insert_queue()delete_queue() 被使用但既没有声明也没有定义。该代码包含一条奇怪的行 i-; — 应该是 i--; 吗?您应该使用比您年轻的标准而不是 C90 进行编译。为了有用,您应该包含省略的功能。还有很多其他问题需要解决; display() 应该是void display(void)create_graph() 应该是void create_graph(),等等
    • for (j = 1;jrear) 是什么意思?
    • 这段代码自 2010 年以来一直在互联网上流传 - 没有迹象表明它曾经编译或工作过
    【解决方案3】:

    我认为问题在于 Knuth 的拓扑排序算法有多个版本。 1968 年发表的第一个与卡恩算法(1962 年发表)相同。 Donald Knuth 于 1964 年发表了一种为拓扑排序生成所有可能解决方案的算法(它使用 deque D 用作排序的计数器数组)。

    http://www.cs.iit.edu/~cs560/fall_2012/Research_Paper_Topological_sorting/Topological%20sorting.pdf

    【讨论】:

      【解决方案4】:

      https://github.com/theartofcomputerprogramming/programs/blob/main/vol_1_fundamental_algorithms_chap_2_information_structures/sec_2.2.3_linked_allocation/algorithm_t_topological_sort.chttps://github.com/theartofcomputerprogramming/programs/blob/main/vol_1_fundamental_algorithms_chap_2_information_structures/sec_2.2.3_linked_allocation/algorithm_t_topological_sort.c有一个来自 TAOCP 的 Knuth 拓扑排序算法的 C 实现

      C代码用2.2.3节TAOCP的链接分配中的算法T(拓扑排序)的每个步骤进行注释

      Knuth 的算法非常快速和紧凑,因为队列链接将其与 Kahn 的算法区分开来。 C 代码在文件顶部包含对队列如何工作的说明。

      值得注意的是,Knuth 在 TAOCP 的第 1 卷中很早就介绍了这种算法,远早于后来的卷中介绍树或图。

      该程序采用二进制输入并输出二进制数据,以与 TAOCP 的 MMIX 补充附录的第 2.2.3 节链接分配中的程序 T(拓扑排序)兼容。它可以被认为是 MMIX 实现的一个端口,可用于查看 Knuth 算法的预期输出。

      data 子目录中有用于测试的示例二进制数据以及要检查的文本表示 - 我已将问题示例中的数据添加到 in.2.le.dat(来源 in.2.txt

      可以在 repo 中使用此工具生成二进制数据 - https://github.com/theartofcomputerprogramming/programs/blob/main/tools/texttobinary.sh

      我用 gcc 11.2 和 vc++ 2022 版本 17.1 测试了程序

      在 Windows 上构建的注意事项 - 使用外部构建目录,因为 repo 中的源路径很长并且打破了 cmake 路径限制 - cmake 配置可能会失败,并出现关于 No CMAKE_C_COMPILER could be foundNo CMAKE_CXX_COMPILER could be found 的神秘错误

      这是在 x64 等小端系统上运行程序的方法

      $ algorithm_t_topological_sort -h
      
      usage: algorithm_t_topological_sort <in.dat >out.dat
      
      Implements Algorithm T (Topological sort) from 2.2.3 Linked Allocation, The Art of Computer Programming Volume 1, Fundamental Algorithms by Donald Knuth
      Input and output is binary compatible with Program T (Topological sort) from 2.2.3 Linked Allocation, The MMIX Supplement by Martin Ruckert
      
      Reads sequence of pairs of binary uint32_t values on stdin
      Each pair is a dependency relation between objects
      First of each pair is predecessor, second is successor
      First pair is special: first value is 0, second is objects count
      Last pair is special: both values are 0
      Output on stdout is binary uint32_t values in topologically sorted order
      
      Examples:
      algorithm_t_topological_sort <in.0.le.dat | od -An -td4 -w4 -v
      
      $ cat in.2.le.dat | od -An -td4 -w4 -v | paste -d ' ' - - | tr -s ' ' | sed 's/ //'
      
      0 15
      1 2
      2 3
      4 5
      5 1
      5 12
      5 6
      7 6
      8 9
      10 11
      12 10
      12 13
      13 14
      13 9
      14 15
      0 0
      

      结果与问题和 cmets 中看到的排序顺序不同 - 注意 Linux 上的 tsort 也实现了 Knuth 算法

      $ algorithm_t_topological_sort <in.2.le.dat | od -An -td4 -w4 -v | tr -s '\n' ' ' | sed 's/ //'
      
      8 7 4 5 6 12 1 13 10 2 9 14 11 3 15 0
      

      【讨论】:

        猜你喜欢
        • 2017-02-27
        • 2022-11-26
        • 1970-01-01
        • 2020-10-26
        • 1970-01-01
        • 1970-01-01
        • 2021-07-28
        • 1970-01-01
        相关资源
        最近更新 更多