IT技术小屋

秋风秋雨,皆入我心

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  38 随笔 :: 1 文章 :: 19 评论 :: 0 Trackbacks

2013年12月24日 #

本文介绍了包括 Python、Java、Haskell等在内的一系列编程语言的深度学习库。

Python
  • Theano是一种用于使用数列来定义和评估数学表达的 Python 库。它可以让 Python 中深度学习算法的编写更为简单。很多其他的库是以 Theano 为基础开发的。
  • Caffe是一种以表达清晰、高速和模块化为理念建立起来的深度学习框架。它是由伯克利视觉和学习中心(BVLC)和网上社区贡献者共同开发的。谷歌的 DeepDream 人工智能图像处理程序正是建立在 Caffe 框架之上。这个框架是一个 BSD 许可的带有 Python 接口的 C++库。
  • nolearn包含大量其他神经网络库中的包装器和抽象(wrappers and abstractions),其中最值得注意的是 Lasagne,其中也包含一些机器学习的实用模块。
  • Genism是一个部署在 Python 编程语言中的深度学习工具包,用于通过高效的算法处理大型文本集。
  • Chainer连接深度学习中的算法与实现,它强劲、灵活而敏锐,是一种用于深度学习的灵活的框架。
  • deepnet是一种基于 GPU 的深度学习算法的 Python 实现,比如:前馈神经网络、受限玻尔兹曼机、深度信念网络、自编码器、深度玻尔兹曼机和卷积神经网络。
  • Hebel是一个在 Python 中用于带有神经网络的深度学习的库,它通过 PyCUDA 使用带有 CUDA 的 GPU 加速。它可实现大多数目前最重要的神经网络模型,提供了多种不同的激活函数和训练方式,如动量,Nesterov 动量,退出(dropout)和 前期停止(early stopping)。
  • CXXNET是一种快速,简明的分布式深度学习框架,它以 MShadow 为基础。它是轻量级可扩展的 C++/CUDA 神经网络工具包,同时拥有友好的 Python/Matlab 界面,可供机器学习的训练和预测使用。
  • DeepPy是一种建立在 Mumpy 之上的 Python 化的深度学习框架。
  • DeepLearning是一个用 C++和 Python 开发的深度学习库。
C++
  • eblearn是一个机器学习的开源 C++库,由纽约大学机器学习实验室的 Yann LeCun 牵头研发。尤其是,按照 GUI、演示和教程来部署的带有基于能量的模型的卷积神经网络。
  • SINGA被设计用来进行已有系统中分布式训练算法的普通实现。它由 Apache Software Foundation 提供支持。
Java
  • N-Dimensional Arrays for Java (ND4J)是一种为 JVM 设计的科学计算库。它们被应用在生产环境中,这就意味着路径被设计成可以最小的 RAM 内存需求来快速运行。
  • Deeplearning4j是第一个为 Java 和 Scala 编写的消费级开元分布式深度学习库。它被设计成在商业环境中使用,而非研究工具。
  • Encog是一种先进的机器学习框架,支持支持向量机(Support Vector Machines),人工神经网络(Artificial Neural Networks),基因编程(Genetic Programming),贝叶斯网络(Bayesian Networks),隐马尔科夫模型(Hidden Markov Models)和 遗传算法(Genetic Algorithms)。
Lua
  • Torch是一种科学计算框架,可支持多种计算机学习算法。
Haskell
  • DNNGraph是一个用 Haskell 编写的深度神经网络生成 DSL。
.NET
  • Accord.NET是一种.NET 机器学习框架,包含声音和图像处理库,它完全由 C# 编写。它是一种为开发生产级的计算机视觉、计算机听觉、信号处理和统计应用而设计的完整框架。
R
  • darch包可以用于建立多层神经网络(深层结构)。其中的训练方式包括使用对比发散法进行提前训练,或使用通常的训练方法(如反向传播和共轭梯度)进行一些微调。
  • deepnet实现了一些深度学习架构和神经网络算法,包括 BP、RBM、DBN、深度自编码器等等。

posted @ 2016-11-13 00:45 Meng Lee 阅读(440) | 评论 (0)编辑 收藏

今天,终于有时间静下心来回顾过去两年来所做的事情,感慨万千,一时之间竟不知从何说起。两年以来,遇到的困难着实不少,但每每遭遇挫折与不顺之后,却往往能柳暗花明,遇到新的转机,让我真真切切地感受到了功夫不负有心人这句话的含意。

一、为什么要出国
其实,之前从来没有考虑过要出国,更没有想过能直接出国工作。回想一下,这个决定的做出,多半还是缘于自己骨子里的不安分。我从很大程度上来说是一个闲不住的人,从小学、中学、大学到研究生,我几乎每天都有明确的目标。然而,2013年从公司到事业单位工作以后,我的生活发生了巨大地转变。简单的工作、空洞的公文、无聊的活动占据了我全部的工作任务。有段时间几乎天天写材料搞活动。领导经常夸我材料写得又快又好,活动也搞得有声有色,心里感觉很有成就感。然而,时间一长,逐渐发现那些公文永远是一个套路,以至于我分门别类,摸索出了几个万能模板。而活动则千篇一律,让人疲于应付。我甚至可以看到六十岁退休时我在干什么,于是一阵恐惧感常常会莫名袭来,因为我不安分、不满足于此。我不能放弃所学所长,我不能庸庸碌碌在这里度过未来的几十年,我还有梦想,我还要登高看世界。为了这个,我走过了不平凡的两年。

二、如何出国
对于普通人来说,出国大致有三条路。
第一条路是申请去国外留学,取得学位之后以应届毕业生的身份找工作,然后留在国外生活。这是一条比较稳妥、简便的路,走这条路的人最多。
第二条路是先进入跨国公司的中国分公司工作一段时间,然后找机会外派到国外总部工作。走这条路的要求比较多,首先要能够进入比较大的跨国公司工作,其次这个公司愿意将中国员工transfer到国外,同时还要外国总部有部门愿意接收你,所以还是需要一些运气。但是,如果成功,好处也显而易见。省去了读书的时间和学费,降低了家庭负担,对于家境一般的人是非常好的选择。
第三条路是直接参加外国公司的面试,通过之后直接去国外工作。这条路要求最高,需要通过外国公司严格的面试,另外还要能够成功取得签证(美国工作签证就需要抽签)。因此,走这条路更需要实力、机遇和运气。
鉴于第三条路非常难走,为了保证成功,我选择了同时申请学校和参加外国公司面试的办法,这也注定了我将付出更多的艰苦努力。

三、申请学校
申请学校从准备到最终完成,我大概用了一年时间。其间参加了三次GRE和一次托福考试。回想准备的过程,最大的敌人就是自己,最重要的法宝就是坚持坚持再坚持。记得第一次考GRE没有取得理想的成绩,因为是第一次参加英语考试,心情非常失落。幸亏当时有女朋友(现在的老婆)的鼓励,我继续复习没有放弃。经过一个月的复习,取得了非常不错的托福成绩。记得托福出成绩的那天,我紧张得不敢查,点开页面的那一刻,我都不敢相信居然能有这么不错的成绩。特别是听力,考试的时候觉得好几个都没有听清楚,最后居然有27分,真是不可思议,可见功夫不负有心人,付出总有回报的。
有了英语成绩之后,就是撰写申请文书。这方面我完全没有经验,所有的信息全部是通过一亩三分地论坛获得的。这个论坛信息非常丰富,基本上所有申请相关的内容都有涉及。我每天都会花一些时间浏览别人的帖子,为自己定位选校,找文书灵感等等。非常感谢我的本科和研究生导师,还有蒋志诚为我递推荐信,没有你们的帮助,我不可能完成申请工作。
最后,我申请了美国和加拿大的十五所学校的计算机专业的研究生,拿到了CMU、USC和多伦多大学的offer。其中,CMU的Data Science program应该是世界数一数二的,录取率非常低,毕业后的去向也非常好,大多数都可以进入美国一流公司工作。多大也是加拿大排名第一的学校,计算机的就业也非常好。

四、Facebook的面试
参加Facebook的面试也完全是无意的,在Linkedin上收到了Facebook HR的邀请信,于是也没有怎么准备就做了电面,居然反馈非常好,马上就给我安排了onsite面试,地点是印度的海得拉巴。但是,始终是没有做什么准备,而且和谷歌不一样的是,HR办事效率实在太高,每一轮间隔都非常短,导致我根本没有时间热身一下,连leetcode都没有做过就匆匆参加面试了,最终没有如愿通过面试。
不过,这次面试还是很有收获。第一次出国,第一次参加美国公司全英文面试,学到了太多,积累了经验,可以说如果没有Facebook的失败,我是不可能进入谷歌的。

五、Google的面试
参加谷歌的面试可以说完全是老婆的怂恿。从印度参加完Facebook面试回来之后,我就开始专心于学校申请了。但是,老婆建议我试试面一下Google。由于Facebook的失利和Google近乎苛刻的面试流程,我开始是抗拒参加的。最后,在老婆的一再要求下,我终于找了一个在谷歌上海工作的师兄做了内推。四月底我收到了谷歌北京HR的第一通电话,也正式拉开了我为期一年的面试流程。
和HR通电话不久,我进行了第一次电话面试。谷歌的电话面试和Facebook差不多,就是面试官打过来,把题目口述并且写在Google Doc上,然后我把程序写在Google Doc上。第一次电面的题目不难,但谷歌对代码效率和清晰度的要求远远超出了我的想像。第一轮面得磕磕绊绊,但是幸好面试官是中国人,非常nice,没有让我fail。
于是,我又被要求进行第二次电面。期间由于面试官临时有事爽约,我等了差不多一个月。但是,也就是这一个月,我努力做了一些准备,虽然面试依旧不是十全十美,但是我还是有惊无险地进入到了onsite面试环节。
虽然可以onsite面试了,但是我依旧对进入谷歌不报任何希望,因为我清楚的知道,谷歌面试实在是太难了,onsite面试的挑战将远远大于电面。因此,我去北京面试完全是想做一次免费旅游。面试前一天还许久不见的万威夫妇吃饭,聊得很开心,完全没有把面试放在心上。
也许是放松的原因,我前一天晚上睡得很好,第二天我精神非常好。
不过谷歌毕竟是谷歌,面试第一轮一上来就给了我一个下马威。一个coding题一个设计题,表面上很简单,但是做出来总是有这样那样的问题,第一轮完了之后我基本打算回家了。
但是,不知道为什么,从第二轮开始,就越来越顺利,coding做得非常好,基本上是一次写完,没有涂改,也没有被面试官找到大的bug。突然之间,隐隐感觉出现了一丝希望。
四轮过后,我结束了第一次onsite面试。但是,三天之后,我被告知由于设计题做得不好,我被要求进行一次加面,地点在上海。于是,我又在上海做了一次面试,只有一个设计题。我感觉答得还可以,但是心情真的是忐忑不安,特别是接下来的一个礼拜,几乎是坐立不安。
记得是一个礼拜之后的礼拜五中午,我正做准备主持下午的道德讲堂,突然接到了一个010的电话,我知道是谷歌的电话。接通电话的那一刻,空气都几乎要凝固了,当听到通过HC的消息时,我激动得不能自已。不可能完成的任务居然完成了,虽然不知道能不能去美国总部工作,但是能进入谷歌已经非常不容易了,而且谷歌非常鼓励transfer去美国工作,因此机会还是很多的。
然而,让我没有想到的是,接下来的team match却异常艰难,陆陆续续几个team都没有成功match上。转眼就到了2014年春季,半年的等待让我对何时进入谷歌非常悲观,加上申请学校工作十分繁重,我基本没有关注这个事情。
就在我快要放弃的时候,拿到了美国一个公司的offer,他们答应给我办H1B签证。于是,我把这个情况告诉了谷歌,要求他们尽快给找到team,不然我就去美国了。结果谷歌居然在三天之内为我match上了英国office的一个team,让人不得不感叹还是要offer多才好啊!于是,我又经过了近三个月的签证办理流程,终于要启程赴英了。

回顾两年来的努力,终于要实现自己的梦想了,感慨万千。在短短的人生中,能有这一段不寻常的经历,我觉得十分幸运。展望未来,我想读万卷书不如行万里路,未来希望能够利用在伦敦工作的机会,尽量多去欧洲各国走走,丰富自己的阅历,开拓自己的眼界。

最后要感谢老婆一直以来的支持和鼓励,你一直是我前进的动力;其次要感谢父母的不理解和不支持,你们的反对让我更加完善了自己的计划,逼着我找到了一条最好的出路;还要感谢师长和朋友们的帮助,感谢杨老师和沈老师还有蒋志诚不厌其烦地帮我递推荐信,感谢万威夫妇多次在北京款待我,没有你们的美食,我是不可能完成面试的;还有许多帮助过我的人,在这里就不能一一感谢了。
posted @ 2014-06-13 02:00 Meng Lee 阅读(1463) | 评论 (0)编辑 收藏

算法很简单,核心思想是:对某个值A[i]来说,能trapped的最多的water取决于在i之前最高的值leftMostHeight[i]和在i右边的最高的值rightMostHeight[i]。(均不包含自身)。如果min(left,right) > A[i],那么在i这个位置上能trapped的water就是min(left,right) – A[i]。
有了这个想法就好办了,第一遍从左到右计算数组leftMostHeight,第二遍从右到左计算rightMostHeight,在第二遍的同时就可以计算出i位置的结果了,而且并不需要开空间来存放rightMostHeight数组。
时间复杂度是O(n),只扫了两遍。

 1 public class TrappingRainWater {
 2     public int trap(int A[], int n) {
 3         if (n <= 2)
 4             return 0;
 5 
 6         int[] lmh = new int[n];
 7         lmh[0] = 0;
 8         int maxh = A[0];
 9         for (int i = 1; i < n; ++i) {
10             lmh[i] = maxh;
11             if (maxh < A[i])
12                 maxh = A[i];
13         }
14         int trapped = 0;
15         maxh = A[n - 1];
16         for (int i = n - 2; i > 0; --i) {
17             int left = lmh[i];
18             int right = maxh;
19             int container = Math.min(left, right);
20             if (container > A[i]) {
21                 trapped += container - A[i];
22             }
23             if (maxh < A[i])
24                 maxh = A[i];
25         }
26         return trapped;
27     }
28 }
posted @ 2014-01-14 09:16 Meng Lee 阅读(206) | 评论 (0)编辑 收藏

The set [1,2,3,…,n] contains a total of n! unique permutations.
By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3):
"123"
"132"
"213"
"231"
"312"
"321"
Given n and k, return the kth permutation sequence.
Note: Given n will be between 1 and 9 inclusive.

这道题其实有很强的规律可循。首先,n个元素的排列总数是n!。在下面的分析中,让k的范围是0 <= k < n!。(题目代码实际上是1<=k<=n!)
可以看到一个规律,就是这n!个排列中,第一位的元素总是(n-1)!一组出现的,也就说如果p = k / (n-1)!,那么排列的最开始一个元素一定是arr[p]。
这个规律可以类推下去,在剩余的n-1个元素中逐渐挑选出第二个,第三个,...,到第n个元素。程序就结束。
 1 /**
 2  * The set [1,2,3,…,n] contains a total of n! unique permutations.
 3  * 
 4  * By listing and labeling all of the permutations in order, We get the
 5  * following sequence (ie, for n = 3):
 6  * 
 7  * "123" "132" "213" "231" "312" "321" Given n and k, return the kth permutation
 8  * sequence.
 9  * 
10  * Note: Given n will be between 1 and 9 inclusive.
11  * 
12  */
13 
14 public class PermutationSequence {
15     public String getPermutation(int n, int k) {
16         char[] arr = new char[n];
17         int pro = 1;
18         for (int i = 0; i < n; ++i) {
19             arr[i] = (char) ('1' + i);
20             pro *= (i + 1);
21         }
22         k = k - 1;
23         k %= pro;
24         pro /= n;
25         for (int i = 0; i < n - 1; ++i) {
26             int selectI = k / pro;
27             k = k % pro;
28             pro /= (n - i - 1);
29             int temp = arr[selectI + i];
30             for (int j = selectI; j > 0; --j) {
31                 arr[i + j] = arr[i + j - 1];
32             }
33             arr[i] = (char) temp;
34         }
35         return String.valueOf(arr);
36     }
37 }
38 
posted @ 2014-01-07 16:06 Meng Lee 阅读(436) | 评论 (0)编辑 收藏

Lowest Common Ancestor of a Binary Search Tree (BST)
Given a binary search tree (BST), find the lowest common ancestor of two given nodes in the BST.
       _______6______
      /              \
   ___2__          ___8__
  /      \        /      \
  0      _4       7       9
        /  \
        3   5
Using the above tree as an example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. 
But how about LCA of nodes 2 and 4? Should it be 6 or 2?
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between 
two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a 
node to be a descendant of itself).” Since a node can be a descendant of itself, the LCA of 2 and 
4 should be 2, according to this definition.
Hint:
A top-down walk from the root of the tree is enough.

 1 public class LowestCommonAncestorOfaBinarySearchTree {
 2     public TreeNode LCA(TreeNode root, TreeNode p, TreeNode q) {
 3         if (root == null || p == null || q == null)
 4             return null;
 5         if (Math.max(p.val, q.val) < root.val)
 6             return LCA(root.left, p, q);
 7         if (Math.min(p.val, q.val) > root.val)
 8             return LCA(root.right, p, q);
 9         return root;
10     }
11 }


Given a binary tree, find the lowest common ancestor of two given nodes in the tree.
 
 
        _______3______
       /              \
    ___5__          ___1__
   /      \        /      \
  6      _2       0       8
         /  \
         7   4
If you are not so sure about the definition of lowest common ancestor (LCA), please refer to my previous 
post: Lowest Common Ancestor of a Binary Search Tree (BST) or the definition of LCA here. Using the tree 
above as an example, the LCA of nodes 5 and 1 is 3. Please note that LCA for nodes 5 and 4 is 5.
 
Hint:
Top-down or bottom-up? Consider both approaches and see which one is more efficient.
 1 public class LowestCommonAncestorOfBinaryTree {
 2     public TreeNode LCA(TreeNode root, TreeNode p, TreeNode q) {
 3         if (root == null)
 4             return null;
 5         if (root == p || root == q)
 6             return root;
 7         TreeNode left = LCA(root.left, p, q);
 8         TreeNode right = LCA(root.right, p, q);
 9         if (left != null && right != null)
10             return root;
11         return left != null ? left : right;
12     }
13 }
posted @ 2014-01-06 09:32 Meng Lee 阅读(327) | 评论 (0)编辑 收藏

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.
Below is one possible representation of s1 = "great":
    great
   /    \
  gr    eat
 / \    /  \
g   r  e   at
           / \
          a   t
To scramble the string, we may choose any non-leaf node and swap its two children.
For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".
    rgeat
   /    \
  rg    eat
 / \    /  \
r   g  e   at
           / \
          a   t
We say that "rgeat" is a scrambled string of "great".
Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".
    rgtae
   /    \
  rg    tae
 / \    /  \
r   g  ta  e
       / \
      t   a
We say that "rgtae" is a scrambled string of "great".
Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.

 1 public class ScrambleString {
 2     public boolean isScramble(String s1, String s2) {
 3         if (s1.length() != s2.length())
 4             return false;
 5         if (s1.equals(s2))
 6             return true;
 7 
 8         int[] A = new int[26];
 9         for (int i = 0; i < s1.length(); i++) {
10             ++A[s1.charAt(i) - 'a'];
11         }
12 
13         for (int j = 0; j < s2.length(); j++) {
14             --A[s2.charAt(j) - 'a'];
15         }
16 
17         for (int k = 0; k < 26; k++) {
18             if (A[k] != 0)
19                 return false;
20         }
21 
22         for (int i = 1; i < s1.length(); i++) {
23             boolean result = isScramble(s1.substring(0, i), s2.substring(0, i))
24                     && isScramble(s1.substring(i), s2.substring(i));
25             result = result
26                     || (isScramble(s1.substring(0, i),
27                             s2.substring(s2.length() - i, s2.length())) && isScramble(
28                             s1.substring(i), s2.substring(0, s2.length() - i)));
29             if (result)
30                 return true;
31         }
32         return false;
33     }
34 }
posted @ 2014-01-05 12:33 Meng Lee 阅读(176) | 评论 (0)编辑 收藏

Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].
The largest rectangle is shown in the shaded area, which has area = 10 unit.
For example,
Given height = [2,1,5,6,2,3],
return 10.

本题需要使用栈维护一个高度单调递增的序列下标,如果遇到一个元素比当前栈顶元素高度小,那么出栈,并计算当前最大面积。如果栈为空,需要特殊考虑。
 1 public class LargestRectangleinHistogram {
 2     public int largestRectangleArea(int[] height) {
 3         Stack<Integer> stack = new Stack<Integer>();
 4         int i = 0;
 5         int maxArea = 0;
 6         int[] h = new int[height.length + 1];
 7         h = Arrays.copyOf(height, height.length + 1);
 8         while (i < h.length) {
 9             if (stack.isEmpty() || h[stack.peek()] <= h[i]) {
10                 stack.push(i++);
11             } else {
12                 int t = stack.pop();
13                 maxArea = Math.max(maxArea, h[t] * (stack.isEmpty() ? i : i - stack.peek() - 1));
14             }
15         }
16         return maxArea;
17     }
18 }
posted @ 2014-01-05 12:31 Meng Lee 阅读(260) | 评论 (0)编辑 收藏

Given a binary tree, return the inorder traversal of its nodes' values.
For example:
Given binary tree {1,#,2,3},
   1
    \
     2
    /
   3
return [1,3,2].
Note: Recursive solution is trivial, could you do it iteratively?

切记p节点初始时指向root.left。代码如下:
 1 public class BinaryTreeInorderTraversal {
 2     public ArrayList<Integer> inorderTraversal(TreeNode root) {
 3         ArrayList<Integer> inOrder = new ArrayList<Integer>();
 4         if (root == null)
 5             return inOrder;
 6         Stack<TreeNode> s = new Stack<TreeNode>();
 7         s.add(root);
 8         TreeNode p = root.left;
 9         while (!s.empty()) {
10             while (p != null) {
11                 s.add(p);
12                 p = p.left;
13             }
14             TreeNode n = s.pop();
15             inOrder.add(n.val);
16             p = n.right;
17             if (p != null) {
18                 s.add(p);
19                 p = p.left;
20             }
21         }
22         return inOrder;
23     }
24 }
posted @ 2014-01-04 11:17 Meng Lee 阅读(157) | 评论 (0)编辑 收藏

Given a collection of integers that might contain duplicates, S, return all possible subsets.
Note:
Elements in a subset must be in non-descending order.
The solution set must not contain duplicate subsets.
For example,
If S = [1,2,2], a solution is:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

由于元素中可能存在重复,因此较之于Subset的实现,需要加一些判断。如果碰到了重复元素,只需要在上一次迭代新增的子集的基础上再进行迭代即可。实现代码如下:
 1 public class SubsetsII {
 2     public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] num) {
 3         ArrayList<ArrayList<Integer>> ret = new ArrayList<ArrayList<Integer>>();
 4         ArrayList<ArrayList<Integer>> lastLevel = null;
 5         ret.add(new ArrayList<Integer>());
 6         Arrays.sort(num);
 7         for (int i = 0; i < num.length; i++) {
 8             ArrayList<ArrayList<Integer>> tmp = new ArrayList<ArrayList<Integer>>();
 9             ArrayList<ArrayList<Integer>> prev = i == 0 || num[i] != num[i - 1] ? ret : lastLevel;
10             for (ArrayList<Integer> s : prev) {
11                 ArrayList<Integer> newSet = new ArrayList<Integer>(s);
12                 newSet.add(num[i]);
13                 tmp.add(newSet);
14             }
15             ret.addAll(tmp);
16             lastLevel = tmp;
17         }
18         return ret;
19     }
20 }
posted @ 2014-01-03 16:40 Meng Lee 阅读(169) | 评论 (0)编辑 收藏

Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:
Only one letter can be changed at a time
Each intermediate word must exist in the dictionary
For example,
Given:
start = "hit"
end = "cog"
dict = ["hot","dot","dog","lot","log"]
Return
  [
    ["hit","hot","dot","dog","cog"],
    ["hit","hot","lot","log","cog"]
  ]
Note:
All words have the same length.
All words contain only lowercase alphabetic characters.

这个题目应该算是leetcode上比较难的题目了。刚开始我采用了和Word Ladder相似的做法,只是用ArrayList记录了当前变换路径,在小数据的情况下可以Accept,但是大数据超时。究其原因,是由于为每个当前节点记录变换路径的时候,需要复制之前的ArrayList,这个时间开销较大。
其实,我们可以采用一个Map<String, HashSet<String>>结构,记录字典单词的每一个前驱,这样我们可以从end反向遍历,构造出转换路径。
同时,我利用了两个ArrayList,交替记录上一层和下一层的节点,如果下一层节点为空,则不存在路径,立即返回。如果下一层中出现了end,证明找到了所有的最短路径,停止搜索开始构造路径。实现代码如下:
 1 public class WordLadderII {
 2     private void GeneratePath(Map<String, ArrayList<String>> prevMap,
 3             ArrayList<String> path, String word,
 4             ArrayList<ArrayList<String>> ret) {
 5         if (prevMap.get(word).size() == 0) {
 6             path.add(0, word);
 7             ArrayList<String> curPath = new ArrayList<String>(path);
 8             ret.add(curPath);
 9             path.remove(0);
10             return;
11         }
12 
13         path.add(0, word);
14         for (String pt : prevMap.get(word)) {
15             GeneratePath(prevMap, path, pt, ret);
16         }
17         path.remove(0);
18     }
19 
20     public ArrayList<ArrayList<String>> findLadders(String start, String end,
21             HashSet<String> dict) {
22         ArrayList<ArrayList<String>> ret = new ArrayList<ArrayList<String>>();
23         Map<String, ArrayList<String>> prevMap = new HashMap<String, ArrayList<String>>();
24         dict.add(start);
25         dict.add(end);
26         for (String d : dict) {
27             prevMap.put(d, new ArrayList<String>());
28         }
29         ArrayList<HashSet<String>> candidates = new ArrayList<HashSet<String>>();
30         candidates.add(new HashSet<String>());
31         candidates.add(new HashSet<String>());
32         int current = 0;
33         int previous = 1;
34         candidates.get(current).add(start);
35         while (true) {
36             current = current == 0 ? 1 : 0;
37             previous = previous == 0 ? 1 : 0;
38             for (String d : candidates.get(previous)) {
39                 dict.remove(d);
40             }
41             candidates.get(current).clear();
42             for (String wd : candidates.get(previous)) {
43                 for (int pos = 0; pos < wd.length(); ++pos) {
44                     StringBuffer word = new StringBuffer(wd);
45                     for (int i = 'a'; i <= 'z'; ++i) {
46                         if (wd.charAt(pos) == i) {
47                             continue;
48                         }
49 
50                         word.setCharAt(pos, (char) i);
51 
52                         if (dict.contains(word.toString())) {
53                             prevMap.get(word.toString()).add(wd);
54                             candidates.get(current).add(word.toString());
55                         }
56                     }
57                 }
58             }
59 
60             if (candidates.get(current).size() == 0) {
61                 return ret;
62             }
63             if (candidates.get(current).contains(end)) {
64                 break;
65             }
66         }
67 
68         ArrayList<String> path = new ArrayList<String>();
69         GeneratePath(prevMap, path, end, ret);
70 
71         return ret;
72     }
73 }
posted @ 2014-01-02 13:59 Meng Lee 阅读(853) | 评论 (0)编辑 收藏

Distinct Subsequences

Given a string S and a string T, count the number of distinct sub sequences of T in S.
A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not).
Here is an example:
S = "rabbbit", T = "rabbit"
Return 3.

这两个题目很相似,状态方程是f(i, j) = f(i - 1, j) + S[i] == T[j]? f(i - 1, j - 1) : 0 Where f(i, j) is the number of distinct sub-sequence for T[0:j] in S[0:i].
数组初始情况是第一列全部是1,代表T字符串是空串,S和自己做匹配。实现代码如下:
 1 public class DistinctSubsequences {
 2     public int numDistinct(String S, String T) {
 3         int[][] f = new int[S.length() + 1][T.length() + 1];
 4         for (int k = 0; k < S.length(); k++)
 5             f[k][0] = 1;
 6         for (int i = 1; i <= S.length(); i++) {
 7             for (int j = 1; j <= T.length(); j++) {
 8                 if (S.charAt(i - 1) == T.charAt(j - 1)) {
 9                     f[i][j] += f[i - 1][j] + f[i - 1][j - 1];
10                 } else {
11                     f[i][j] += f[i - 1][j];
12                 }
13             }
14         }
15         return f[S.length()][T.length()];
16     }
17 }

Edit Distance
Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.)
You have the following 3 operations permitted on a word:
a) Insert a character
b) Delete a character
c) Replace a character

 1 public class EditDistance {
 2     public int minDistance(String word1, String word2) {
 3         if (word1.length() == 0 || word2.length() == 0)
 4             return word1.length() == 0 ? word2.length() : word1.length();
 5         int[][] arr = new int[word2.length() + 1][word1.length() + 1];
 6         for (int i = 0; i <= word1.length(); i++) {
 7             arr[0][i] = i;
 8         }
 9         for (int j = 0; j <= word2.length(); j++) {
10             arr[j][0] = j;
11         }
12         for (int i = 0; i < word1.length(); i++) {
13             for (int j = 0; j < word2.length(); j++) {
14                 if (word1.charAt(i) == word2.charAt(j)) {
15                     arr[j + 1][i + 1] = arr[j][i];
16                 } else {
17                     int dis = Math.min(arr[j][i + 1], arr[j + 1][i]);
18                     arr[j + 1][i + 1] = Math.min(arr[j][i], dis) + 1;
19                 }
20             }
21         }
22         return arr[word2.length()][word1.length()];
23     }
24 }
posted @ 2013-12-31 10:21 Meng Lee 阅读(232) | 评论 (0)编辑 收藏

给定一个无序的整数数组,怎么找到第一个大于0,并且不在此数组的整数。比如[1,2,0] 返回 3, [3,4,-1,1] 返回 2。最好能O(1)空间和O(n)时间。
 1 public class Solution {
 2     public int findMissedNumber(int[] nums) {
 3         for (int i = 0; i < nums.length; i++) {
 4             if (nums[i] > 0 && nums[i] - 1 != i && nums[i] != nums[nums[i] - 1]) {
 5                 int tmp = nums[nums[i] - 1];
 6                 nums[nums[i] - 1] = nums[i];
 7                 nums[i] = tmp;
 8                 i--;
 9             }
10         }
11         for (int j = 0; j < nums.length; j++) {
12             if (nums[j] - 1 != j) return j + 1;
13         }
14         return nums.length + 1;
15     }
16 }
posted @ 2013-12-28 15:31 Meng Lee 阅读(133) | 评论 (0)编辑 收藏

3个字符串a,b,c。判断c是否是a和b的interleave,也就是c中应该有a,b中所有字 符,并且c中字符顺序和a,b中一样。比如,
1. a = "ef" b = "gh" c = "egfh" return true
2. a = "ef" b = "gh" c = "ehgf" return false

分析:
这个题目中,并没有说明a和b中是否有相同的字符,这个直接影响了最终的解法。所以,大家在面试的过程中,要和面试官进行交互,弄清楚之后再动手。a和b中不含有相同字符的情况很简单,这里略去。下面给出a和b中包含相同字符的动态规划的解法。

 1 public class Solution {
 2     public boolean isInterleaved(String a, String b, String c) {
 3         int lengthA = a.length();
 4         int lengthB = b.length();
 5         int lengthC = c.length();
 6         if (lengthA + lengthB != lengthC)
 7             return false;
 8         boolean[][] map = new boolean[lengthB + 1][lengthA + 1];
 9         map[0][0] = true;
10         for (int m = 1; m < lengthA; m++) {
11             map[0][m] = (a.charAt(m - 1) == c.charAt(m - 1) && map[0][m - 1]);
12         }
13         for (int n = 1; n < lengthB; n++) {
14             map[n][0] = (b.charAt(n - 1) == c.charAt(n - 1) && map[n - 1][0]);
15         }
16         for (int i = 1; i <= lengthB; i++) {
17             for (int j = 1; j <= lengthA; j++) {
18                 map[i][j] = (c.charAt(i + j - 1) == b.charAt(i - 1) && map[i - 1][j])
19                         || (c.charAt(i + j - 1) == a.charAt(j - 1) && map[i][j - 1]);
20             }
21         }
22         return map[lengthB][lengthA];
23     }
24 }


posted @ 2013-12-28 14:29 Meng Lee 阅读(147) | 评论 (0)编辑 收藏

删除字符串中的“b”和“ac”,需要满足如下的条件:
1. 字符串只能遍历一次
2. 不能够实用额外的空间

例如:
1. acbac   ==>  ""
2. aaac    ==>  aa
3. ababac  ==>   aa
4. bbbbd   ==>   d
5. aaccac  ==> ""

 1 public class Solution {
 2     public String deleteChars(String s) {
 3         StringBuffer sb = new StringBuffer(s);
 4         int fast = 0, slow = -1;
 5         int length = s.length();
 6         while (fast < length) {
 7             if (sb.charAt(fast) == 'b') {
 8                 fast++;
 9             } else if (fast < length - 1 && sb.charAt(fast) == 'a' && sb.charAt(fast + 1) == 'c') {
10                 fast += 2;
11             } else {
12                 sb.setCharAt(++slow, sb.charAt(fast++));
13                 if (slow > 0 && sb.charAt(slow - 1) == 'a' && sb.charAt(slow) == 'c') {
14                     slow -= 2;
15                 }
16             }
17         }
18         return sb.substring(0, slow + 1);
19     }
20 }


posted @ 2013-12-28 11:02 Meng Lee 阅读(102) | 评论 (0)编辑 收藏

对一个字符串按照回文进行分割,例如aba|b|bbabb|a|b|aba就是字符串ababbbabbababa的一个回文分割,每一个字串都是一个回文。请找到可以分割的最少的字串数。例如:
1. ababbbabbababa最少4个字符串,分割三次:a|babbbab|b|ababa
2. 如果字符串整体是回文,则需要0次分割,最少1个字符串

分析:
递归方程为:f(i) = MIN(f(j - 1) + 1), map[j][i] == true, 0<=j<i,其中f(i)为以第i个字符结尾的字符串的最小切割次数,map数组用来记录s[i][j]是否是对称的。实现代码如下:
 1 public class Solution {
 2     public int minPalindromePartition(String s) {
 3         int length = s.length();
 4         boolean[][] map = new boolean[length][length];
 5         int[] record = new int[length];
 6         for (int k = 0; k < length; k++) {
 7             record[k] = k;
 8         }
 9         for (int offset = 0; offset < length; offset++) {
10             for (int i = 0, j = i + offset; i < length - offset; i++, j++) {
11                 if (i == j || (s.charAt(i) == s.charAt(j) && (j - i == 1 || map[i + 1][j - 1]))) {
12                     map[i][j] = true;
13                 }
14             }
15         }
16         for (int i = 1; i < length; i++) {
17             for (int j = 0; j < i; j++) {
18                 if (map[j][i]) {
19                     if (j > 0) {
20                         record[i] = Math.min(record[i], record[j - 1] + 1);
21                     } else {
22                         record[i] = 0;
23                     }
24                 }
25             }
26         }
27         return record[length - 1];
28     }
29 }
posted @ 2013-12-27 16:06 Meng Lee 阅读(127) | 评论 (0)编辑 收藏

求二叉搜索树的中序后继节点

分析:
1. 如果目标节点的右子树不为空,则返回右子树的最小节点;
2. 如果目标节点的右子树为空,则从根节点开始遍历。
实现代码如下:
 1 public class Solution {
 2     public TreeNode inOrderSuccessor(TreeNode root, TreeNode target) {
 3         if (target == nullreturn null;
 4         if (target.right != nullreturn minValue(target.right);
 5         TreeNode succ = null;
 6         while (root != null) {
 7             if (target.val < root.val) {
 8                 succ = root;
 9                 root = root.left;
10             } else if (target.val > root.val) {
11                 root = root.right;
12             } else {
13                 break;
14             }
15         }
16         return succ;
17     }
18 
19     private TreeNode minValue(TreeNode root) {
20         while (root.left != null) {
21             root = root.left;
22         }
23         return root;
24     }
25 }
posted @ 2013-12-27 11:06 Meng Lee 阅读(197) | 评论 (0)编辑 收藏

最少插入字符

给定字符串,可以通过插入字符,使其变为回文。求最少插入字符的数量。例如:
1. ab最少插入1个字符,变为bab
2. aa最少插入0个字符
3. abcd最少插入3个字符,dcbabcd

分析
    这个题目的分析思路,和前面两期是非常相似的:给出递归的解法,发现重复的子问题,改进为动态规划的解法,这是一个分析的过程,待同学们比较熟悉时候,可以直接给出动态规划的解决方案,就很好了。
    这个题目,递归该如何解呢?给定一个字符串str,长度为n,怎么插入最少的字符,是的字符串变为回文呢?插入最少的字符,就是要尽量利用原来的字符,在原字符串str中,尽量利用更多能够匹配的字符。怎么对这个问题进行分解呢?考虑str字符串整体:
    1. 如果str[0]==str[n-1],则问题转变为求str[1,n-2],插入最少字符,得到回文
    2. 如果str[0]!=str[n-1],则需要插入一个字符要么和str[0]相同,要么和str[n-1]相同,
    3. 如果和str[0],则转变为str[1,n-1],插入最少字符,得到回文
    4. 如果和str[n-1],则转变为str[0,n-2],插入最少字符,得到回文
    上面的第2种情况中,需要取两个值最小值。则完成了问题的分解,并且,基本情况也分析完全,则有递归式为:
    fmi(str, l, h) = (str[l] == str[h]) ? fmi(str, l+1, h-1) : (min(fmi(str, i+1, h), fmi(str,l, h-1))+1)
    通过上面的式子,有经验的、熟练的同学,很直接的就能看出来,存在重复的子问题,这就意味着,我们可以讲子问题的解缓存使用。如果,没有直接能够看出来的同学们,还是可以按照我们之前的方法,把递归树画出来吧,那样更加一目了然。
    那么,这个题目该如何用动态规划的解决呢?如何重复利用子问题的解呢?似乎有些不那么直接。但其实也是于规律可循的。上面的递归式,是从字符串的两 边,想中间移动递归,根据动态规划解决问题的思想,我们先解决子问题,再重复利用子问题,就是要从内向外解决,大家还记得回文子串判断的那个题目么,动态 规划解法的外层循环是子串的长度,这个题目也是类似的。示例代码如下:
 1 public class Solution {
 2     public int constructPalindrome(String s) {
 3         int length = s.length();
 4         int[][] map = new int[length][length];
 5         for (int offset = 1; offset < length; offset++) {
 6             for (int i = 0, j = offset; i < length - offset; i++, j++) {
 7                 if (i == j - 1) {
 8                     if (s.charAt(i) != s.charAt(j)) {
 9                         map[i][j] = 1;
10                     }
11                 } else {
12                     if (s.charAt(i) != s.charAt(j)) {
13                         map[i][j] = Math.min(map[i][j - 1], map[i + 1][j]) + 1;
14                     } else {
15                         map[i][j] = map[i + 1][j - 1];
16                     }
17                 }
18             }
19         }
20         return map[0][length - 1];
21     }
22 }
posted @ 2013-12-26 16:53 Meng Lee 阅读(158) | 评论 (0)编辑 收藏

1、给定一组区间,表示为[start,end],请给出方法,将有重叠的区间进行合并。例如:
给定:[1,3],[2,6],[8,10],[15,18],
合并:[1,6],[8,10],[15,18].
分析:题目很直观,首先把区间递增排序,然后从头合并,合并时观察当前区间的start是否小于或等于前一个区间的end。代码如下:
 1 public class MergeIntervals {
 2     public class IntervalCmp implements Comparator<Interval> {
 3         @Override
 4         public int compare(Interval i1, Interval i2) {
 5             if (i1.start < i2.start) return -1;
 6             if (i1.start == i2.start && i1.end <= i2.end) return -1;
 7             return 1;
 8         }
 9     }
10     
11     public ArrayList<Interval> merge(ArrayList<Interval> intervals) {
12         ArrayList<Interval> ret = new ArrayList<Interval>();
13         if (intervals.size() == 0) return ret;
14         Interval[] arr = new Interval[intervals.size()];
15         intervals.toArray(arr);
16         Arrays.sort(arr, new IntervalCmp());
17         int start = arr[0].start;
18         int end = arr[0].end;
19         for (int i = 0; i < arr.length; i++) {
20             if (arr[i].start <= end) {
21                 end = Math.max(end, arr[i].end);
22             } else {
23                 ret.add(new Interval(start, end));
24                 start = arr[i].start;
25                 end = arr[i].end;
26             }
27         }
28         ret.add(new Interval(start, end));
29         return ret;
30     }
31 }
2、给定的一组区间,将区间中的存在的任意区间的父区间删除,例如:
给定:[1,2] [1,3] [1,4] [5,9] [6,8]
删除后:[1,2] [6,8]
分析:此题暴力的解法的复杂度为O(N^2)。受上一题排序的启发,是否有好一点的思路呢?
我们可以按照start递增排序,若start相等,则按照end递减排序。考虑排序后的第i-1 和第i个区间,由于start是递增的,所以第i-1个区间的start肯定小于等于第i个区间的start。若第i-1个区间的end大于等于第i个区间的end,则第i-1个区间就成为第i个区间的父区间了。

按照这个思路,可以试着在排序之后逆向顺序处理每一个区间。假设当前处理第i个区间,如前所述,若第i-1个区间的end大于等于第i个区间的end,则第i-1个区间成为第i个区间的父区间,可以保留第i个区间,将第i-1个区间删除。由于第i-1个区间是第i个区间的父区间,所以第i-1个区间的父区间也是第i个区间的父区间,这种情形下删掉第i-1个区间,后续不会漏删第i-1个区间的父区间。若第i-1个区间的end小于第i个区间的end,则可以跳过第i个区间,开始处理第i-1个区间。因为按照这种处理的方法,在处理到第i个区间时,该区间不会是任何区间的父区间(若是父区间已经被如前所述的方法删除了)。而且,在这种情形下,后续可能出现的第i个区间的父区间会是第i-1个区间的父区间,所以也不会漏掉第i个区间的父区间。所以排序之后逆向处理,只需要O(N)的时间,就可以解决这道问题。整体的时间复杂度为O(nlogn)。
 1 public class Solution {
 2     public class IntervalCmp implements Comparator<Interval> {
 3         @Override
 4         public int compare(Interval i1, Interval i2) {
 5             if (i1.start < i2.start) return -1;
 6             if (i1.start == i2.start && i1.end >= i2.end) return -1;
 7             return 1;
 8         }
 9     }
10     
11     public ArrayList<Interval> merge(ArrayList<Interval> intervals) {
12         ArrayList<Interval> ret = new ArrayList<Interval>();
13         if (intervals.size() == 0) return ret;
14         Interval[] arr = new Interval[intervals.size()];
15         intervals.toArray(arr);
16         Arrays.sort(arr, new IntervalCmp());
17         int start = arr[arr.length - 1].start;
18         int end = arr[arr.length - 1].end;
19         for (int i = arr.length - 2; i >= 0; i--) {
20             if (arr[i].end < end) {
21                 ret.add(new Interval(start, end));
22                 start = arr[i].start;
23                 end = arr[i].end;
24             }
25         }
26         ret.add(new Interval(start, end));
27         return ret;
28     }
29 }
posted @ 2013-12-26 14:47 Meng Lee 阅读(1785) | 评论 (0)编辑 收藏

Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[
     [2],
    [3,4],
   [6,5,7],
  [4,1,8,3]
]
The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11).
Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.

本题本来的想法是用递归做,实现代码如下:
 1 public class Solution {
 2     public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
 3         int row = triangle.size();
 4         return findMinPath(triangle, 0, 0, row);
 5     }
 6 
 7     private int findMinPath(ArrayList<ArrayList<Integer>> triangle, int row,
 8             int col, int totalRow) {
 9         if (row == totalRow - 1) {
10             return triangle.get(row).get(col);
11         } else {
12             return triangle.get(row).get(col) + Math.min(findMinPath(triangle, row + 1, col, totalRow), findMinPath(triangle, row + 1, col + 1, totalRow));
13         }
14     }
15 }
提交之后发现超时,于是考虑到可能是递归的开销问题,考虑用迭代解题。实现如下:
 1 public class Triangle {
 2     public int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
 3         int n = triangle.size() - 1;
 4         int[] path = new int[triangle.size()];
 5         for (int o = 0; o < triangle.get(n).size(); o++) {
 6             path[o] = triangle.get(n).get(o);
 7         }
 8         for (int i = triangle.size() - 2; i >= 0; i--) {
 9             for (int j = 0, t = 0; j < triangle.get(i + 1).size() - 1; j++, t++) {
10                 path[t] = triangle.get(i).get(t)
11                         + Math.min(path[j], path[j + 1]);
12             }
13         }
14         return path[0];
15     }
16 }
这个解法的核心是从叶节点自底向上构造解空间。
posted @ 2013-12-25 11:31 Meng Lee 阅读(142) | 评论 (0)编辑 收藏

Subsets的解法是从空集开始,依次取每一个元素与子集中的每个集合做并操作。实现代码如下:

 1 public class Subsets {
 2     public ArrayList<ArrayList<Integer>> subsets(int[] S) {
 3         Arrays.sort(S);
 4         ArrayList<ArrayList<Integer>> subsets = new ArrayList<ArrayList<Integer>>();
 5         subsets.add(new ArrayList<Integer>());
 6         for (int i = 0; i < S.length; i++) {
 7             int size = subsets.size();
 8             for (int j = 0; j < size; j++) {
 9                 ArrayList<Integer> subset = new ArrayList<Integer>(
10                         subsets.get(j));
11                 subset.add(S[i]);
12                 subsets.add(subset);
13             }
14         }
15         return subsets;
16     }
17 }

Gray Code的算法是初始时集合中只有{0, 1}两个元素,此后在每个元素的前面附加一个1。实现代码如下:

 1 public class GrayCode {
 2     public ArrayList<Integer> grayCode(int n) {
 3         ArrayList<Integer> result = new ArrayList<Integer>();
 4         if (n == 0) {
 5             result.add(0);
 6             return result;
 7         }
 8         ;
 9         result.add(0);
10         result.add(1);
11         for (int i = 1; i < n; i++) {
12             ArrayList<Integer> tmp = new ArrayList<Integer>(result);
13             Integer a = 1 << i;
14             for (int k = result.size() - 1; k >= 0; k--) {
15                 tmp.add(result.get(k) + a);
16             }
17             result = tmp;
18         }
19         return result;
20     }
21 }

posted @ 2013-12-24 12:06 Meng Lee 阅读(243) | 评论 (0)编辑 收藏