实际背景:找出交易链中存在环的情况,目的是找出存在非法交易例如洗钱。
核心思想:
注意:每次遍历时都会先删除以遍历的路径然后加上新的路径,例如(ab,abe,abg,abf)则ab删除新的abe,abg,abf存在集合中。当形成环时则存放在一个新的集合中,直到原来的集合中不存在元素,则执行完毕。
其中要考虑
1.回溯路径如:ab,ba
2.重复环:abgh,hgba
3.不包含指定节点的环 : ebf(图中未画出)
4.递归算法会影响性能,因为节点很多。
java代码实现:
package com.cy.graph;
import java.util.*;
/**
* Search all rings in undirected graphs
*
* @author chenyue
* @date 2019/10/26
*/
public class GraphSearchRing {
public Integer numVertex;
public Integer numEdge;
public VertexNode[] vertexNodes;
public static Integer firstNode;
public GraphSearchRing(VertexNode[] vertexNodes) {
this.numEdge = 0;
this.numVertex = vertexNodes.length;
this.vertexNodes = vertexNodes;
}
/**
* Insert edge for node
*
* @param start
* @param end
*/
public void insertEdge(Integer start, Integer end) {
VertexNode vertexNode = vertexNodes[start];
EdgeNode edgeNode = new EdgeNode(end, null);
EdgeNode firstEdgeNode = vertexNode.firstEdge;
if (firstEdgeNode == null) {
vertexNode.firstEdge = edgeNode;
} else {
edgeNode.next = firstEdgeNode;
vertexNode.firstEdge = edgeNode;
}
}
/**
* Implementation of non-Recursive algorithm
*
* @param root
*/
public void findRing(int root) {
VertexNode node = vertexNodes[root];
EdgeNode edge = node.firstEdge;
List> allLinklist = new ArrayList<>();
List nodeListResult = new ArrayList<>();
// traversal firstnode reference edge
while (edge != null) {
if (vertexNodes[edge.adjvex].firstEdge != null) {
ArrayList nodeList = new ArrayList();
nodeList.add(firstNode);
nodeList.add(edge.adjvex);
allLinklist.add(nodeList);
}
edge = edge.next;
}
//eaxmple : ab abg,abgh,ebgha ,if last number equal firstnode(0) is ring
while (true) {
if (allLinklist.size() == 0) {
break;
}
Iterator> iterator = allLinklist.listIterator();
while (iterator.hasNext()) {
ArrayList nodeList = iterator.next();
int size = nodeList.size();
//is ring
if (nodeList.get(size - 1).equals(firstNode)) {
StringBuilder buffer = new StringBuilder();
Boolean exit = false;
for (Integer integer : nodeList) {
buffer.append(integer);
}
//distinct example: 0123 3210
for (String nodeaList : nodeListResult) {
if (nodeaList.equals(buffer.reverse().toString())) {
exit = true;
}
}
if (!exit) {
nodeListResult.add(buffer.toString());
}
}
// remove ab repalce abe,abg,abf
iterator.remove();
node = vertexNodes[nodeList.get(size - 1)];
EdgeNode edgeNode = node.firstEdge;
//traversal graph next edge
while (edgeNode != null) {
//exxclude the same path. example : 10 01,ab ba
if (!edgeNode.adjvex.equals(nodeList.get(size - 2)) && !node.id.equals(firstNode)) {
ArrayList newNodeList = new ArrayList<>();
nodeList.forEach(t -> {
newNodeList.add(t);
});
//example : abe abg
newNodeList.add(edgeNode.adjvex);
((ListIterator>) iterator).add(newNodeList);
}
edgeNode = edgeNode.next;
}
}
}
nodeListResult.forEach(System.out::println);
}
/**
* print graph structure for mid traversal
*
* @param graph
*/
public static void printGraphStructure(GraphSearchRing graph, VertexNode[] vertexNodes) {
for (int i = 0; i < graph.numVertex; i++) {
VertexNode vertexNode = graph.vertexNodes[i];
EdgeNode firstEdge = vertexNode.firstEdge;
EdgeNode currentEdge = firstEdge;
System.out.print(vertexNode.data + ":");
while (currentEdge != null) {
int vertexNodeIndex = currentEdge.adjvex;
System.out.print("->" + vertexNodes[vertexNodeIndex].data);
currentEdge = currentEdge.next;
}
System.out.println();
}
}
public static void main(String[] args) {
VertexNode[] vertexNodes = {
new VertexNode(0, "a", null),
new VertexNode(1, "b", null),
new VertexNode(2, "c", null),
new VertexNode(3, "d", null),
new VertexNode(4, "e", null),
new VertexNode(5, "f", null),
new VertexNode(6, "g", null),
new VertexNode(7, "h", null),
new VertexNode(8, "i", null),
new VertexNode(9, "j", null),
};
GraphSearchRing graph = new GraphSearchRing(vertexNodes);
//Init edge equal 11
graph.insertEdge(0, 8);
graph.insertEdge(0, 9);
graph.insertEdge(0, 2);
graph.insertEdge(0, 7);
graph.insertEdge(0, 1);
graph.insertEdge(1, 4);
graph.insertEdge(1, 5);
graph.insertEdge(1, 6);
graph.insertEdge(6, 7);
graph.insertEdge(3, 9);
graph.insertEdge(3, 4);
//Undirected graph symmetry
graph.insertEdge(8, 0);
graph.insertEdge(9, 0);
graph.insertEdge(2, 0);
graph.insertEdge(7, 0);
graph.insertEdge(1, 0);
graph.insertEdge(4, 1);
graph.insertEdge(5, 1);
graph.insertEdge(6, 1);
graph.insertEdge(7, 6);
graph.insertEdge(9, 3);
graph.insertEdge(4, 3);
//Filter node for edge equal 1
//in fact this method is not used
/*for (int i = 0; i < vertexNodes.length; i++) {
if (vertexNodes[i].firstEdge.next == null) {
VertexNode vertexNode = vertexNodes[i];
vertexNode.firstEdge = null;
vertexNodes[i] = vertexNode;
}
}*/
//0 equal a . we will find the firstnode is a.
firstNode = 0;
graph.findRing(firstNode);
}
}
package com.cy.graph;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
/**
* 表头节点
*
* @author cy
* @date 2019/10/26
*/
public class VertexNode {
/**
* 结点序号
* */
public Integer id;
/**
* 结点信息
* */
public String data;
/**
* 第一条边
* */
public EdgeNode firstEdge;
}
package com.cy.graph;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
/**
* 边节点
*
* @author cy
* @date 2019/10/26
*/
public class EdgeNode {
/**
* 每条边的下一结点
* */
public Integer adjvex;
/**
* 下一个边结点
* */
public EdgeNode next;
}
输出:
01670
093410
07614390
也可以将输出的数字通过vertexNodes转化为字符
最后感谢我们架构师设计的算法还有他的图。hhhh~~~