首先,给大家推荐一个平台,Coursera (类比国内的mooc网),你可以在上面学习诸多国外一流大学的公开课视频,各个领域的都有,涉猎范围很广。想要出国留学的小伙伴儿不妨在上面事先感受一波国外授课的氛围与模式。
言归正传,作为一名程序猿,算法实在是太重要了!即便不是一名程序员,学习一下算法编程也是很有好处的。因为,在某种层面上讲,算法是你针对计算机进行的解决问题的思想上的映射,代码就是你想法的载体,逻辑的展现形式和与计算机进行沟通的工具。随着学习的深入,你会发现好多编程的技巧其实都源于生活!学习编程就是感悟生命!当然,在某种程度上,你也可以理解为我在胡说八道!哈哈!
下面介绍一下我学习普林斯顿大学_算法公开课:Part_1的经历吧!
由于是回顾课程,所以重点放在对核心算法的理解与剖析上:
第一部分:并查集
问题描述:你可以把下面这个图整体想象成是一个地下管道通水系统。黑色的是土,白色的是空管道,蓝色的是通水的管道。
显然,水是从上往下流的,只有靠近最上边界的管道才可以通水。同时,这个通水的过程是有压力的,不知道是不是有一个水泵,还是自然现象,凡是联通的水管情况是保持一致的,即不管管道是否是逆生长,既然你们手拉手是联通的,那就是一伙儿的,要么同流合污,要么空空如也!这里有一个关键点,你在编程中会注意到:下边界不是自联通的。而上边界,你可以想象成在其上方有一个水库,凡是靠近边界的水管都有水喝,呵呵!说了这么多废话,那么问题究竟是什么呢?就是针对一大片直立的黑土地,随机的铺设管道,啥时候水管才能Percolate?Percolate啥意识呢?就是上下边界中存在至少一条联通的水流!
答案是:
mean= 0.59229975
stddev= 0.011781283036553099
95% confidence interval=[0.5899906185248356,0.5946088814751644]
就是说大概随机管道铺设覆盖率达到60%,上下边界水流就通了!
怎么样,60分及格不无道理吧!编程即生活吧!
并查集核心算法:
package unionFind;
public class SelfUN {
private int count;
private int[] parent;
//private int[] size;//记录以当前节点为根节点的子树拥有的节点总数。
public SelfUN(int n) {//节点初始化。
count = n;
parent = new int[n];
//size = new int[n];
for (int i = 0; i < n; i++) {//初始状态每个节点的父节点是其自身。
parent[i] = i;
//size[i] = 1;
}
}
public int getCount() {//获取并查集中节点总数。
return count;
}
private void validate(int p) {//节点是否有效判别。
try {
if (p < 0 || p >= count) {
throw new IllegalArgumentException("");
}
} catch (Exception e) {
System.out.println("The parameter is wrong!");
}
}
public int find(int p) {//查集
validate(p);
int root = p;
while (parent[root] != root) {//找到根节点
root = parent[root];
}
while (p != root) {//路径压缩,将除根节点以外的所有节点都直指根节点。即每一次find都是一次优化。
int tmp = parent[p];
parent[p] = root;
p = tmp;
}
return root;
}
public boolean connected(int p, int q) {//追根溯源,查看两个节点是否具有相同的根节点,进而判别是否互相联通。
return find(p) == find(q);
}
public void union(int p, int q) {//将两个节点并集,归为一类。
if (find(p) == find(q))//如果原本互联,则无需操作。
return;
/* 此处保证总是将较小的那棵树合并到较大的那棵树上,以原较大的树的根节点作为合并后的根节点的好处是使得树更均衡,也是一种路径上的优化。
* 一种极端的情况是:路径压缩过程中可以保证遍历的是节点更少的那棵树,时间复杂度在一定程度上降低了。
* if(size[p]<=size[q]) { parent[p]=q; size[q]+=size[p]; }else { parent[q]=p;
* size[p]+=size[q]; }
*/
// count--;
if (p <= q) {//此处类比大根堆,遵循某种规律实际上也是一种路径上的优化,与size的引入同理。
parent[p] = q;
} else {
parent[q] = p;
}
}
}
算法评论:
尼古拉斯·沃斯说过:算法+数据结构=程序
本人认为:算法是一种抽象的逻辑思维,而数据结构作为实现算法的描述基础,对于算法的衍生和实现起到了举足轻重的作用。合理的数据结构是优质算法的第一步,一定程度上左右着算法的走向,很是关键。
并查集的数据结构就是树。简单的数组却巧妙的描述了父子节点间的关系:数组下标为子节点,数组值为父节点。当父子节点不断联通,关系也变得复杂起来,比如某个节点是另一个节点七大姑八大姨的儿子的舅舅的姥爷的姥姥的侄子,你会想?What?!!但如果告诉你,追根溯源你们有相同的老祖宗,500年前是一家啊!是不是关系就清晰明了了。管他三七二十一,反正500年前是一家,都是亲戚。并查集所关注的正式这个源头。而树所包含的逻辑关系很好的描述了追根溯源这一流程。树的根节点就是源头,就是传说中的老祖宗!
算法应用:
应用一,先给大家热个身,玩个裁剪小游戏。问题不作描述,自己理解吧!