无向图:学习整理

无向图

使用的数据结构:

  • 邻接表:使用一个以顶点为索引的列表数组 – 每个元素都是和该顶点相邻的顶点列表
    • 空间和V+E成正比
    • 添加一条条边所需的时间为常数
    • 遍历顶点v的所有相邻顶点所需的时间和v的度数成正比

基本数据结构:

public class Graph {

    private final int V;
    private int E;
    private Bag<Integer>[] adj; //邻接表

    public Graph(int V) {
        this.V = V;
        this.E = 0;
        // 充当一条链表,存储相邻的节点
        Bag<Integer>[] bags = (Bag<Integer>[]) new Bag[V];
        for (int i = 0; i < V; i++) {
            adj[i] = new Bag<>();
        }
    }

    public int E() {
        return E;
    }

    public int V() {
        return V;
    }

    public void addEdge(int v, int w) {
        adj[v].add(w);
        adj[w].add(v);
        E++;
    }
	// 遍历相邻的节点
    public Iterable<Integer> adj(int v) {
        return adj[v];
    }
}

深度优先搜索(DFS)

核心方法:访问一个顶点时

  • 将它标记为已访问
  • 递归地访问它的所有没有被标记过的邻近顶点
  • DFS中每条边总会被访问量词,且在第二次时会发现这个顶点已经被标记过
public class DepthFirstPaths {
    private boolean[] marked;
    // 比如需要从3开始遍历到4,5;5找到后返回到,再从3到4
    // 那么edgeTo[5] = 3; edgeTo[4] = 3; -- 都是从3到这两个点
    private int[] edgeTo; // 从起点到一个顶点的已知路径上的最后一个顶点
    private final int s;

    public DepthFirstPaths(Graph G, int s) {
        marked = new boolean[G.V()];
        edgeTo = new int[G.V()];
        this.s = s;
        dfs(G, s);
    }

    private void dfs(Graph G, int v) {
        // 访问到该顶点,置为true
        marked[v] = true;
        for (int w : G.adj(v)) {
            // 如果没有访问过
            if (!marked[w]) {
                // 将路径添加到数组中
                edgeTo[w] = v;
                dfs(G, v);
            }
        }
    }

    public boolean hasPathTo(int v) {
        return marked[v];
    }

    public Iterable<Integer> pathTo(int v) {
        if(!hasPathTo(v)) return null;
        Stack<Integer> path = new Stack<>();
        // 根据路径点,依次入栈,最后将起点入栈,后进先出弹出。得到路径
        for (int i = v; i != s ; i = edgeTo[i]) {
            path.push(i);
        }
        path.push(s);
        return path;
    }

}

DFS应用:检测连通分量:

// 记录连通分量
public class CC {
    private boolean[] marked;
    // 记录顶点所属的连通分量的id
    private int[] id;
    private int count;

    public CC(Graph G) {
        marked = new boolean[G.V()];
        id = new int[G.V()];
        for (int i = 0; i < G.V(); i++) {
            if (!marked[i]) {
                // 执行完一次DFS,遍历这个点可达的所有顶点。
                // 这些顶点构成了一个连通分量
                dfs(G, i);
                count++;
            }
        }
    }

    private void dfs(Graph G, int s) {
        marked[s] = true;
        id[s] = count;
        for (int w : G.adj(s)) {
            if (!marked[s]) {
                dfs(G, w);
            }
        }
    }

    public boolean connected(int v, int w){
        // 如果所属的连通分量id相同,则证明v和w相互连通
        return id[v] == id[w];
    }
}

广度优先搜索 – BFS

比喻:广度优先搜索就像是一组人在一起朝各个方向走这座迷宫。

通过栈(LIFO)来描述DFS,用队列(FIFO)来描述BFS

算法:

  • 取队列中的一下一个顶点并标记它
  • 将于v相邻的所有未被标记过的顶点加入队列,并标记
  • 注意入队的顺序是有严格要求的,因为是将结点的邻接结点进行的入队
private void bfs(Graph G, int s) {
    // 用LinkedList来实现队列操作
    Queue<Integer> queue = new LinkedList<>();
    marked[s] = true;
    // 加入队首
    queue.offer(s);
    while (!queue.isEmpty()) {
        // 取出队尾元素
        int v = queue.poll();
        for (int w : G.adj(v)) {
            // 将所有未被标记的元素加入队列
            if (!marked[w]) {
                marked[w] = true;
                queue.offer(w);
                edgeTo[w] = v;
            }
        }
    }
}

你可能感兴趣的:(LeetCode)