[data structure]一些证明

proof of Kosaraju

该证明存在瑕疵,我只是为了方面我自己使用,所以有些符号和名词我并没有明说定义,此外在某些地方你可以将s当做v,而v当做w。

以下证明需要结合算法4的对应代码

public class KosarajuSharirSCC {
    private boolean[] marked;     // marked[v] = has vertex v been visited?
    private int[] id;             // id[v] = id of strong component containing v
    private int count;            // number of strongly-connected components


    public KosarajuSharirSCC(Digraph G) {

        // compute reverse postorder of reverse graph
        DepthFirstOrder dfs = new DepthFirstOrder(G.reverse());

        // run DFS on G, using reverse postorder to guide calculation
        marked = new boolean[G.V()];
        id = new int[G.V()];
        for (int v : dfs.reversePost()) {
            if (!marked[v]) {
                dfs(G, v);
                count++;
            }
        }

        // check that id[] gives strong components
        assert check(G);
    }
    // DFS on graph G
    private void dfs(Digraph G, int v) {
        marked[v] = true;
        id[v] = count;
        for (int w : G.adj(v)) {
            if (!marked[w]) dfs(G, w);
        }
    }

    public int count() {
        return count;
    }


    public boolean stronglyConnected(int v, int w) {
        validateVertex(v);
        validateVertex(w);
        return id[v] == id[w];
    }

    public int id(int v) {
        validateVertex(v);
        return id[v];
    }

    // does the id[] array contain the strongly connected components?
    private boolean check(Digraph G) {
        TransitiveClosure tc = new TransitiveClosure(G);
        for (int v = 0; v < G.V(); v++) {
            for (int w = 0; w < G.V(); w++) {
                if (stronglyConnected(v, w) != (tc.reachable(v, w) && tc.reachable(w, v)))
                    return false;
            }
        }
        return true;
    }

    // throw an IllegalArgumentException unless {@code 0 <= v < V}
    private void validateVertex(int v) {
        int V = marked.length;
        if (v < 0 || v >= V)
            throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1));
    }

    public static void main(String[] args) {
        In in = new In(args[0]);
        Digraph G = new Digraph(in);
        KosarajuSharirSCC scc = new KosarajuSharirSCC(G);

        // number of connected components
        int m = scc.count();
        StdOut.println(m + " strong components");

        // compute list of vertices in each strong component
        Queue<Integer>[] components = (Queue<Integer>[]) new Queue[m];
        for (int i = 0; i < m; i++) {
            components[i] = new Queue<Integer>();
        }
        for (int v = 0; v < G.V(); v++) {
            components[scc.id(v)].enqueue(v);
        }

        // print results
        for (int i = 0; i < m; i++) {
            for (int v : components[i]) {
                StdOut.print(v + " ");
            }
            StdOut.println();
        }

    }
}

命题:设 G R G^R GR G G G的反向图,将 G R G^R GR的逆后序遍历顺序放到dfs( G G G, G R 逆后序遍历顺序 G^R逆后序遍历顺序 GR逆后序遍历顺序)中,则构造函数中的每一次递归调用的顶点都标记了同一个SCC中。

设v为初始顶点, w为dfs递归中的adj(v),要证上述命题,实际上就是证:
w和v是强连通的,每个w都会在构造函数的dfs(G,v)中被访问到。 且构造函数中dfs(G,v)所到达的顶点w都和v强连通。

该证明的核心是,1、如何保证若v和w是强连通的,构造函数dfs(G,v)一定能访问到所有的w。2、所有被构造函数dfs(G,v)访问w一定是跟v强连通的

第一部分

第一部分的证明用反证法:若dfs(G,v)不访问w,因为v->w的关系,所以只能认为w是被其他v0或v1什么的访问过;因为w跟v强连通,所以dfs(G,w)一定能访问到v,但v已经被访问过了,出现矛盾。所以第一部分得证;

第二部分

第二部分,因为已经证明v一定能到达w,此时只需要证明w一定能到达v即可。后者等价于 v G R − > w G R v_{G^R} -> w_{G^R} vGR>wGR

考虑以下这个情况,对于 w G − > v G w_G ->v_G wG>vG来说,取反图则为 v G R − > w G R v_{G^R}->w_{G^R} vGR>wGR,此时的逆后序排序就是 v G R v_{G^R} vGR, w G R w_{G^R} wGR,所以我们会在构造函数中使用order.reversePost();

当进入dfs中时, v G R v_{G^R} vGR w G R w_{G^R} wGR的递归关系有两种:

第一种这意味着 w G R w_{G^R} wGR连接不到 v G R v_{G^R} vGR,这等价于 v G v_G vG连接不到 w G w_G wG;由于在第一部分中已经证明了如果v,w强连接,则v一定能访问到w,所以第一种关系是不存在的。

dfs(w)
	...
 w done
...
dfs(v)
	...
v done

第二种意味着 v G R v_{G^R} vGR-> w G R w_{G^R} wGR,等价于 w G − > v G w_G -> v_G wG>vG

dfs(v)
	...
	dfs(w)
		...
	w done
	...
v done

此时 v G − > w G v_G -> w_G vG>wG w G − > v G w_G -> v_G wG>vG,结合第一部分的证明,此时该算法一定能够保证

  1. 构造函数一定能访问到所有与v相连通的w
  2. 构造函数所访问的所有w一定跟v是相连通的

命题P,中文420,最短路径的最优性条件

命题:IFF v 到 w的任意一条边 e,这些值满足 d i s t T o [ w ] < = d i s t T o [ v ] + e . w e i g h t ( ) distTo[w]<=distTo[v]+e.weight() distTo[w]<=distTo[v]+e.weight()时,他们是最短路径长度。

必要性:假设 d i s t T o [ w ] distTo[w] distTo[w]是s到w的最短路径,若存在s到w中的任意v有 d i s t T o [ w ] > d i s t T o [ v ] + e . w e i g h t ( ) distTo[w]>distTo[v]+e.weight() distTo[w]>distTo[v]+e.weight(),则意味着s到w的路径需要改道,即从s要经过v,然后才到w。此时 d i s t T o [ w ] distTo[w] distTo[w]就不是最短路径。

充分性:假设s与w是可达的,且最短路径为s->v0->v1->…vk->w则有以下式子

d i s t T o [ v 1 ] < = d i s t T o [ s ] + e 1. w e i g h t ( ) ; d i s t T o [ v 2 ] < = d i s t T o [ v 1 ] + e 2. w e i g h t ( ) ; . . . d i s t T o [ w ] = d i s t T o [ v k ] < = d i s t T o [ v k − 1 ] + e k . w e i g h t ( ) ; distTo[v1]<=distTo[s] + e1.weight();\\ distTo[v2]<=distTo[v1] + e2.weight();\\ ...\\ distTo[w] = distTo[vk]<=distTo[vk-1] + ek.weight(); distTo[v1]<=distTo[s]+e1.weight();distTo[v2]<=distTo[v1]+e2.weight();...distTo[w]=distTo[vk]<=distTo[vk1]+ek.weight();
因为distTo[s]=0,则distTo[v1] <= e1.weight();则distTo[v2] <= e1.weigth() + e2.weight();所以最终有
d i s t T o [ w ] < = e 1. w e i g h t ( ) + e 2. w e i g h t ( ) + . . . + e k . w e i g h t ( ) ; distTo[w]<=e1.weight() + e2.weight() + ... + ek.weight(); distTo[w]<=e1.weight()+e2.weight()+...+ek.weight();

命题Q,中文P421,通用最短路径算法(非负权重)

命题:将distTo[s]初始化为0,其他则为无穷大,然后继续如下操作: r e l a x i n g relaxing relaxing G中任意边,直到不存在有效边为止。则对于任意从s可达的顶点w,在进行上述操作后,distTo[w]的值就是s到w的最短路径。

对于s可达的任意w,当distTo[w]是无穷大时,则必然会进行 r e l a x i n g relaxing relaxing操作,即存在e是有效边,最终distTo[w] = distTo[v] + e.weight(), v是s->w的某一处顶点;而对于非无穷大的distTo[w]来说,如果存在有效边,则其也会更新,且distTo[w_new] < distTo[w_old]。当有效边不再存在时,则意味着命题P成立。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值