【数据结构与算法】->算法->拓扑排序->如何确定代码源文件的编译依赖?

Ⅰ 前言

我们一般写程序的时候,一个完整的项目往往会包含很多代码源文件,编译器在编译整个项目的时候,需要按照依赖关系,依次编译每个源文件。比如 a.java 依赖 b.java,那在编译的时候,编译器需要先编译 b.java,才能编译 a.java。

原来我写 C 语言的程序的时候,经常要用到联合编译,当时我们运行程序都是手动在命令行上编译和运行,所以如果要联合编译而且有依赖关系,比如 a.c 里用到了 b.c 的函数,我们就要先把 b.c 编译,生成一个 obj 文件,然后再用 b.obj 和 a.c 一起编译。大家如果看我最早的文章,一定会经常看到。现在用 Java 就舒服很多,IDE 会自动编译这个文件所依赖的其他文件,不用我再一个一个按照依赖关系手动编译了。

编译器通过分析源文件或者程序员事先写好的编译配置文件(比如 Makefile)文件,来获取这种局部的依赖关系。那么编译器又该如何通过源文件两两之间的局部依赖关系,确定一个全局的编译顺序呢?

在这里插入图片描述
这就要用到我们这篇文章要讲的这个算法了。

Ⅱ 拓扑排序算法解析

上面提出的问题的解决思路与 “图” 这种数据结构的一个经典算法 “拓朴排序算法” 有关。那什么是拓扑排序呢?这个概念很好理解,我们来看一个生活中的拓扑排序的例子。

我们在穿衣服的时候都有一定的顺序,我们可以把这种顺序想成,衣服与衣服之间有一定的依赖关系。比如说,你必须先穿内裤,才能穿外裤,不能反过来,不是每个人都能穿出超人的感觉。

假设我们现在有八件衣服要穿,它们之间的两两依赖关系我们已经很清楚了,那如何安排一个穿衣序列,能够满足所有的两两之间的依赖关系?

这就是个拓朴排序的问题。从这个例子里,你应该能想到,在很多时候,拓扑排序的序列并不是唯一的。你可以看一下下面这张图,这两种排序都满足这些局部先后关系的穿衣序列。

在这里插入图片描述
弄懂了生活中的这个例子,你应该对开篇讲的编译依赖关系有思路了,它和这个问题一样,都可以抽象成一个拓扑排序问题。

拓扑排序的原理非常简单,我们的重点放在拓扑排序的实现上面。

我们知道,算法是构建在具体的数据结构上的,针对这个问题,我们先来看看,如何将问题背景抽象成具体的数据结构。

如果 a 先于 b 执行,也就是说 b 依赖于 a,那么就在顶点 a 和顶点 b 之间,构建一条从 a 指向 b 的边。而且,这个图不仅要是有向图,还要是一个有向无环图,也就是不能存在像 a->b->c->d->a 这样的循环依赖关系。因为图中一旦出现环,拓扑排序就无法工作了。实际上,拓扑排序本身就是基于有向无环图的一个算法。

package com.tyz.about_topo.core;

import java.util.LinkedList;

/**
 * 构造有向无环图
 * @author Tong
 */
public class Graph {
   
	private int vertex; //顶点数
	private LinkedList<Integer> adj[]; //邻接表

	public Graph() {
   
	}

	@SuppressWarnings("unchecked")
	public Graph(int vertex) {
   
		this.vertex = vertex;
		this.adj = new LinkedList[this.vertex];
		
		for (int i = 0; i < vertex; i++) {
   
			this.adj[i] = new LinkedList<Integer>();
		}
	}
	
	public void addEdge(int start, int end) {
    //加有向边
		this.adj[start].add(end);
	}

	
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值