一、题目
给出一个O(V+E)时间的算法,以计算一个有向图G=(V,E)的分支图。注意在算法产生的分支图中,两个顶点之间至多只能有一条边。
二、思考
没有找到关于有向图的分支图的定义,在网上找了一种解法,根据这个解法的结果推测,有向图的分支图应该是书P338,22-9(c)中顶点合并后的结果。
过程如下:
STEP1:求强联通分量,结果用scc[u]表示,即顶点u属于第scc[u]个强联通分量,O(V+E)
STEP2:按照scc[u]从小到大对顶点排序,O(V)
STEP3:把每个强联通分量的第一个顶点加入到V|SCC中,O(V)
STEP4:计算集合S={(x, y)|edge(u, v)属于E,x=scc[u],y=scc[v],x!=y},O(E)
STEP5:对S做基数排序,即两次计数排序,O(V+E)
STEP6:把每个不同的(x, y)加入到E|SCC中,O(E)
总时间O(V+E)
三、代码
1.基数排序Radix_Sort.h
struct Set
{
int x;
int y;
};
int digit = 2, length_A;
//基数排序调用的稳定排序
void Stable_Sort(Set *A, Set *B, int k, int d)
{
int i, j;
//将C数组初始化为0,用于计数
int *C = new int[k+1];
for(i = 0; i <= k; i++)
C[i] = 0;
int *D = new int[length_A+1];
for(j = 1; j <= length_A; j++)
{
//D[j]表示第[j]个元素的第i位数字
if(d == 1)
D[j] = A[j].x;
else
D[j] = A[j].y;
//C[j]表示数字D[j]在数组A中出现的次数
C[D[j]]++;
}
//C[i]表示所以<=i的数字出现过的次数
for(i = 1; i <= k; i++)
C[i] = C[i] + C[i-1];
//初始化B为0,B用于输出排序结果
// for(i = 1; i <= length_A; i++)
// B[i] = 0;
for(j = length_A; j >= 1; j--)
{
//如果<=D[j]的数字的个数是x,那么排序后A[j]应该出现在第x个位置,即B[x]=A[j]
B[C[D[j]]] = A[j];
C[D[j]]--;