发现笔记转过来,没有图的~~~~~~~~~~~悲剧,给出共享笔记链接

https://www.evernote.com/pub/yanbinliu/algorithm

很久之前就在coursera看到Robert的算法课程,当时太懒惰,没有跟着学习。前两天逛coursera偶然发现又开课了,于是毫不犹豫跟着

走起。

基本上每一个课程开始,总会有introduction blabla之类的。Robert的算法课程,上来不到10分钟的欢迎致辞,然后就直接从一个例子讲起,来分析一个问题。

建模,找到算法求解,找不足,改进,迭代直到满意为止。

首先给出的问题是dynamic connectivity,动态连通问题,就是给出譬如

(1,2)

(7,3)

(4,9)

……

等连接在一起的点对,

然后判断,一对(p,q)之间是否存在连通路径。

为了解决这个问题,主要分为两步:

1.将给出的点对连接起来,即Union的过程

2.判断一个对点(p,q)之间是否有连通路径

一个比较直观的应用就是迷宫…………,不过当然不止如此,譬如数字图像,网路,电路等等。

为了简化问题,忽略其他无关因素,只考虑0-9这10个数字。

首先,给出的分析quick find

顾名思义,可知这种算法,可以快速判断两个点是否连接。

采用长度为10的数组,数组索引标识点自身,数组值表示与其数组索引连接的点,

 
如图所示,0,5,6是连通的,1,2,7也是连通的。
某大牛说,算法+数据结构=程序,其实很大程度上有些问题,一旦给出了数据结构,算法就水到渠成了的,虽然这里是个很小的问题。
下面就是简单的c#代码
 
 public class QuickFind
    {
        public List< int> list;
        public QuickFind( int n)
        {
            list = new List< int>(n);
            for ( int i = 0; i < n; i++)
            {
                list.Add(i);
            }
        }
        public void AddUnion( int p, int q)
        {
            var pid = list[p];
            var qid = list[q];
            for ( int i = 0; i < list.Count(); i++)
            {
                if (list[i] == pid) list[i] = qid;
            }
        }
        public bool Connected( int p, int q)
        {
            return list[p] == list[q];
        }
        public void writeResult()
        {
            foreach ( var item in list)
            {
                Console.Write(item.ToString()+ ' ');
            }
        }
 
    }
 
提出了算法,就要简单分析一下复杂度之类的,blabla的,如下图
 
对于find来讲,复杂度很低,只要O(1)就行,每次只是需要比较两个索引点的值就行,
但是对于union的动作就有点高了,对于N个点对来实现union命令,复杂度是O(N^2)(N的平方)。

 
接下来分析quick union 算法
同时是以数组作为数据结构,只不过每个索引点中的值是该点的父节点,知道索引值等于存储值为止,最终的节点就是root节点,上图比较容易懂
3的root是9,root节点相同的,就是连通的。
自然而然就可以得到简单的代码实现
 
 
 
public class QuickUnion
    {
        int[] list;
        public QuickUnion( int n)
        {
             list = new  int[n];
            for ( int i = 0; i < n; i++)
            {             
                list[i]=i;
            }
        }
        public bool Find( int p, int q)
        {
            return root(p) == root(q);
        }
        public void AddUnion( int p, int q)
        {
            int i = root(p);
            int j = root(q);
            if (i == j) return;
            list[i]=j;
        }
        private int root( int p)
        {
            while (p != list[p])
            {        
                p = list[p];
            }
            return p;
        }
        public void writeResult()
        {
            for ( int i = 0; i < list.Count(); i++)
            {
                Console.Write(list[i].ToString()+ ' ');
            }
        }
    }
算法复杂都分析如上图。
quick-find的缺点是union动作代价太高,虽然保持了形成的树是flat的,但是为了保持flat代价太高
quick-union的缺点是形成的树太高,而且find操作代价高
 

当然了,有了解决方案,但是效果不理想,下一步想的就是怎么improvement了~~
 
 
Improvement 1
 
 
改进一:利用weighted quick-union,即在每次union的时候,将size比较小的树的根指向size较大的树的根,
而不是反过来。这里size大小指的是每个树的节点的多少,而不是树的高度(当然也可以是,只不过节点数比较好计算)。
 
 
这当然是一个挺好的改进,不过让我想到空间和时间的不可完美统一。虽然时间上改进了,但是空间上需要多出一个size数组来保持树的大小,所以凡事没有完美的~~
 
下图是一个100个sites,88个union操作得出的结果,weiggted算法效果还是很显著的
 
 
代码如下
 
 
public class WeightedQuickUnion
    {
        int[] list;
        int[] size;
        public WeightedQuickUnion( int n)
        {
             list = new  int[n];
             size = new int[n];
            for ( int i = 0; i < n; i++)
            {
              
                list[i]=i;
                size[i]=1;
            }
        }
        public bool Find( int p, int q)
        {
            return root(p) == root(q);
        }
        public void AddUnion( int p, int q)
        {
            int i = root(p);
            int j = root(q);
            if (i == j) return;
            if(size[i] < size[j])
            {
                list[i] = j;
                size[j] += size[i];
            }
            else
            {
                list[j] = list[i];
                size[i] += size[j];
            }
        }
        private int root( int p)
        {
            while (p != list[p])
            {
                p = list[p];
            }
            return p;
        }
        public void writeResult()
        {
            for ( int i = 0; i < list.Count(); i++)
            {
                Console.Write(list[i].ToString()+ ' ');
            }
        }
    }
简单分析下:
     find操作需要判断节点的root是否一致,所以和节点的深度相关
     union操作在给定节点root的情况下,是话费常量时间的。
然而,具有N个节点的weighted quick-union树的深度是不大于lgN的(2为底的)。
 
pf:
 
 
 
复杂度分析,就此我们得到满意答案了吗?
 
Of  Course NOT!!
 
 
Improvement 2:path compression
 
即quick-union with path compression,
对一个一个节点p,直接将其父节点设为root节点,那就是极好的了。
 
 
 
 
 
这几幅图,可以很好的展现出算法的实现原理。但是,但是,但是,这只需要添加一句代码就ok的啊,竟然只要一句的
 private int root(int p)
        {
            while (p != list[p])
            {
                list[p] = list[list[p]];//添加这一句
                p = list[p];
            }
            return p;
        }
一句话:No reason not to!
 
不可否认啊,效果好的算法一般分析起来,就没那么看起来简单了~~amortized analysis
 
[Hopcroft-Ulman,Tarjan] Starting from an empty data structure, any sequence of M 
union-find ops on N objects makes ≤ c ( N+ Mlg* N)array accesses
 
简单的算法,优雅的数学~~
 
但是,又是但是,每次都会有但是
虽然在实践中,WQUPC是线性的,但是理论确不是,甚至理论上不存在理论的算法。
 
复杂度分析大阅兵
 
10^9个union和find对10^9节点,WQUPC可以将时间从30年降到6s~~这就是算法的强大
~~
 
 

最后的最后,一个实践。
渗透概率问题,
 
对于一个节点是否open的概率p,与是否渗透的概率之间有什么关系~~
如图
over~~
 

跟着Sedgewick学算法(week 1 UnionFind)的更多相关文章

  1. 跟着Sedgewick学算法(week 1 ElementarySort)

     链接https://www.evernote.com/shard/s408/sh/dbe0167f-20e0-41c4-a49b-75717ad98695/461148482ffb6add092be ...

  2. 最小生成树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind

    最小支撑树树--Prim算法,基于优先队列的Prim算法,Kruskal算法,Boruvka算法,“等价类”UnionFind 最小支撑树树 前几节中介绍的算法都是针对无权图的,本节将介绍带权图的最小 ...

  3. 1164: 零起点学算法71——C语言合法标识符(存在问题)

    1164: 零起点学算法71——C语言合法标识符 Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: 10 ...

  4. 1163: 零起点学算法70——Yes,I can!

    1163: 零起点学算法70--Yes,I can! Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: ...

  5. 1147: 零起点学算法54——Fibonacc

    1147: 零起点学算法54--Fibonacc Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: 20 ...

  6. 1145: 零起点学算法52——数组中删数II

    1145: 零起点学算法52--数组中删数II Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: 293 ...

  7. 1137: 零起点学算法44——多组测试数据输出II

    1137: 零起点学算法44--多组测试数据输出II Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: ...

  8. 1136: 零起点学算法43——多组测试数据输出I

    1136: 零起点学算法43--多组测试数据输出I Time Limit: 1 Sec  Memory Limit: 128 MB   64bit IO Format: %lldSubmitted: ...

  9. 1135: 零起点学算法42——多组测试数据(求和)IV

    1135: 零起点学算法42--多组测试数据(求和)IV Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted ...

随机推荐

  1. 团队Alpha版本(七)冲刺

    目录 组员情况 组员1(组长):胡绪佩 组员2:胡青元 组员3:庄卉 组员4:家灿 组员5:凯琳 组员6:翟丹丹 组员7:何家伟 组员8:政演 组员9:黄鸿杰 组员10:刘一好 组员11:何宇恒 展示 ...

  2. 清除浮动float (:after方法)

    1. 什么时候需要清除浮动?清除浮动有哪些方法? (1)对元素进行了浮动(float)后,该元素就会脱离文档流,浮动在文档之上.在CSS中,任何元素都可以浮动.浮动元素会生成一个块级框,而不论它本身是 ...

  3. 关于usr/bin/ld: cannot find -lxxx问题总结(Qt编译错误cannot find -lGL)

    决定终结这个问题!(网上要想找到完整的解答实在太难了) http://blog.sciencenet.cn/blog-676535-541444.html 前两天手贱,把虚拟机玩崩溃了,只好重装虚拟机 ...

  4. http和Tcp的长连接和短连接

    . http协议和tcp/ip 协议的关系(1) http是应用层协议,tcp协议是传输层协议,ip协议是网络协议.(2) IP协议主要解决网络路由和寻址问题(3) tcp协议主要解决在IP层协议之上 ...

  5. 洛谷 P2597 [ZJOI2012]灾难 解题报告

    P2597 [ZJOI2012]灾难 题目描述 阿米巴是小强的好朋友. 阿米巴和小强在草原上捉蚂蚱.小强突然想,如果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发 ...

  6. mongo-aggregate命令详解

    一.aggregate执行流程和特性 1.执行流程: db.collection.aggregate()是基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage)组成的管道,可以对每个阶段的管 ...

  7. 华中农业大学第四届程序设计大赛网络同步赛 J

    Problem J: Arithmetic Sequence Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 1766  Solved: 299[Subm ...

  8. SICAU-OJ: 数字游戏

    数字游戏 题意:给出一个长度为n的数字,然后抹去k个数,使得剩下的数最大. 题解: 贪心的思想:让答案串中每一位尽可能大. 我们肯定要用完这k次的,假设有一个答案字符串ans,我们现在遍历给出的串,假 ...

  9. python登录qq

    登录qq的用的是get方法, 首先抓login_sig(某个包中的cookie),接着验证码的包(包含对验证码的校验),,最后计算一个p的加密算法,接着再get请求一个链接 https://ssl.p ...

  10. xmlhelper类

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Xm ...