IT技術(shù)小屋

          秋風(fēng)秋雨,皆入我心

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            38 隨筆 :: 1 文章 :: 19 評論 :: 0 Trackbacks

          2013年12月22日 #

          本文介紹了包括 Python、Java、Haskell等在內(nèi)的一系列編程語言的深度學(xué)習(xí)庫。

          Python
          • Theano是一種用于使用數(shù)列來定義和評估數(shù)學(xué)表達的 Python 庫。它可以讓 Python 中深度學(xué)習(xí)算法的編寫更為簡單。很多其他的庫是以 Theano 為基礎(chǔ)開發(fā)的。
          • Caffe是一種以表達清晰、高速和模塊化為理念建立起來的深度學(xué)習(xí)框架。它是由伯克利視覺和學(xué)習(xí)中心(BVLC)和網(wǎng)上社區(qū)貢獻者共同開發(fā)的。谷歌的 DeepDream 人工智能圖像處理程序正是建立在 Caffe 框架之上。這個框架是一個 BSD 許可的帶有 Python 接口的 C++庫。
          • nolearn包含大量其他神經(jīng)網(wǎng)絡(luò)庫中的包裝器和抽象(wrappers and abstractions),其中最值得注意的是 Lasagne,其中也包含一些機器學(xué)習(xí)的實用模塊。
          • Genism是一個部署在 Python 編程語言中的深度學(xué)習(xí)工具包,用于通過高效的算法處理大型文本集。
          • Chainer連接深度學(xué)習(xí)中的算法與實現(xiàn),它強勁、靈活而敏銳,是一種用于深度學(xué)習(xí)的靈活的框架。
          • deepnet是一種基于 GPU 的深度學(xué)習(xí)算法的 Python 實現(xiàn),比如:前饋神經(jīng)網(wǎng)絡(luò)、受限玻爾茲曼機、深度信念網(wǎng)絡(luò)、自編碼器、深度玻爾茲曼機和卷積神經(jīng)網(wǎng)絡(luò)。
          • Hebel是一個在 Python 中用于帶有神經(jīng)網(wǎng)絡(luò)的深度學(xué)習(xí)的庫,它通過 PyCUDA 使用帶有 CUDA 的 GPU 加速。它可實現(xiàn)大多數(shù)目前最重要的神經(jīng)網(wǎng)絡(luò)模型,提供了多種不同的激活函數(shù)和訓(xùn)練方式,如動量,Nesterov 動量,退出(dropout)和 前期停止(early stopping)。
          • CXXNET是一種快速,簡明的分布式深度學(xué)習(xí)框架,它以 MShadow 為基礎(chǔ)。它是輕量級可擴展的 C++/CUDA 神經(jīng)網(wǎng)絡(luò)工具包,同時擁有友好的 Python/Matlab 界面,可供機器學(xué)習(xí)的訓(xùn)練和預(yù)測使用。
          • DeepPy是一種建立在 Mumpy 之上的 Python 化的深度學(xué)習(xí)框架。
          • DeepLearning是一個用 C++和 Python 開發(fā)的深度學(xué)習(xí)庫。
          C++
          • eblearn是一個機器學(xué)習(xí)的開源 C++庫,由紐約大學(xué)機器學(xué)習(xí)實驗室的 Yann LeCun 牽頭研發(fā)。尤其是,按照 GUI、演示和教程來部署的帶有基于能量的模型的卷積神經(jīng)網(wǎng)絡(luò)。
          • SINGA被設(shè)計用來進行已有系統(tǒng)中分布式訓(xùn)練算法的普通實現(xiàn)。它由 Apache Software Foundation 提供支持。
          Java
          • N-Dimensional Arrays for Java (ND4J)是一種為 JVM 設(shè)計的科學(xué)計算庫。它們被應(yīng)用在生產(chǎn)環(huán)境中,這就意味著路徑被設(shè)計成可以最小的 RAM 內(nèi)存需求來快速運行。
          • Deeplearning4j是第一個為 Java 和 Scala 編寫的消費級開元分布式深度學(xué)習(xí)庫。它被設(shè)計成在商業(yè)環(huán)境中使用,而非研究工具。
          • Encog是一種先進的機器學(xué)習(xí)框架,支持支持向量機(Support Vector Machines),人工神經(jīng)網(wǎng)絡(luò)(Artificial Neural Networks),基因編程(Genetic Programming),貝葉斯網(wǎng)絡(luò)(Bayesian Networks),隱馬爾科夫模型(Hidden Markov Models)和 遺傳算法(Genetic Algorithms)。
          Lua
          • Torch是一種科學(xué)計算框架,可支持多種計算機學(xué)習(xí)算法。
          Haskell
          • DNNGraph是一個用 Haskell 編寫的深度神經(jīng)網(wǎng)絡(luò)生成 DSL。
          .NET
          • Accord.NET是一種.NET 機器學(xué)習(xí)框架,包含聲音和圖像處理庫,它完全由 C# 編寫。它是一種為開發(fā)生產(chǎn)級的計算機視覺、計算機聽覺、信號處理和統(tǒng)計應(yīng)用而設(shè)計的完整框架。
          R
          • darch包可以用于建立多層神經(jīng)網(wǎng)絡(luò)(深層結(jié)構(gòu))。其中的訓(xùn)練方式包括使用對比發(fā)散法進行提前訓(xùn)練,或使用通常的訓(xùn)練方法(如反向傳播和共軛梯度)進行一些微調(diào)。
          • deepnet實現(xiàn)了一些深度學(xué)習(xí)架構(gòu)和神經(jīng)網(wǎng)絡(luò)算法,包括 BP、RBM、DBN、深度自編碼器等等。

          posted @ 2016-11-13 00:45 Meng Lee 閱讀(455) | 評論 (0)編輯 收藏

          今天,終于有時間靜下心來回顧過去兩年來所做的事情,感慨萬千,一時之間竟不知從何說起。兩年以來,遇到的困難著實不少,但每每遭遇挫折與不順之后,卻往往能柳暗花明,遇到新的轉(zhuǎn)機,讓我真真切切地感受到了功夫不負(fù)有心人這句話的含意。

          一、為什么要出國
          其實,之前從來沒有考慮過要出國,更沒有想過能直接出國工作。回想一下,這個決定的做出,多半還是緣于自己骨子里的不安分。我從很大程度上來說是一個閑不住的人,從小學(xué)、中學(xué)、大學(xué)到研究生,我?guī)缀趺刻於加忻鞔_的目標(biāo)。然而,2013年從公司到事業(yè)單位工作以后,我的生活發(fā)生了巨大地轉(zhuǎn)變。簡單的工作、空洞的公文、無聊的活動占據(jù)了我全部的工作任務(wù)。有段時間幾乎天天寫材料搞活動。領(lǐng)導(dǎo)經(jīng)??湮也牧蠈懙糜挚煊趾?,活動也搞得有聲有色,心里感覺很有成就感。然而,時間一長,逐漸發(fā)現(xiàn)那些公文永遠是一個套路,以至于我分門別類,摸索出了幾個萬能模板。而活動則千篇一律,讓人疲于應(yīng)付。我甚至可以看到六十歲退休時我在干什么,于是一陣恐懼感常常會莫名襲來,因為我不安分、不滿足于此。我不能放棄所學(xué)所長,我不能庸庸碌碌在這里度過未來的幾十年,我還有夢想,我還要登高看世界。為了這個,我走過了不平凡的兩年。

          二、如何出國
          對于普通人來說,出國大致有三條路。
          第一條路是申請去國外留學(xué),取得學(xué)位之后以應(yīng)屆畢業(yè)生的身份找工作,然后留在國外生活。這是一條比較穩(wěn)妥、簡便的路,走這條路的人最多。
          第二條路是先進入跨國公司的中國分公司工作一段時間,然后找機會外派到國外總部工作。走這條路的要求比較多,首先要能夠進入比較大的跨國公司工作,其次這個公司愿意將中國員工transfer到國外,同時還要外國總部有部門愿意接收你,所以還是需要一些運氣。但是,如果成功,好處也顯而易見。省去了讀書的時間和學(xué)費,降低了家庭負(fù)擔(dān),對于家境一般的人是非常好的選擇。
          第三條路是直接參加外國公司的面試,通過之后直接去國外工作。這條路要求最高,需要通過外國公司嚴(yán)格的面試,另外還要能夠成功取得簽證(美國工作簽證就需要抽簽)。因此,走這條路更需要實力、機遇和運氣。
          鑒于第三條路非常難走,為了保證成功,我選擇了同時申請學(xué)校和參加外國公司面試的辦法,這也注定了我將付出更多的艱苦努力。

          三、申請學(xué)校
          申請學(xué)校從準(zhǔn)備到最終完成,我大概用了一年時間。其間參加了三次GRE和一次托??荚??;叵霚?zhǔn)備的過程,最大的敵人就是自己,最重要的法寶就是堅持堅持再堅持。記得第一次考GRE沒有取得理想的成績,因為是第一次參加英語考試,心情非常失落。幸虧當(dāng)時有女朋友(現(xiàn)在的老婆)的鼓勵,我繼續(xù)復(fù)習(xí)沒有放棄。經(jīng)過一個月的復(fù)習(xí),取得了非常不錯的托福成績。記得托福出成績的那天,我緊張得不敢查,點開頁面的那一刻,我都不敢相信居然能有這么不錯的成績。特別是聽力,考試的時候覺得好幾個都沒有聽清楚,最后居然有27分,真是不可思議,可見功夫不負(fù)有心人,付出總有回報的。
          有了英語成績之后,就是撰寫申請文書。這方面我完全沒有經(jīng)驗,所有的信息全部是通過一畝三分地論壇獲得的。這個論壇信息非常豐富,基本上所有申請相關(guān)的內(nèi)容都有涉及。我每天都會花一些時間瀏覽別人的帖子,為自己定位選校,找文書靈感等等。非常感謝我的本科和研究生導(dǎo)師,還有蔣志誠為我遞推薦信,沒有你們的幫助,我不可能完成申請工作。
          最后,我申請了美國和加拿大的十五所學(xué)校的計算機專業(yè)的研究生,拿到了CMU、USC和多倫多大學(xué)的offer。其中,CMU的Data Science program應(yīng)該是世界數(shù)一數(shù)二的,錄取率非常低,畢業(yè)后的去向也非常好,大多數(shù)都可以進入美國一流公司工作。多大也是加拿大排名第一的學(xué)校,計算機的就業(yè)也非常好。

          四、Facebook的面試
          參加Facebook的面試也完全是無意的,在Linkedin上收到了Facebook HR的邀請信,于是也沒有怎么準(zhǔn)備就做了電面,居然反饋非常好,馬上就給我安排了onsite面試,地點是印度的海得拉巴。但是,始終是沒有做什么準(zhǔn)備,而且和谷歌不一樣的是,HR辦事效率實在太高,每一輪間隔都非常短,導(dǎo)致我根本沒有時間熱身一下,連leetcode都沒有做過就匆匆參加面試了,最終沒有如愿通過面試。
          不過,這次面試還是很有收獲。第一次出國,第一次參加美國公司全英文面試,學(xué)到了太多,積累了經(jīng)驗,可以說如果沒有Facebook的失敗,我是不可能進入谷歌的。

          五、Google的面試
          參加谷歌的面試可以說完全是老婆的慫恿。從印度參加完Facebook面試回來之后,我就開始專心于學(xué)校申請了。但是,老婆建議我試試面一下Google。由于Facebook的失利和Google近乎苛刻的面試流程,我開始是抗拒參加的。最后,在老婆的一再要求下,我終于找了一個在谷歌上海工作的師兄做了內(nèi)推。四月底我收到了谷歌北京HR的第一通電話,也正式拉開了我為期一年的面試流程。
          和HR通電話不久,我進行了第一次電話面試。谷歌的電話面試和Facebook差不多,就是面試官打過來,把題目口述并且寫在Google Doc上,然后我把程序?qū)懺贕oogle Doc上。第一次電面的題目不難,但谷歌對代碼效率和清晰度的要求遠遠超出了我的想像。第一輪面得磕磕絆絆,但是幸好面試官是中國人,非常nice,沒有讓我fail。
          于是,我又被要求進行第二次電面。期間由于面試官臨時有事爽約,我等了差不多一個月。但是,也就是這一個月,我努力做了一些準(zhǔn)備,雖然面試依舊不是十全十美,但是我還是有驚無險地進入到了onsite面試環(huán)節(jié)。
          雖然可以onsite面試了,但是我依舊對進入谷歌不報任何希望,因為我清楚的知道,谷歌面試實在是太難了,onsite面試的挑戰(zhàn)將遠遠大于電面。因此,我去北京面試完全是想做一次免費旅游。面試前一天還許久不見的萬威夫婦吃飯,聊得很開心,完全沒有把面試放在心上。
          也許是放松的原因,我前一天晚上睡得很好,第二天我精神非常好。
          不過谷歌畢竟是谷歌,面試第一輪一上來就給了我一個下馬威。一個coding題一個設(shè)計題,表面上很簡單,但是做出來總是有這樣那樣的問題,第一輪完了之后我基本打算回家了。
          但是,不知道為什么,從第二輪開始,就越來越順利,coding做得非常好,基本上是一次寫完,沒有涂改,也沒有被面試官找到大的bug。突然之間,隱隱感覺出現(xiàn)了一絲希望。
          四輪過后,我結(jié)束了第一次onsite面試。但是,三天之后,我被告知由于設(shè)計題做得不好,我被要求進行一次加面,地點在上海。于是,我又在上海做了一次面試,只有一個設(shè)計題。我感覺答得還可以,但是心情真的是忐忑不安,特別是接下來的一個禮拜,幾乎是坐立不安。
          記得是一個禮拜之后的禮拜五中午,我正做準(zhǔn)備主持下午的道德講堂,突然接到了一個010的電話,我知道是谷歌的電話。接通電話的那一刻,空氣都幾乎要凝固了,當(dāng)聽到通過HC的消息時,我激動得不能自已。不可能完成的任務(wù)居然完成了,雖然不知道能不能去美國總部工作,但是能進入谷歌已經(jīng)非常不容易了,而且谷歌非常鼓勵transfer去美國工作,因此機會還是很多的。
          然而,讓我沒有想到的是,接下來的team match卻異常艱難,陸陸續(xù)續(xù)幾個team都沒有成功match上。轉(zhuǎn)眼就到了2014年春季,半年的等待讓我對何時進入谷歌非常悲觀,加上申請學(xué)校工作十分繁重,我基本沒有關(guān)注這個事情。
          就在我快要放棄的時候,拿到了美國一個公司的offer,他們答應(yīng)給我辦H1B簽證。于是,我把這個情況告訴了谷歌,要求他們盡快給找到team,不然我就去美國了。結(jié)果谷歌居然在三天之內(nèi)為我match上了英國office的一個team,讓人不得不感嘆還是要offer多才好?。∮谑?,我又經(jīng)過了近三個月的簽證辦理流程,終于要啟程赴英了。

          回顧兩年來的努力,終于要實現(xiàn)自己的夢想了,感慨萬千。在短短的人生中,能有這一段不尋常的經(jīng)歷,我覺得十分幸運。展望未來,我想讀萬卷書不如行萬里路,未來希望能夠利用在倫敦工作的機會,盡量多去歐洲各國走走,豐富自己的閱歷,開拓自己的眼界。

          最后要感謝老婆一直以來的支持和鼓勵,你一直是我前進的動力;其次要感謝父母的不理解和不支持,你們的反對讓我更加完善了自己的計劃,逼著我找到了一條最好的出路;還要感謝師長和朋友們的幫助,感謝楊老師和沈老師還有蔣志誠不厭其煩地幫我遞推薦信,感謝萬威夫婦多次在北京款待我,沒有你們的美食,我是不可能完成面試的;還有許多幫助過我的人,在這里就不能一一感謝了。
          posted @ 2014-06-13 02:00 Meng Lee 閱讀(1494) | 評論 (0)編輯 收藏

          算法很簡單,核心思想是:對某個值A(chǔ)[i]來說,能trapped的最多的water取決于在i之前最高的值leftMostHeight[i]和在i右邊的最高的值rightMostHeight[i]。(均不包含自身)。如果min(left,right) > A[i],那么在i這個位置上能trapped的water就是min(left,right) – A[i]。
          有了這個想法就好辦了,第一遍從左到右計算數(shù)組leftMostHeight,第二遍從右到左計算rightMostHeight,在第二遍的同時就可以計算出i位置的結(jié)果了,而且并不需要開空間來存放rightMostHeight數(shù)組。
          時間復(fù)雜度是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 閱讀(224) | 評論 (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.

          這道題其實有很強的規(guī)律可循。首先,n個元素的排列總數(shù)是n!。在下面的分析中,讓k的范圍是0 <= k < n!。(題目代碼實際上是1<=k<=n!)
          可以看到一個規(guī)律,就是這n!個排列中,第一位的元素總是(n-1)!一組出現(xiàn)的,也就說如果p = k / (n-1)!,那么排列的最開始一個元素一定是arr[p]。
          這個規(guī)律可以類推下去,在剩余的n-1個元素中逐漸挑選出第二個,第三個,...,到第n個元素。程序就結(jié)束。
           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 閱讀(451) | 評論 (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 閱讀(346) | 評論 (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 閱讀(223) | 評論 (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.

          本題需要使用棧維護一個高度單調(diào)遞增的序列下標(biāo),如果遇到一個元素比當(dāng)前棧頂元素高度小,那么出棧,并計算當(dāng)前最大面積。如果棧為空,需要特殊考慮。
           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 閱讀(274) | 評論 (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節(jié)點初始時指向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 閱讀(171) | 評論 (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],
            []
          ]

          由于元素中可能存在重復(fù),因此較之于Subset的實現(xiàn),需要加一些判斷。如果碰到了重復(fù)元素,只需要在上一次迭代新增的子集的基礎(chǔ)上再進行迭代即可。實現(xiàn)代碼如下:
           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 閱讀(183) | 評論 (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.

          這個題目應(yīng)該算是leetcode上比較難的題目了。剛開始我采用了和Word Ladder相似的做法,只是用ArrayList記錄了當(dāng)前變換路徑,在小數(shù)據(jù)的情況下可以Accept,但是大數(shù)據(jù)超時。究其原因,是由于為每個當(dāng)前節(jié)點記錄變換路徑的時候,需要復(fù)制之前的ArrayList,這個時間開銷較大。
          其實,我們可以采用一個Map<String, HashSet<String>>結(jié)構(gòu),記錄字典單詞的每一個前驅(qū),這樣我們可以從end反向遍歷,構(gòu)造出轉(zhuǎn)換路徑。
          同時,我利用了兩個ArrayList,交替記錄上一層和下一層的節(jié)點,如果下一層節(jié)點為空,則不存在路徑,立即返回。如果下一層中出現(xiàn)了end,證明找到了所有的最短路徑,停止搜索開始構(gòu)造路徑。實現(xiàn)代碼如下:
           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 閱讀(868) | 評論 (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.

          這兩個題目很相似,狀態(tài)方程是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].
          數(shù)組初始情況是第一列全部是1,代表T字符串是空串,S和自己做匹配。實現(xiàn)代碼如下:
           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 閱讀(244) | 評論 (0)編輯 收藏

          給定一個無序的整數(shù)數(shù)組,怎么找到第一個大于0,并且不在此數(shù)組的整數(shù)。比如[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 閱讀(151) | 評論 (0)編輯 收藏

          3個字符串a(chǎn),b,c。判斷c是否是a和b的interleave,也就是c中應(yīng)該有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中包含相同字符的動態(tài)規(guī)劃的解法。

           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 閱讀(163) | 評論 (0)編輯 收藏

          刪除字符串中的“b”和“ac”,需要滿足如下的條件:
          1. 字符串只能遍歷一次
          2. 不能夠?qū)嵱妙~外的空間

          例如:
          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 閱讀(114) | 評論 (0)編輯 收藏

          對一個字符串按照回文進行分割,例如aba|b|bbabb|a|b|aba就是字符串a(chǎn)babbbabbababa的一個回文分割,每一個字串都是一個回文。請找到可以分割的最少的字串?dāng)?shù)。例如:
          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個字符結(jié)尾的字符串的最小切割次數(shù),map數(shù)組用來記錄s[i][j]是否是對稱的。實現(xiàn)代碼如下:
           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 閱讀(142) | 評論 (0)編輯 收藏

          求二叉搜索樹的中序后繼節(jié)點

          分析:
          1. 如果目標(biāo)節(jié)點的右子樹不為空,則返回右子樹的最小節(jié)點;
          2. 如果目標(biāo)節(jié)點的右子樹為空,則從根節(jié)點開始遍歷。
          實現(xiàn)代碼如下:
           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 閱讀(208) | 評論 (0)編輯 收藏

          最少插入字符

          給定字符串,可以通過插入字符,使其變?yōu)榛匚?。求最少插入字符的?shù)量。例如:
          1. ab最少插入1個字符,變?yōu)閎ab
          2. aa最少插入0個字符
          3. abcd最少插入3個字符,dcbabcd

          分析
              這個題目的分析思路,和前面兩期是非常相似的:給出遞歸的解法,發(fā)現(xiàn)重復(fù)的子問題,改進為動態(tài)規(guī)劃的解法,這是一個分析的過程,待同學(xué)們比較熟悉時候,可以直接給出動態(tài)規(guī)劃的解決方案,就很好了。
              這個題目,遞歸該如何解呢?給定一個字符串str,長度為n,怎么插入最少的字符,是的字符串變?yōu)榛匚哪??插入最少的字符,就是要盡量利用原來的字符,在原字符串str中,盡量利用更多能夠匹配的字符。怎么對這個問題進行分解呢?考慮str字符串整體:
              1. 如果str[0]==str[n-1],則問題轉(zhuǎn)變?yōu)榍髎tr[1,n-2],插入最少字符,得到回文
              2. 如果str[0]!=str[n-1],則需要插入一個字符要么和str[0]相同,要么和str[n-1]相同,
              3. 如果和str[0],則轉(zhuǎn)變?yōu)閟tr[1,n-1],插入最少字符,得到回文
              4. 如果和str[n-1],則轉(zhuǎn)變?yōu)閟tr[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)
              通過上面的式子,有經(jīng)驗的、熟練的同學(xué),很直接的就能看出來,存在重復(fù)的子問題,這就意味著,我們可以講子問題的解緩存使用。如果,沒有直接能夠看出來的同學(xué)們,還是可以按照我們之前的方法,把遞歸樹畫出來吧,那樣更加一目了然。
              那么,這個題目該如何用動態(tài)規(guī)劃的解決呢?如何重復(fù)利用子問題的解呢?似乎有些不那么直接。但其實也是于規(guī)律可循的。上面的遞歸式,是從字符串的兩 邊,想中間移動遞歸,根據(jù)動態(tài)規(guī)劃解決問題的思想,我們先解決子問題,再重復(fù)利用子問題,就是要從內(nèi)向外解決,大家還記得回文子串判斷的那個題目么,動態(tài) 規(guī)劃解法的外層循環(huán)是子串的長度,這個題目也是類似的。示例代碼如下:
           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 閱讀(172) | 評論 (0)編輯 收藏

          1、給定一組區(qū)間,表示為[start,end],請給出方法,將有重疊的區(qū)間進行合并。例如:
          給定:[1,3],[2,6],[8,10],[15,18],
          合并:[1,6],[8,10],[15,18].
          分析:題目很直觀,首先把區(qū)間遞增排序,然后從頭合并,合并時觀察當(dāng)前區(qū)間的start是否小于或等于前一個區(qū)間的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、給定的一組區(qū)間,將區(qū)間中的存在的任意區(qū)間的父區(qū)間刪除,例如:
          給定:[1,2] [1,3] [1,4] [5,9] [6,8]
          刪除后:[1,2] [6,8]
          分析:此題暴力的解法的復(fù)雜度為O(N^2)。受上一題排序的啟發(fā),是否有好一點的思路呢?
          我們可以按照start遞增排序,若start相等,則按照end遞減排序??紤]排序后的第i-1 和第i個區(qū)間,由于start是遞增的,所以第i-1個區(qū)間的start肯定小于等于第i個區(qū)間的start。若第i-1個區(qū)間的end大于等于第i個區(qū)間的end,則第i-1個區(qū)間就成為第i個區(qū)間的父區(qū)間了。

          按照這個思路,可以試著在排序之后逆向順序處理每一個區(qū)間。假設(shè)當(dāng)前處理第i個區(qū)間,如前所述,若第i-1個區(qū)間的end大于等于第i個區(qū)間的end,則第i-1個區(qū)間成為第i個區(qū)間的父區(qū)間,可以保留第i個區(qū)間,將第i-1個區(qū)間刪除。由于第i-1個區(qū)間是第i個區(qū)間的父區(qū)間,所以第i-1個區(qū)間的父區(qū)間也是第i個區(qū)間的父區(qū)間,這種情形下刪掉第i-1個區(qū)間,后續(xù)不會漏刪第i-1個區(qū)間的父區(qū)間。若第i-1個區(qū)間的end小于第i個區(qū)間的end,則可以跳過第i個區(qū)間,開始處理第i-1個區(qū)間。因為按照這種處理的方法,在處理到第i個區(qū)間時,該區(qū)間不會是任何區(qū)間的父區(qū)間(若是父區(qū)間已經(jīng)被如前所述的方法刪除了)。而且,在這種情形下,后續(xù)可能出現(xiàn)的第i個區(qū)間的父區(qū)間會是第i-1個區(qū)間的父區(qū)間,所以也不會漏掉第i個區(qū)間的父區(qū)間。所以排序之后逆向處理,只需要O(N)的時間,就可以解決這道問題。整體的時間復(fù)雜度為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 閱讀(1797) | 評論 (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.

          本題本來的想法是用遞歸做,實現(xiàn)代碼如下:
           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 }
          提交之后發(fā)現(xiàn)超時,于是考慮到可能是遞歸的開銷問題,考慮用迭代解題。實現(xiàn)如下:
           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 }
          這個解法的核心是從葉節(jié)點自底向上構(gòu)造解空間。
          posted @ 2013-12-25 11:31 Meng Lee 閱讀(155) | 評論 (0)編輯 收藏

          Subsets的解法是從空集開始,依次取每一個元素與子集中的每個集合做并操作。實現(xiàn)代碼如下:

           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。實現(xiàn)代碼如下:

           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 閱讀(253) | 評論 (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.

          對每個數(shù)字相乘,記錄到一個數(shù)組中,然后對這個數(shù)字的每個元素進行進位檢查。主要相乘的時候要分別從兩個數(shù)字的最后一位開始,還要記錄偏移量。實現(xiàn)如下:
           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 閱讀(138) | 評論 (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 }

          非遞歸解法:以尋找左邊界為例,這個解法的思路就是一直向左進行二分查找,直到找到最左的目標(biāo)元素或者最左的目標(biāo)元素的下一個元素。因此,二分查找結(jié)束之后,需要判斷找到的元素到底是目標(biāo)元素還是他的第一個不滿足的元素,然后根據(jù)情況返回下標(biāo)。代碼實現(xiàn)如下:
           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 閱讀(169) | 評論 (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

          本題先對數(shù)組進行二分搜索,如果找到了目標(biāo)元素就返回相應(yīng)的index,如果最終沒有找到對應(yīng)的元素,則比較最后一個元素與目標(biāo)元素的大小。實現(xiàn)代碼如下:
           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 閱讀(103) | 評論 (0)編輯 收藏

          Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for "abcabcbb" is "abc", which the length is 3. For "bbbbb" the longest substring is "b", with the length of 1.

          本題比較簡單,需要注意的是左指針右移時,需要將它掠過的元素從map中移除。實現(xiàn)代碼如下:
           1 public class Solution {
           2     public int lengthOfLongestSubstring(String s) {
           3         int length = s.length();
           4         if (length == 0) return 0;
           5         Map<Character, Integer> map = new HashMap<Character, Integer>();
           6         int ret = 0;
           7         int count = 0;
           8         int start = 0;
           9         for (int i = 0; i < length; i++) {
          10             char c = s.charAt(i);
          11             if (map.containsKey(c)) {
          12                 int newStart = map.remove(c).intValue() + 1;
          13                 for (int j = start; j < newStart; j++) {
          14                     map.remove(s.charAt(j));
          15                 }
          16                 start = newStart;
          17                 ret = ret < count ? count : ret;
          18                 count = i - start + 1;
          19             } else {
          20                 count++;
          21             }
          22             map.put(c, i);
          23         }
          24         return ret < count ? count : ret;
          25     }
          26 }
          posted @ 2013-12-22 20:07 Meng Lee 閱讀(641) | 評論 (0)編輯 收藏

          Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.
          For example,
          Given the following matrix:
          [
           [ 1, 2, 3 ],
           [ 4, 5, 6 ],
           [ 7, 8, 9 ]
          ]
          You should return [1,2,3,6,9,8,7,4,5].

          可以為矩陣設(shè)置上下左右四個邊界,每次繞著這四個邊界打印元素。實現(xiàn)代碼如下:
           1 public class Solution {
           2     public ArrayList<Integer> spiralOrder(int[][] matrix) {
           3         ArrayList<Integer> ret = new ArrayList<Integer>();
           4         if (matrix.length == 0) return ret;
           5         int upper = 0, bottom = matrix.length - 1;
           6         int left = 0, right = matrix[0].length - 1;
           7         int i = 0, j = 0;
           8         while (true) {
           9             for (i = left; i <= right; i++) {
          10                 ret.add(matrix[upper][i]);
          11             }
          12             if (++upper > bottom) break;
          13             for (j = upper; j <= bottom; j++) {
          14                 ret.add(matrix[j][right]);
          15             }
          16             if (--right < left) break;
          17             for (i = right; i >= left; i--) {
          18                 ret.add(matrix[bottom][i]);
          19             }
          20             if (--bottom < upper) break;
          21             for (j = bottom; j >= upper; j--) {
          22                 ret.add(matrix[j][left]);
          23             }
          24             if (++left > right) break;
          25         }
          26         return ret;
          27     }
          28 }
          posted @ 2013-12-22 16:59 Meng Lee 閱讀(686) | 評論 (0)編輯 收藏

          主站蜘蛛池模板: 诸城市| 北安市| 嘉荫县| 微山县| 奉化市| 陇西县| 名山县| 六安市| 四子王旗| 甘谷县| 郴州市| 宜宾市| 黄龙县| 宜川县| 泸西县| 称多县| 游戏| 天镇县| 文化| 育儿| 正安县| 忻城县| 五指山市| 陆丰市| 汪清县| 都兰县| 十堰市| 呼和浩特市| 尤溪县| 措勤县| 永春县| 班戈县| 嘉善县| 綦江县| 安徽省| 应城市| 河东区| 巴彦县| 晋江市| 宜州市| 湛江市|