您可以扫描边缘列表,交换索引,这样对于每个索引(i, j)
,i < j
. 您在 O(N) 中执行此操作。
您还需要一个排序的边列表,这是 O(N log N)。获得排序的边列表后,您可以将其存储为 Symmetric-CSR 格式。读取单元格(y,x)
时,如果y > x
然后交换y
和x
. 然后你阅读row_pointer[y]
and row_pointer[y+1]
,让它们成为Pa
and ,然后开始在and之间Pb
扫描CSR[i]
i ;如果>= (找到或未找到取决于 if = 或 >)或 if (未找到)则退出。Pa
Pb
x
CSR[i]
i == Pb
您还可以生成第二个边缘列表 where j > i
,并对其进行排序。此时,您可以同时扫描两条边,并生成不需要对称的 CSR 列表。
j0 = j1 = N+1
# i-th row:
# we are scanning NodesIJ[ij] and NodesJI[ji].
If NodesIJ[ij][0] == i
j0 = NodesIJ[ij][1]
If NodesJI[ji][0] == i
j1 = NodesIJ[ji][1]
if (j0 < j1)
j = j0
j0 = N+1
ij++
else
if (j1 == N+1)
# Nothing more on this row, and j0 and j1 are both N+1.
i++;
continue
j = j1
j1 = N+1
ji++
# We may now store (i,j) in CSR
if (-1 == row_ind[i])
row_ind[i] = csr;
col_ind[csr++] = j
可以改进上面的算法,观察对于任何给定i
的 ,如果存在p
和q
,那么NodesIJ[p] = i
和NodesJI[q] = i
,它将始终是,NodesIJ[p][1] > NodesJI[q][1]
因为前一个列表描述了右上三角形,而后者描述了左下角。所以我们可以扫描 NodesJI 直到NodesJI[p][0]
是 i,然后继续NodesJI[q]
。
我们还可以避免总是检查row_ind
初始化,注意如果csr
索引没有改变,那么该行是空的,对应的值可以是 -1(或 N+1,或者我们想要的任何“无效”值),否则它必须是的前一个值csr
。
scsr = csr;
while NodesIJ[ij][0] == i
col_ind[csr++] = NodesIJ[ij++][1]
while NodesJI[ji][0] == i
col_ind[csr++] = NodesJI[ji++][1]
row_ind[i++] = (csr == scsr) ? -1 : scsr;
以上运行时间为 O(N log N)。
另一种方法是分配矩阵,将边缘列表解码为矩阵并将其解析为 CSR。这是 O(N),但可能需要太多内存;对于 N 的列表大小,您最多可能有 N^2(或 (N/a)^2,a 是平均连接数)单元格。数百万条边的列表可能很容易需要数十 GB 的存储空间。