目的
通过减小图的规模,可以有效的降低社区挖掘算法的运行时间。比如一个具有n个点的图,它的点集的大小为n,则点集具有2n个子集,我们需要对这2n个点集判断它是否可以构成一个社区。当图中点的个数减小时,可以发现判断次数大大减小。
结构性约束简化算法
介绍:结构性约束要求图中每个点都至少有k个邻接点,其中k为结构性约束阈值(可以根据实验自己设置)。
目的:当对一个图运行此算法后,所得的结果是一个满足结构性约束的图。即此算法删除了图中度小于k的点,使得最后图中每个点的度都大于等于k。
难点:当那个删除一个度小于k的顶点后,此点的邻接点的度会发生改变。对于每一个度大于等于k的邻接点而言,改变的结果有两种:第一种是删除点后,邻接点的度依旧大于等于k;第二种是删除点后,邻接点的度变的小于k。对于第二种情况,我们需要删除此邻接点,而删除此邻接点又会使其它点的度发生改变。
算法思想:使用 先进先出 思想维护一个动态数组,这个动态数组保存需要删除的点,将因此点删除而度减一进而要被删除的点添加到此数组中后,再将此点从图中删除,从数组中删除。
代码
/**
* 化简g,不断删除g中度小于k的点,会出现删除点u,使得点v的度小于k,此时也要删除点v
* 最终g中每个点都满足度大于等于k
* @param g
* @param k
*/
public void simplifyK(int k){
//1.初始化一个数组,保存被删除的点
List<NodeLinkedList> abandonedNodes = new ArrayList<NodeLinkedList>();
//2.遍历图中每一个点,将degree<k的点添加到abandonedNodes
for(int i=0; i<this.getSize(); i++){
if(this.getNodeList().get(i).getHead().getDegree() < k){
abandonedNodes.add(this.getNodeList().get(i));
}
}
//3.遍历abandonedNodes中所有点
for(int i=0; i<abandonedNodes.size(); i++){
//3.1 abandonedNodes中的链表的头结点head的度小于k
Node head = abandonedNodes.get(i).getHead();
//3.2 处理head的邻接点
Node linked_node = head.getNext();
while(linked_node != null){
//3.2.1 首先定位到以邻接点为头的NodeLinkedList
int index = -1;
for(int j=0; j<this.getSize(); j++){
if(this.getNodeList().get(j).getHead().equals(linked_node)){
index = j;
break;
}
}
//3.2.2 如果找到,则在此NodeLinkedList中删除head
if(index != -1){
NodeLinkedList nll = this.getNodeList().get(index);
nll.remove(head); //删除head,并且size-1;
nll.getHead().setDegree((nll.getSize() - 1)); //头结点的度就等于,以它为头的链表长度-1
//3.2.3 然后判断nll的头结点的度在删除head后,是否小于k
//如果小于,且nll不在abandonedNodes中,则将其添加到abandonedNodes中
if(nll.getHead().getDegree() < k && !Utils.contains(abandonedNodes, nll)){
abandonedNodes.add(nll);
}
}
//3.2.3 查看head的下一个邻接点
linked_node = linked_node.getNext();
}
//3.3在图中删除以head为头结点的NodeLinkedList
for(int j=0; j<this.getSize(); j++){
//找到,则删除
if(this.getNodeList().get(j).getHead().equals(head)){
this.getNodeList().remove(j);
}
}
}
}
相似性约束简化算法
介绍:相邻的两点计算相似度,如果相似度满足条件,则两点满足相似性约束。在本实验中将两点间的距离作为相似度,如果两点间的距离小于相似性阈值r则两点相似,否则,不相似。
目的:对一个图运行此算法后,图中任意一条边相连的两点都是相似的。
注意:此算法的删除操作只删除了边,不会删除图中的点;我们使用邻接链表结构来存储图,这种结构会将一条边存储两次,具体表现为:如果存在边(u,v),则在以u点为头结点的链表中存储此边,在以v点为头结点的链表中同样会存储此边。
代码
/**
* 删除g中每个NodeLinkedList中与头结点不相似的节点
* 即删除g中不相似的边
* @param r
*/
public void simplifyR(double r){
//遍历图中每个NodeLinkedList,删除nll中与头结点不相似的节点
for(int i=0; i<this.size; i++){
/*
//获取nll
NodeLinkedList temp_nll = this.nodeList.get(i);
//删除nll中与头结点不相似的节点
temp_nll.deleteDissim(r);
*/
this.nodeList.get(i).deleteDissim(r); //删除nll中与头结点不相似的节点
}
}
代码中只是删除了存储结构中的点,不会删除实际图结构中的点,其实质是删除边。
deleteDissim( r )在1.4节构建空间网络图给出的链表类中。
连通子图提取算法
介绍:就是判断一个图是否连通,如果不连通则将图中的连通子图提取出来分别存储。如果是单纯的提取算法并不会改变图结构。
原因:依据原始数据构造图,图可能是不连通的,而是由多个连通图构成的;由于上面介绍的两种简化算法通过删除点和删除边对图结构造成了改变,可能会使一个连通图变得不连通;在多个连通子图上分别运行 社区枚举算法 比在一个大图上运行此算法效率要高的多。
目的:得到一个连通图集合。
算法思想:采用了广度优先搜素的思想,可以搜索到的都是连通的喽。深度优先搜索也可以。
代码
/**
* g由多个紧密子图构成,这些紧密子图之间没有边相连,获取每一个紧密子图
* @param g
*/
public void getSubgraphs(Graph g){
//1.首先建立一个数组,保存对应索引的节点对应的紧密子图序号
int size = g.getSize();
int[] position = new int[size];
for(int i=0; i<size; i++){
position[i] = -1; //初始值为-1,表示该点还没有被划分
}
//3.对每个点进行划分
int subgraph_num = 0; //子图序号从0开始
for(int i=0; i<g.getSize(); i++){
if(position[i] == -1){ //表示第i个顶点还没有被划分
//使用BFS思想,将第i个顶点所在的连接图提取出来
Queue<Integer> queue = new LinkedList<Integer>();
queue.offer(i); //将点i添加到队列中
position[i] = subgraph_num; //对index对应的点进行划分
while(!queue.isEmpty()){ //如果队列不为空
int index = queue.element(); //获取对列头元素
queue.remove(); //删除对列头,即将index从队列中删除
//找到第index节点的邻接点,并将对应节点序号添加到队列中
Node temp_node = g.getNodeList().get(index).getHead();
while(temp_node.getNext() != null){
temp_node = temp_node.getNext(); //获取一个相邻节点
//找此相邻节点的序号
for(int j=0; j<g.getSize(); j++){
Node linked_node = g.getNodeList().get(j).getHead();
//如果一点没有被访问过,且为对应的相邻点,则将该序号加入到队列中
if(position[j]==-1 && temp_node.equals(linked_node)){
queue.offer(j);
position[j] = subgraph_num;
}
}
}
}
//找到第i个顶点所在的连接图(即第subgraph_num个连接图)后,再找第subgraph_num+1个
subgraph_num++;
}
}
//System.out.println(subgraph_num);
//4.划分信息已在position中,构造出子图,获得子图序列
this.cores = new ArrayList<Graph>();
for(int i=0; i<subgraph_num; i++){
//初始化第i个子图
Graph temp_g = new Graph();
//构造第i个子图
for(int j=0; j<size; j++){
if(position[j] == i){
temp_g.setNodeList(g.getNodeList().get(j)); //构造子图
}
}
//将第i个子图添加到cores中
this.cores.add(temp_g);
}
this.size = this.cores.size();
}