IT技術(shù)小屋

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

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

          2013年12月22日 #

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

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

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

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

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

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

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

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

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

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

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

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

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

          本題需要使用棧維護(hù)一個(gè)高度單調(diào)遞增的序列下標(biāo),如果遇到一個(gè)元素比當(dāng)前棧頂元素高度小,那么出棧,并計(jì)算當(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 閱讀(278) | 評(píng)論 (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é)點(diǎn)初始時(shí)指向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 閱讀(175) | 評(píng)論 (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的實(shí)現(xiàn),需要加一些判斷。如果碰到了重復(fù)元素,只需要在上一次迭代新增的子集的基礎(chǔ)上再進(jìn)行迭代即可。實(shí)現(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 閱讀(189) | 評(píng)論 (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.

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

          這兩個(gè)題目很相似,狀態(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和自己做匹配。實(shí)現(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 閱讀(248) | 評(píng)論 (0)編輯 收藏

          給定一個(gè)無(wú)序的整數(shù)數(shù)組,怎么找到第一個(gè)大于0,并且不在此數(shù)組的整數(shù)。比如[1,2,0] 返回 3, [3,4,-1,1] 返回 2。最好能O(1)空間和O(n)時(shí)間。
           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 閱讀(156) | 評(píng)論 (0)編輯 收藏

          3個(gè)字符串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

          分析:
          這個(gè)題目中,并沒(méi)有說(shuō)明a和b中是否有相同的字符,這個(gè)直接影響了最終的解法。所以,大家在面試的過(guò)程中,要和面試官進(jìn)行交互,弄清楚之后再動(dòng)手。a和b中不含有相同字符的情況很簡(jiǎn)單,這里略去。下面給出a和b中包含相同字符的動(dòng)態(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 閱讀(168) | 評(píng)論 (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 閱讀(121) | 評(píng)論 (0)編輯 收藏

          對(duì)一個(gè)字符串按照回文進(jìn)行分割,例如aba|b|bbabb|a|b|aba就是字符串a(chǎn)babbbabbababa的一個(gè)回文分割,每一個(gè)字串都是一個(gè)回文。請(qǐng)找到可以分割的最少的字串?dāng)?shù)。例如:
          1. ababbbabbababa最少4個(gè)字符串,分割三次:a|babbbab|b|ababa
          2. 如果字符串整體是回文,則需要0次分割,最少1個(gè)字符串

          分析:
          遞歸方程為:f(i) = MIN(f(j - 1) + 1), map[j][i] == true, 0<=j<i,其中f(i)為以第i個(gè)字符結(jié)尾的字符串的最小切割次數(shù),map數(shù)組用來(lái)記錄s[i][j]是否是對(duì)稱的。實(shí)現(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 閱讀(146) | 評(píng)論 (0)編輯 收藏

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

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

          最少插入字符

          給定字符串,可以通過(guò)插入字符,使其變?yōu)榛匚摹G笞钌俨迦胱址臄?shù)量。例如:
          1. ab最少插入1個(gè)字符,變?yōu)閎ab
          2. aa最少插入0個(gè)字符
          3. abcd最少插入3個(gè)字符,dcbabcd

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

          1、給定一組區(qū)間,表示為[start,end],請(qǐng)給出方法,將有重疊的區(qū)間進(jìn)行合并。例如:
          給定:[1,3],[2,6],[8,10],[15,18],
          合并:[1,6],[8,10],[15,18].
          分析:題目很直觀,首先把區(qū)間遞增排序,然后從頭合并,合并時(shí)觀察當(dāng)前區(qū)間的start是否小于或等于前一個(gè)區(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ā),是否有好一點(diǎn)的思路呢?
          我們可以按照start遞增排序,若start相等,則按照end遞減排序。考慮排序后的第i-1 和第i個(gè)區(qū)間,由于start是遞增的,所以第i-1個(gè)區(qū)間的start肯定小于等于第i個(gè)區(qū)間的start。若第i-1個(gè)區(qū)間的end大于等于第i個(gè)區(qū)間的end,則第i-1個(gè)區(qū)間就成為第i個(gè)區(qū)間的父區(qū)間了。

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

          本題本來(lái)的想法是用遞歸做,實(shí)現(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)超時(shí),于是考慮到可能是遞歸的開(kāi)銷問(wèn)題,考慮用迭代解題。實(shí)現(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 }
          這個(gè)解法的核心是從葉節(jié)點(diǎn)自底向上構(gòu)造解空間。
          posted @ 2013-12-25 11:31 Meng Lee 閱讀(159) | 評(píng)論 (0)編輯 收藏

          Subsets的解法是從空集開(kāi)始,依次取每一個(gè)元素與子集中的每個(gè)集合做并操作。實(shí)現(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的算法是初始時(shí)集合中只有{0, 1}兩個(gè)元素,此后在每個(gè)元素的前面附加一個(gè)1。實(shí)現(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 閱讀(257) | 評(píng)論 (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.

          對(duì)每個(gè)數(shù)字相乘,記錄到一個(gè)數(shù)組中,然后對(duì)這個(gè)數(shù)字的每個(gè)元素進(jìn)行進(jìn)位檢查。主要相乘的時(shí)候要分別從兩個(gè)數(shù)字的最后一位開(kāi)始,還要記錄偏移量。實(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 閱讀(144) | 評(píng)論 (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].

          這個(gè)題目有遞歸和非遞歸兩個(gè)解法。
          遞歸解法:這個(gè)解法比較簡(jiǎn)潔,代碼如下:
           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 }

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

          本題先對(duì)數(shù)組進(jìn)行二分搜索,如果找到了目標(biāo)元素就返回相應(yīng)的index,如果最終沒(méi)有找到對(duì)應(yīng)的元素,則比較最后一個(gè)元素與目標(biāo)元素的大小。實(shí)現(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 閱讀(109) | 評(píng)論 (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.

          本題比較簡(jiǎn)單,需要注意的是左指針右移時(shí),需要將它掠過(guò)的元素從map中移除。實(shí)現(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 閱讀(646) | 評(píng)論 (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è)置上下左右四個(gè)邊界,每次繞著這四個(gè)邊界打印元素。實(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 閱讀(692) | 評(píng)論 (0)編輯 收藏

          主站蜘蛛池模板: 长海县| 东乡| 元阳县| 农安县| 称多县| 通海县| 繁昌县| 平陆县| 尼勒克县| 婺源县| 大冶市| 南投县| 公主岭市| 峡江县| 禹城市| 河池市| 吉安市| 锦州市| 桐城市| 仪征市| 北川| 县级市| 亚东县| 南澳县| 大英县| 长沙县| 九龙坡区| 鹰潭市| 石楼县| 天柱县| 山丹县| 安多县| 邵东县| 酉阳| 乌兰察布市| 绩溪县| 郴州市| 通州区| 宝山区| 南京市| 东丰县|