IT技术小屋

秋风秋雨,皆入我心

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

2013年12月23日 #

本文介绍了包括 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 阅读(434) | 评论 (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 阅读(1455) | 评论 (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 阅读(201) | 评论 (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 阅读(425) | 评论 (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 阅读(323) | 评论 (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 阅读(157) | 评论 (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 阅读(251) | 评论 (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 阅读(147) | 评论 (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 阅读(160) | 评论 (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 阅读(847) | 评论 (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 阅读(225) | 评论 (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 阅读(128) | 评论 (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 阅读(136) | 评论 (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 阅读(94) | 评论 (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 阅读(119) | 评论 (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 阅读(190) | 评论 (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 阅读(152) | 评论 (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 阅读(1776) | 评论 (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 阅读(138) | 评论 (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 阅读(235) | 评论 (0)编辑 收藏

Given two numbers represented as strings, return multiplication of the numbers as a string.
Note: The numbers can be arbitrarily large and are non-negative.

对每个数字相乘,记录到一个数组中,然后对这个数字的每个元素进行进位检查。主要相乘的时候要分别从两个数字的最后一位开始,还要记录偏移量。实现如下:
 1 public class MultiplyStrings {
 2     public String multiply(String num1, String num2) {
 3         int length1 = num1.length();
 4         int length2 = num2.length();
 5         int[] m = new int[length1 + length2];
 6         for (int k = length2 - 1, offset2 = 0; k >= 0; k--, offset2++) {
 7             for (int i = length1 - 1, offset1 = 0; i >= 0; i--, offset1++) {
 8                 m[length1 + length2 - 1 - offset1 - offset2] += (num1.charAt(i) - '0') * (num2.charAt(k) - '0');
 9             }
10         }
11         int add = 0;
12         for (int t = length1 + length2 - 1; t >= 0; t--) {
13             int value = m[t] + add;
14             add = value / 10;
15             m[t] = value % 10;
16         }
17         StringBuffer sb = new StringBuffer();
18         int w = 0;
19         for (; w < length1 + length2; w++) {
20             if (m[w] != 0) break;
21         }
22         for (int e = w; e < length1 + length2; e++) {
23             sb.append(m[e]);
24         }
25         return sb.length() == 0 ? "0" : sb.toString();
26     }
27 }

posted @ 2013-12-23 11:59 Meng Lee 阅读(119) | 评论 (0)编辑 收藏

Given a sorted array of integers, find the starting and ending position of a given target value.
Your algorithm's runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].
For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].

这个题目有递归和非递归两个解法。
递归解法:这个解法比较简洁,代码如下:
 1 public class SearchforaRange {
 2         public int[] searchRange(int[] A, int target) {
 3                 int low = findLow(A, target, 0, A.length - 1);
 4                 int high = findHigh(A, target, 0, A.length - 1);
 5                 int[] ret = new int[2];
 6                 ret[0] = low;
 7                 ret[1] = high;
 8                 return ret;
 9         }
10 
11         private int findLow(int[] A, int target, int l, int r) {
12                 int mid = 0;
13                 int ret = -1;
14                 while (l <= r) {
15                         mid = (l + r) / 2;
16                         if (A[mid] == target) {
17                                 ret = mid;
18                                 int next = findLow(A, target, l, mid - 1);
19                                 if (next != -1) {
20                                         ret = next;
21                                 }
22                                 break;
23                         } else if (A[mid] < target) {
24                                 l = mid + 1;
25                         } else {
26                                 r = mid - 1;
27                         }
28 
29                 }
30                 return ret;
31         }
32 
33         private int findHigh(int[] A, int target, int l, int r) {
34                 int mid = 0;
35                 int ret = -1;
36                 while (l <= r) {
37                         mid = (l + r) / 2;
38                         if (A[mid] == target) {
39                                 ret = mid;
40                                 int next = findHigh(A, target, mid + 1, r);
41                                 if (next != -1) {
42                                         ret = next;
43                                 }
44                                 break;
45                         } else if (A[mid] < target) {
46                                 l = mid + 1;
47                         } else {
48                                 r = mid - 1;
49                         }
50                 }
51                 return ret;
52         }
53 }

非递归解法:以寻找左边界为例,这个解法的思路就是一直向左进行二分查找,直到找到最左的目标元素或者最左的目标元素的下一个元素。因此,二分查找结束之后,需要判断找到的元素到底是目标元素还是他的第一个不满足的元素,然后根据情况返回下标。代码实现如下:
 1 public class Solution {
 2     public int[] searchRange(int[] A, int target) {
 3         int left = findLeft(A, target);
 4         int right = findRight(A, target);
 5         return new int[] { left, right };
 6     }
 7 
 8     private int findLeft(int[] A, int target) {
 9         int i = 0, j = A.length - 1;
10         while (i < j) {
11             int mid = (i + j) / 2;
12             if (A[mid] < target) {
13                 i = mid + 1;
14             } else {
15                 j = mid - 1;
16             }
17         }
18         if (A[i] == target) return i;
19         if (i < A.length - 1 && A[i + 1] == target) return i + 1;
20         return -1;
21     }
22 
23     private int findRight(int[] A, int target) {
24         int i = 0, j = A.length - 1;
25         while (i < j) {
26             int mid = (i + j) / 2;
27             if (A[mid] > target) {
28                 j = mid - 1;
29             } else {
30                 i = mid + 1;
31             }
32         }
33         if (j >= 0 && A[j] == target)
34             return j;
35         if (j > 0 && A[j - 1] == target)
36             return j - 1;
37         return -1;
38     }
39 }
posted @ 2013-12-23 09:58 Meng Lee 阅读(152) | 评论 (0)编辑 收藏

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You may assume no duplicates in the array.
Here are few examples.
[1,3,5,6], 5 → 2
[1,3,5,6], 2 → 1
[1,3,5,6], 7 → 4
[1,3,5,6], 0 → 0

本题先对数组进行二分搜索,如果找到了目标元素就返回相应的index,如果最终没有找到对应的元素,则比较最后一个元素与目标元素的大小。实现代码如下:
 1 public class Solution {
 2     public int searchInsert(int[] A, int target) {
 3         int length = A.length;
 4         if (A.length == 0) return 0;
 5         int i = 0, j = length - 1;
 6         while (i < j) {
 7             int mid = (i + j) / 2;
 8             if (A[mid] == target) return mid;
 9             if (A[mid] < target) {
10                 i = mid + 1;
11             } else {
12                 j = mid - 1;
13             }
14         }
15         return A[i] < target ? i + 1 : i;
16     }
17 }
posted @ 2013-12-23 09:11 Meng Lee 阅读(84) | 评论 (0)编辑 收藏