组件化项目自动打Tag算法分析
一、问题背景:
组件化开发项目的过程中,会产生很多子库嵌入到壳工程,子库之间依赖关系复杂度也越来越大,每一次发版改动的子库需要打Tag,子库打完Tag后,壳工程打打Tag,项目越来越大时,手动打Tag愈发繁琐,要做到脚本自动化完成打tag,必须先分析子库之间的依赖关系,确定打Tag的先后顺序。
二、抽象模型
壳工程为依赖关系的起点,子库之间也有复杂的依赖关系,使用图状结构再来表示最为合适,下图描述了一个组件化项目壳工程与子库,子库与子库之间的依赖关系。
【图一:依赖关系图】
三、依赖关系图的存储结构
1、 数组表示法(二维数组,一个表示顶点信息,一个表示依赖关系)
注:例如存在A->B的依赖关系,则矩阵位置(2,1)为1,否则为0,纵向看是每个结点的被依赖关系,横向是依赖关系。
2、邻接表表示法
简单的具体实现:
typedef Node{ intput[], output[]}//定义结点,两个数组分别表示入度和出度
Set(Node,Node*......)//集合包含所有的结点
3、十字链表表示
四、依赖关系图的遍历
通过遍历图,找到出度为0的结点,出度为0即说明此仓库没有依赖项,是需要先打Tag的库。处理完后在图中标记此结点,这个结点关联的弧即全部失效,再从剩余的结点找到出度为0的点,依此类推,直到最后一个结点。
1、邻接矩阵的递归遍历以及非递归遍历:
核心代码:
邻接矩阵的创建(C语言实现)
typedef struct /*图的邻接矩阵*/
{
int vexnum, arcnum; //顶点数量,边数量
char vexs[N]; //顶点数组
int arcs[N][N]; ///邻接矩阵
} graph;
void createGraph(graph *g) /*建立一个有向图的邻接矩阵*/
{
int i = 0; int j = 0; char v; g->vexnum = 0; g->arcnum = 0;
printf("输入顶点序列(char字符表示,例如一共有顶点A,顶点B,顶点C,输入ABC#,)(以#结束):\n");
while ((v = getchar()) != '#') {
g->vexs[i] = v; /*写入顶点信息*/
i++;
}
g->vexnum = i; /*顶点数目*/
initgraph();/*邻接矩阵初始化*/
printf("\n输入边的信息(例如:顶点AB的边,从A到B,输入AB回车):\n");
char m; char n;
scanf("\n%c%c", &m, &n);
while (m != '#' && n != '#') {
int indexI = indexOfVex(m, g); int indexj = indexOfVex(n, g);
if (indexI != -1 && indexj != -1) {
g->arcs[indexI][indexj] = 1;
}
scanf("\n%c%c", &m, &n);
}
}
邻接矩阵的递归遍历,时间复杂度为O(n^2),实现较为简单
从任意顶点开始,即从第几行开始遍历,遍历到第i列不为0,递归第i行。
void dfs(int i, graph *g) /*从第i个顶点出发深度优先搜索*/
{
int j;
visited[i] = 1;
for (j = 0; j < g->vexnum; j++)
{
if (visited[j] == 0 && g->arcs[i][j] == 1)
{
dfs(j, g);
}
}
printf("\n打tag:%c", g->vexs[i]);
}
邻接矩阵的非递归遍历
借助栈来实现,如从第0个顶点开始遍历,第0个顶点入栈
取栈顶元素,遍历此顶点的出度,找到一个,入栈,再取栈顶元素循环
栈的变化:
l A
l AB
l ABC
l AB //C结点没有出度(没有依赖项),出栈,出栈即打Tag时机
l ABE //B的第二个出度E结点入栈(第二个依赖)
l ABED // E的出度D入栈
l ABE // D没有出度,出栈
l ABF //B的第三个出度F入栈
l ABFG
l ABF
l AB
l A
l 栈空,结束循环
邻接矩阵打Tag示例代码:https://gitlab.com/360fengdai/tagorder.git
2、邻接表的递归遍历
邻接表的创建(C语言实现,数组代替邻接链接):
数据结构:
typedef struct /*顶点*/
{
char info; //顶点
int visit;
int outCount; //出度数量
int inCount; //入度数量
char outdegree[N]; //出度数组
char indegree[N]; //入度数组
} vex;
typedef struct
{
int count;// 顶点数量
vex data[N];// 顶点数组
} graph;
邻接表递归遍历示例代码:https://gitlab.com/360fengdai/tagorder.git
3、十字链表的遍历
数据结构定义:
typedef struct EdgeNode //弧结点的定义
{
int tailvex; //弧尾结点的下标
int headvex; //弧头结点的下标
struct EdgeNode *headlink; //指向弧头相同的下一条弧的链域
struct EdgeNode *taillink; //指向弧尾相同的下一条弧的链域
EdgeType info; //弧中所含有的信息 可以是权值等
} EdgeNode;
typedef struct VertexNode //顶点结点的定义
{
int index; //下标值
VertexType data; //顶点内容
EdgeNode *firstout; //顶点的第一条出弧
EdgeNode *firstin; //顶点的第一条入弧
} VertexNode;
typedef struct OLGraph //十字链表存储结构的有向图定义
{
VertexNode vertices[MaxVex];
int vexnum; //顶点数
int arcnum; //边数
} OLGraph;