Matriz crítica y lista de adyacencia para la amplitud y profundidad del recorrido de gráficos

Simplemente publíquelo usted mismo, el código se puede copiar y ejecutar directamente

Atravesar primero la anchura

De manera similar a un proceso de búsqueda jerárquica, el recorrido de amplitud requiere el uso de una cola para mantener el orden de los nodos visitados para acceder a los nodos adyacentes de estos nodos en este orden.

El algoritmo específico se expresa de la siguiente manera:

  1. Visite el nodo inicial v y marque el nodo v como visitado.
  2. Nodo v en la cola
  3. Cuando la cola no está vacía, continúe ejecutándose, de lo contrario el algoritmo termina.
  4. Salga de la cola y obtenga el nodo principal u.
  5. Encuentre el primer nodo adyacente w del nodo u.
  6. Si el nodo vecino w del nodo u no existe, vaya al paso 3; de lo contrario, repita los tres pasos siguientes:
    1). Si el nodo w no ha sido visitado, visite el nodo w y márquelo como visitado.
    2). El nodo w entra en la cola
    3). Busque el siguiente nodo adyacente w después del nodo adyacente w del nodo u, vaya al paso 6.

Profundidad-primer recorrido

El primer recorrido en profundidad, comenzando desde el nodo de acceso inicial, sabemos que el nodo de acceso inicial puede tener múltiples nodos adyacentes, la estrategia del primer recorrido en profundidad es visitar primero el primer nodo adyacente y luego usar este nodo adyacente para visitar Como nodo inicial, visite su primer nodo adyacente. En resumen, se puede decir que cada vez que se visita el nodo actual, se visita primero el primer nodo adyacente del nodo actual.

Podemos ver de esto que tal estrategia de acceso es preferir cavar más profundo verticalmente, en lugar de visitar horizontalmente todos los nodos adyacentes de un nodo.

El algoritmo específico se expresa de la siguiente manera:

  1. Visite el nodo inicial v y marque el nodo v como visitado.
  2. Encuentre el primer nodo adyacente w del nodo v.
  3. Si w existe, continúe con 4, de lo contrario el algoritmo termina.
  4. Si no se visita w, realice la recursividad transversal primero en profundidad en w (es decir, trate w como otra v, y luego continúe con el paso 123).
  5. Encuentre el siguiente nodo adyacente del nodo adyacente w del nodo v, vaya al paso 3.

Implementación de matriz crítica

Sabemos que para representar nodos, podemos usar una matriz unidimensional para representar, pero para la relación entre nodos y nodos, no podemos simplemente usar una matriz unidimensional para representar, podemos usar una matriz bidimensional para representar , Que es un método de representación matricial.

Suponemos que A es esta matriz bidimensional, entonces un elemento aij en A no solo refleja la relación entre el nodo vi y el nodo vj, sino que también el valor de aij puede representar el tamaño del peso.

//构造器
public Graph(int n) {
	//初始化矩阵和vertexList
	edges = new int[n][n];
	vertexList = new ArrayList<String>(n);
	numOfEdges = 0;
	
}

//得到第一个邻接结点的下标 w 
/**
 * 
 * @param index 
 * @return 如果存在就返回对应的下标,否则返回-1
 */
public int getFirstNeighbor(int index) {
	for(int j = 0; j < vertexList.size(); j++) {
		if(edges[index][j] > 0) {
			return j;
		}
	}
	return -1;
}
//根据前一个邻接结点的下标来获取下一个邻接结点
public int getNextNeighbor(int v1, int v2) {
	for(int j = v2 + 1; j < vertexList.size(); j++) {
		if(edges[v1][j] > 0) {
			return j;
		}
	}
	return -1;
}

//深度优先遍历算法
//i 第一次就是 0
private void dfs(boolean[] isVisited, int i) {
	//首先我们访问该结点,输出
	System.out.print(getValueByIndex(i) + "->");
	//将结点设置为已经访问
	isVisited[i] = true;
	//查找结点i的第一个邻接结点w
	int w = getFirstNeighbor(i);
	while(w != -1) {//说明有
		if(!isVisited[w]) {
			dfs(isVisited, w);
		}
		//如果w结点已经被访问过
		w = getNextNeighbor(i, w);
	}
	
}

//对dfs 进行一个重载, 遍历我们所有的结点,并进行 dfs
public void dfs() {
	isVisited = new boolean[vertexList.size()];
	//遍历所有的结点,进行dfs[回溯]
	for(int i = 0; i < getNumOfVertex(); i++) {
		if(!isVisited[i]) {
			dfs(isVisited, i);
		}
	}
}

//对一个结点进行广度优先遍历的方法
private void bfs(boolean[] isVisited, int i) {
	int u ; // 表示队列的头结点对应下标
	int w ; // 邻接结点w
	//队列,记录结点访问的顺序
	LinkedList queue = new LinkedList();
	//访问结点,输出结点信息
	System.out.print(getValueByIndex(i) + "=>");
	//标记为已访问
	isVisited[i] = true;
	//将结点加入队列
	queue.addLast(i);
	
	while( !queue.isEmpty()) {
		//取出队列的头结点下标
		u = (Integer)queue.removeFirst();
		//得到第一个邻接结点的下标 w 
		w = getFirstNeighbor(u);
		while(w != -1) {//找到
			//是否访问过
			if(!isVisited[w]) {
				System.out.print(getValueByIndex(w) + "=>");
				//标记已经访问
				isVisited[w] = true;
				//入队
				queue.addLast(w);
			}
			//以u为前驱点,找w后面的下一个邻结点
			w = getNextNeighbor(u, w); //体现出我们的广度优先
		}
	}
	
} 

//遍历所有的结点,都进行广度优先搜索
public void bfs() {
	isVisited = new boolean[vertexList.size()];
	for(int i = 0; i < getNumOfVertex(); i++) {
		if(!isVisited[i]) {
			bfs(isVisited, i);
		}
	}
}

//图中常用的方法
//返回结点的个数
public int getNumOfVertex() {
	return vertexList.size();
}
//显示图对应的矩阵
public void showGraph() {
	for(int[] link : edges) {
		System.err.println(Arrays.toString(link));
	}
}
//得到边的数目
public int getNumOfEdges() {
	return numOfEdges;
}
//返回结点i(下标)对应的数据 0->"A" 1->"B" 2->"C"
public String getValueByIndex(int i) {
	return vertexList.get(i);
}
//返回v1和v2的权值
public int getWeight(int v1, int v2) {
	return edges[v1][v2];
}
//插入结点
public void insertVertex(String vertex) {
	vertexList.add(vertex);
}
//添加边
/**
 * 
 * @param v1 表示点的下标即使第几个顶点  "A"-"B" "A"->0 "B"->1
 * @param v2 第二个顶点对应的下标
 * @param weight 表示 
 */
public void insertEdge(int v1, int v2, int weight) {
	edges[v1][v2] = weight;
	edges[v2][v1] = weight;
	numOfEdges++;
}

Implementación de lista de adyacencia

/**
 * @Author: lyq
 * @Description: 邻接表 本来是 无向图 写成了单向图
 * 但是问题不大 只需要修改addEdges 就可以了 或者在添加边的时候自行添加一下
 * 注意我的边 不包含0这条边的
 * @Date:Create:in 2020/3/7 15:10
 */
public class GraphByList {

    private Node[] graph;

    public GraphByList(int size) {
        this.graph = new Node[size];
        //将这个图的顶点填充 以1 2 3 来填
        for (int i = 0; i < size; i++) {
            this.graph[i] = new Node(i + 1);
        }
    }

    public static void main(String[] args) {
        GraphByList graph = new GraphByList(8);
        graph.addEdges(1, 2, 1);
        graph.addEdges(1, 3, 1);
        graph.addEdges(2, 5, 1);
        graph.addEdges(2, 1, 1);
        graph.addEdges(2, 4, 1);
        graph.addEdges(4, 8, 1);
        graph.addEdges(4, 2, 1);
        graph.addEdges(3, 6, 1);
        graph.addEdges(3, 7, 1);
        graph.addEdges(3, 1, 1);
        graph.addEdges(5, 8, 1);
        graph.addEdges(5, 2, 1);
        graph.addEdges(6, 7, 1);
        graph.addEdges(6, 3, 1);
        graph.addEdges(8, 4, 1);
        graph.addEdges(8, 5, 1);
        graph.addEdges(7, 6, 3);
        graph.addEdges(7, 3, 1);
        graph.printGraph();
//        graph.dfs();
//        graph.dfsRecursion();
//        graph.bfs();
        graph.bfsByQueue(1);
    }



    /**
     * 深度优先,每访问到了一个node 就 得到first 然后转到node 往复,直到 graph里的结点
     * 被访问完 所以需要一个记录已经访问了的结点的集合 可以使用visited
     * 非递归
     */
    public void dfs() {
        //创建一个visit的数组 1表示已经访问
        int[] visited = new int[this.graph.length];
        //首先要得到 一个node的first
        for (int i = 0; i < this.graph.length; i++) {
            if (visited[i] != 1) {
                //说明当前结点还未被访问
                System.out.print(i + 1 + " ");
                visited[i] = 1;
                Edge first = this.graph[i].getFirst();
                while (first != null && visited[first.getNode() - 1] != 1) {
                    //有边,而且这条边对的结点没有被访问过, 那么就访问
                    visited[first.getNode() - 1] = 1;
                    System.out.print(first.getNode() + " ");
                    //循环
                    first = this.graph[first.getNode() - 1].getFirst();
                }
            }
        }
    }

    /**
     * 深度搜索 递归写法
     */
    public void dfsRecursion() {
        //创建一个visit的数组 1表示已经访问
        int[] visited = new int[this.graph.length];
        for (int i = 0; i < this.graph.length; i++) {
            if (visited[i] != 1) {
                //说明没有被访问过,那么就访问 访问放到递归体里面去
                dfsRecursion(i, visited);
            }
        }
    }

    /**
     * 递归子体
     *
     * @param i
     * @param visited
     */
    private void dfsRecursion(int i, int[] visited) {
        //先访问
        System.out.print(i + 1 + " ");
        visited[i] = 1;
        //然后得到这个结点的first
        Edge first = this.graph[i].getFirst();
        if (first == null) {
            //判断是否还有临界边
            return;
        }
        int next = first.getNode() - 1;
        if (visited[next] != 1) {
            //说明first 没有被访问过 那么就递归访问
            dfsRecursion(next, visited);
        } else {
            //说明没有邻接点了,就退出
            return;
        }
    }

    public void addEdges(int node, int edge, int weight) {
        node = node - 1;
        if (this.graph[node].getFirst() == null) {
            this.graph[node].setFirst(new Edge(edge, weight));
        } else {
            Edge p = this.graph[node].getFirst();
            //找到最后一条边 最后一条边的 p.next == null
            while (p.getNext() != null) {
                p = p.getNext();
            }
            p.setNext(new Edge(edge, weight));
        }
    }


    //广度优先遍历 思考:先拿到一个node,然后就需要遍历这个node的每一个Edge , 
   // 然后每遍历一次就置相应 的结点已经访问就行
    //非递归
    //存在的问题 我这个写法 是访问过的结点就不访问了,正规的网上的写法,是将访问的结点放入一个队列里面,每次出队一个进行递归访问
    //其实我有一个想法,如果存在一个孤立的点呢? 我这个方法就可以解决存在孤立的点,我仍然能够访问到它
    //先不谈性能问题
    public void bfs() {
        //创建一个visit的数组 1表示已经访问
        int[] visited = new int[this.graph.length];
        for (int i = 0; i < this.graph.length; i++) {
            //判断这个结点是否已经访问
            if (visited[i] != 1) {
                //未访问 就循环访问edges 不过先得访问这个点
                System.out.print(i + 1 + " ");
                Edge first = this.graph[i].getFirst();
                //判空,这个结点且未被访问过
                while (first != null) {
                    if (visited[first.getNode() - 1] != 1) {
                        //那就访问
                        System.out.print(first.getNode() + " ");
                        visited[first.getNode() - 1] = 1;
                    }
                    first = first.getNext();
                }
            }
        }
    }


    //算法描述为:选择一个初始结点 将初始结点访问到的结点入队,初始结点访问完,就出队一个结点,访问,循环
    //要满足既能够在循环里添加,还要保证能够判断非空 队列是一个效果最好的数据结构,懒得自己写了,就用了java里内置的一个stack ,其他的队列没学过
    public void bfsByQueue(int init) {
        //创建一个visit的数组 1表示已经访问
        int[] visited = new int[this.graph.length];
        System.out.print(init + " ");
        visited[init-1]=1;
        //嘿嘿 用stack 来实现
        Stack<Integer> tmp = new Stack<>();

        tmp.push(init);

        while (!tmp.isEmpty()){
            //取出来
            Integer next = tmp.pop();
            Edge first = this.graph[next-1].getFirst();
            //访问到的第一个结点
            while (first!=null){
                int i = first.getNode() - 1;
                if (visited[i]!=1){
                    //没有被访问过
                    System.out.print(first.getNode()+" ");
                    visited[i]=1;
                    //同时把first 入队
                    tmp.push(i);
                }

                first = first.getNext();
            }
           // iterator=hashSet.iterator();
        }


    }

    public void printGraph() {
        for (int i = 0; i < this.graph.length; i++) {
            Edge p = this.graph[i].getFirst();
            while (p != null) {
                System.out.print(i + 1 + "->" + p.getNode() + " ");
                p = p.getNext();
            }
            System.out.println();
        }
    }
}


class Node {
    private int nodeNum;
    private Edge first;

    public Node(int nodeNum) {
        this.nodeNum = nodeNum;
    }

    public Node() {
    }

    public int getNodeNum() {
        return nodeNum;
    }

    public void setNodeNum(int nodeNum) {
        this.nodeNum = nodeNum;
    }

    public Edge getFirst() {
        return first;
    }

    public void setFirst(Edge first) {
        this.first = first;
    }
}

class Edge {
    private int weight;
    private int node;
    private Edge next;

    public Edge(int node, int weight) {
        this.weight = weight;
        this.node = node;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public int getNode() {
        return node;
    }

    public void setNode(int node) {
        this.node = node;
    }

    public Edge getNext() {
        return next;
    }

    public void setNext(Edge next) {
        this.next = next;
    }
}
Publicado 37 artículos originales · ganado elogios 6 · vistas 4657

Supongo que te gusta

Origin blog.csdn.net/littlewhitevg/article/details/104720166
Recomendado
Clasificación