講一講到底如何“解決問(wèn)題”,本文非常非常非常重要,是我很長(zhǎng)一段時(shí)間的心血與積累,大家一定要耐心看完,共包含3節(jié):
算法對(duì)個(gè)人的意義
解決問(wèn)題的策略
算法問(wèn)題匯總
01 PART 算法對(duì)個(gè)人的意義
在實(shí)際項(xiàng)目中,算法的使用場(chǎng)景有很多,如“Java8中Hashmap使用紅黑樹(shù)來(lái)實(shí)現(xiàn)”、“Redis底層使用LRU來(lái)進(jìn)做淘汰策略”、“大數(shù)據(jù)領(lǐng)域很多問(wèn)題都基于TopK”、“JS原型鏈里使了類似鏈表的成環(huán)檢測(cè)”、“特別復(fù)雜的業(yè)務(wù)邏輯經(jīng)常涉及到DAG”、“MySql為什么索引要用B+樹(shù)”、“Oracle里的開(kāi)窗函數(shù)如何實(shí)現(xiàn)” 等等等等,這些今天我們統(tǒng)統(tǒng)不談。而我,更多的是想和大家聊一聊,算法對(duì)個(gè)人有什么意義?
市面上大部分的算法書(shū)籍,第一章介紹算法,都會(huì)給大家列一列類似上面的那些話,或者就是使用棧或隊(duì)列來(lái)做一個(gè)引子,告訴大家算法很重要,你得需要去學(xué),吧啦吧啦....但是不知道大家有沒(méi)有想過(guò)這樣一個(gè)問(wèn)題,算法對(duì)于個(gè)人而言到底有什么意義呢?如果這個(gè)問(wèn)題大家陌生,那你一定會(huì)聽(tīng)到有寫(xiě)了幾年業(yè)務(wù)邏輯的老程序員,說(shuō)過(guò)“我這些年從來(lái)沒(méi)有用過(guò)算法,除了出去面試的時(shí)候”之類的話。其實(shí),我這里真想說(shuō)一句臟話,這些思想真的是TMD害人不淺啊。甚至我懷疑大多數(shù)說(shuō)這句話本身的人,有兩種:一種就是嚴(yán)重缺乏自信心,覺(jué)得自己一輩子都沒(méi)辦法學(xué)好算法了,所以就這樣吧。第二種就是故意誤人子弟,驅(qū)動(dòng)來(lái)源于自己不會(huì),方式采用侃大山,反正忽悠一個(gè)是一個(gè),再來(lái)身邊也沒(méi)有其他這方面厲害的朋友,說(shuō)完之后自己都沒(méi)意識(shí)到哪里有問(wèn)題,卻對(duì)別人帶去很不好的影響。所以如果你今天看到我的這篇文章,我希望你能記住一世,這輩子都不要說(shuō)出這種類似的話來(lái),保持對(duì)這個(gè)學(xué)科基本的尊重,哪怕多一點(diǎn)點(diǎn)匠心精神。算法對(duì)個(gè)人的意義如下:
算法題目的程序規(guī)模大多都是比較小的,也就意味著切入點(diǎn)很小。使得每一個(gè)做題人,可以最大化的投入時(shí)間研究問(wèn)題的本身。而在工作中,稍大一點(diǎn)的項(xiàng)目,基本上是沒(méi)辦法隨意改變代碼結(jié)構(gòu)的,甚至還會(huì)為了整體性能犧牲程序的簡(jiǎn)潔與優(yōu)雅。所以算法題是可以讓你通過(guò)練習(xí)編寫(xiě)出好代碼的最好的方式,沒(méi)有之一。
算法題目中基本不會(huì)有圖形化界面,只利用文本進(jìn)行輸入和輸出。你可以相當(dāng)專注的去解決問(wèn)題。而在工作中,你能獲得專注去研究一個(gè)問(wèn)題的機(jī)會(huì),幾乎很難。想一想,假如你用JAVA寫(xiě)一個(gè)后臺(tái)功能,其核心代碼不到10行邏輯,但是MVC得占據(jù)你三分之一工作量,定義接口占據(jù)你三分之一工作量,公司假如沒(méi)前端,再占據(jù)你三分之一工作量。整個(gè)這個(gè)過(guò)程,我有一個(gè)Amazon的朋友形容的很貼切,“掏糞”。
預(yù)測(cè)能力的構(gòu)建,在大多數(shù)算法練習(xí)平臺(tái)中,因?yàn)闀?huì)將運(yùn)算時(shí)間和內(nèi)存使用狀況等信息實(shí)時(shí)提供給做題人,所以做題人甚至可以一邊修改代碼,一邊觀察修改對(duì)程序產(chǎn)生的影響。這個(gè)是不得了的,在工作中,絕對(duì)不可能有這樣的機(jī)會(huì)。而在這個(gè)過(guò)程中,做題人可以提高對(duì)邏輯結(jié)構(gòu)復(fù)雜程序進(jìn)行性能預(yù)測(cè)的能力,該能力將伴隨其一身。
提升coding能力的最好方式。假如我們打王者榮耀,你要上王者,不開(kāi)排位,一直打電腦,能上的去嗎?在工作中,你來(lái)回接觸的就那么幾個(gè)人,有幾個(gè)能寫(xiě)出特別優(yōu)秀的代碼,見(jiàn)到了,那說(shuō)明老天眷顧你,大部分人都見(jiàn)不到。但是在算法平臺(tái)的練習(xí)中,基本上我們每一個(gè)問(wèn)題,我們都能看到全世界最優(yōu)秀的人提交的代碼。沒(méi)有對(duì)比,雖然不成傷害,卻更難成為進(jìn)步!只有我們?nèi)ラ喿x別人優(yōu)秀的邏輯,讀懂別人思考的過(guò)程,與全世界頂尖的程序員編寫(xiě)代碼的能力進(jìn)行比較,才可以成為真正的大牛。
算法題讓你難受。用腳指頭想一個(gè)問(wèn)題,在各行各業(yè)中,想成為其行業(yè)的佼佼者,是不是一定有一個(gè)難受的過(guò)程。假如天天寫(xiě)CRUD,并且還得意洋洋,我用一套Generator生成只需要5分鐘,其他時(shí)間就可以打打爐石,勾搭勾搭妹子。不經(jīng)歷一個(gè)難受的過(guò)程,如何可以進(jìn)步?就連郭德綱出名之前,也在玻璃窗里被關(guān)過(guò)兩天兩夜。羅馬不是一天建成的,但是如果不修,那就永遠(yuǎn)建不成。難受就是真理,說(shuō)明你正在進(jìn)步。
單測(cè)都是“騙人的”。請(qǐng)大家不要高估工作中QA的能力(當(dāng)然,也有牛逼的QA,我見(jiàn)過(guò)...),大部分的公司里,QA來(lái)做單測(cè)時(shí),基本上是重新走了一遍開(kāi)發(fā)者的邏輯。更有甚者,開(kāi)發(fā)直接說(shuō)出“我寫(xiě)完都已經(jīng)測(cè)完了,要QA有什么用處”,其實(shí)這并不是一個(gè)段子,因?yàn)榇蟛糠諵A是做不到完美的cover業(yè)務(wù)邏輯的,換句話說(shuō),也就不可能構(gòu)建出完美的測(cè)試用例測(cè)出你代碼的問(wèn)題。但是算法不是,大部分的算法平臺(tái),都提供了實(shí)時(shí)反饋的機(jī)制,如果自己編寫(xiě)的代碼可以得到快速,客觀的意見(jiàn)反饋,這絕對(duì)是有如神助。就好像是你打王者,旁邊有個(gè)小精靈,總是會(huì)在合適的時(shí)機(jī)告訴你,“去下路,中路沒(méi)人”,“小心草叢”。那如果不被帶飛,你信嗎?
總之,正是因?yàn)樗惴}目中只保留了必備的要素,舍棄了其他無(wú)關(guān)緊要的部分,所以可以對(duì)每一位做題人都構(gòu)建一個(gè)學(xué)習(xí)解決問(wèn)題的最佳環(huán)境,而在這個(gè)環(huán)境中的成長(zhǎng)與提高,將對(duì)一個(gè)軟件工程師的生涯產(chǎn)生深遠(yuǎn)影響,乃至一世。所以,請(qǐng)大家能有一顆匠心,你可以選擇不去了解學(xué)習(xí)掌握算法,但是請(qǐng)不要耽誤他人進(jìn)步。山高水長(zhǎng),江湖路遠(yuǎn),珍重萬(wàn)千,有緣再見(jiàn)!
02 PART 解決問(wèn)題的策略
解決簡(jiǎn)單的問(wèn)題時(shí),直接利用已知的技術(shù)便可輕松解決問(wèn)題。但是如果遇到難題,恐怕就需要用各種手段。管他是花貓野貓,抓住耗子都是好貓。在解決問(wèn)題的策略構(gòu)建中,我們首先要對(duì)問(wèn)題和答案結(jié)構(gòu)有一個(gè)直觀的感受,或者說(shuō)猜測(cè)。如果可以把控住當(dāng)前算法的問(wèn)題,具備一個(gè)什么樣的形態(tài),然后就可以把毫無(wú)頭緒的事情,變得有跡可循。在這樣一個(gè)過(guò)程中,一點(diǎn)點(diǎn)的積累經(jīng)驗(yàn),最終就可以提升自己。
上面說(shuō)的內(nèi)容,玄而又玄,那到底如何來(lái)構(gòu)建策略。假如我們遇到一個(gè)問(wèn)題,讓我們找一個(gè)國(guó)家鐵路網(wǎng)中,兩個(gè)城市的最短路徑。這種問(wèn)題,大家肯定首先想到的就是使用迪杰斯特拉算法。但是如果問(wèn)題變成“換乘火車次數(shù)少于N次,尋找最短路徑”,這種問(wèn)題將不能直接套用最短路徑的算法。有時(shí)候只是改變題中條件,就可以讓整個(gè)題目完全走向另一個(gè)邏輯,這就需要大家對(duì)原算法的原理和執(zhí)行過(guò)程特別了解,并且熟讀題意。所以這里我們抽象出兩個(gè)步驟:
讀題
重構(gòu)
讀題的目的,就是閱讀并理解問(wèn)題。不管是是不是算法老手,在這上面栽跟頭的絕對(duì)不是一個(gè)兩個(gè),審題不清是所有人的共性(絕不是你的個(gè)人問(wèn)題),人的天性就期望可以快速得到反饋,這是身體欲望導(dǎo)致的,和你看到漂亮的妹子下半身豎立本質(zhì)沒(méi)有什么區(qū)別。所以這就引出我們的解決方法 - 重構(gòu)。
什么是重構(gòu),重構(gòu)其實(shí)就是一個(gè)抽象化的過(guò)程。借用我們已經(jīng)掌握的數(shù)學(xué)/計(jì)算機(jī)知識(shí),將其表達(dá)成現(xiàn)實(shí)世界概念。大部分的現(xiàn)實(shí)世界概念都是比較復(fù)雜的,我們對(duì)其抽繭剝絲,保留本質(zhì),表述成易于理解的形式。而對(duì)其重構(gòu)的過(guò)程,就可以決定其程序設(shè)計(jì)的方向。舉一個(gè)最簡(jiǎn)單的例子,比如我們要用代碼實(shí)現(xiàn)一個(gè)整數(shù)平方根的開(kāi)方,可以選用牛頓法或者二分法,那這兩種方法是如何被想到的。假如我們把問(wèn)題重構(gòu)成圖形的表達(dá)方式,就比較容易會(huì)推出牛頓法。假如我們把問(wèn)題重構(gòu)成已有的知識(shí)概念,自然就可以想到二分法。而如果我們把問(wèn)題抽象成公式,甚至可以通過(guò)數(shù)學(xué)法來(lái)進(jìn)行解題。劃重點(diǎn),不同的重構(gòu)方式,決定最終程序的走向。
在重構(gòu)的基礎(chǔ)上,其實(shí)我們就可以來(lái)進(jìn)行解題了。但是這里我還要對(duì)其加一個(gè)步驟,化簡(jiǎn)。什么又是化簡(jiǎn),如何化簡(jiǎn)?假如我們有一個(gè)題,我們有一個(gè)二維網(wǎng)格,里邊有N個(gè)點(diǎn),兩點(diǎn)的距離是X坐標(biāo)和Y坐標(biāo)的的和。比如坐標(biāo)(5,1)和(4,7)的點(diǎn)間距就是1+6=7。我們要找到給出的N個(gè)點(diǎn)距離之和最小的新點(diǎn)的坐標(biāo)。
題目因?yàn)楸旧硎嵌S的,我們寫(xiě)代碼其實(shí)不是很好寫(xiě)。所以我們可以將其化簡(jiǎn)為一維。我們把每一個(gè)點(diǎn)的左邊,通過(guò)映射的方式,分別映射到 x軸 和 y軸。然后我們把問(wèn)題轉(zhuǎn)化成在直線上尋找到給出點(diǎn)的距離之和最小的點(diǎn)。這就是化簡(jiǎn)。萬(wàn)物之始,大道至簡(jiǎn),至拙至美。生活中咱們也說(shuō)透過(guò)現(xiàn)象看本質(zhì),放在算法里你就不會(huì)了?
讀題-重構(gòu)-化簡(jiǎn),下來(lái)自然就是解題了。那如何解題?這個(gè)范疇雖然很大,但也不是無(wú)跡可尋,下面兩點(diǎn):
基本數(shù)據(jù)結(jié)構(gòu)和算法的掌握(略)
常見(jiàn)算法問(wèn)題的匯總
基本數(shù)據(jù)結(jié)構(gòu)和算法的掌握,這個(gè)自不必說(shuō),是人都知道,那么常見(jiàn)算法問(wèn)題的匯總又是什么,我們看下一節(jié)。
03 PART 算法問(wèn)題匯總
寫(xiě)算法最怕什么?當(dāng)然是出錯(cuò)。與其重復(fù)相同的錯(cuò)誤,不如從錯(cuò)誤中吸取教訓(xùn)。與其從自己的錯(cuò)誤中吸取教訓(xùn),不如從別人的錯(cuò)誤中學(xué)習(xí)經(jīng)驗(yàn)。總結(jié)常見(jiàn)的算法問(wèn)題,我總不能和你去說(shuō)我們需要掌握數(shù)組、鏈表、二叉樹(shù)、Map等這些數(shù)據(jù)結(jié)構(gòu)。我認(rèn)為的總結(jié),就是錯(cuò)誤的總結(jié)。所以為了快速拔高大家的水準(zhǔn),我準(zhǔn)備了以下這些錯(cuò)誤,請(qǐng)一定耐心看完,反復(fù)閱讀。
遞歸,防止死循環(huán)和內(nèi)存泄露。由于遞歸需要堆棧,所以內(nèi)存消耗要比非遞歸代碼要大很多。而且,如果遞歸深度太大,可能系統(tǒng)撐不住。內(nèi)存會(huì)存在突然飆升的情況。如果是數(shù)據(jù)錯(cuò)誤導(dǎo)致無(wú)限循環(huán),那問(wèn)題就大了。所以這方面問(wèn)題在開(kāi)發(fā)的時(shí)候需要注意。
訪問(wèn)數(shù)組越界,絕大多數(shù)的數(shù)組越界,根本原因在于對(duì)定義混淆。比如定義的時(shí)候想的是“以0起始”,但是寫(xiě)的時(shí)候?qū)懗闪恕耙?起始”。究其根本,數(shù)組越界的問(wèn)題,其實(shí)是對(duì)區(qū)間把控的問(wèn)題。
區(qū)間表意不明。大部分的語(yǔ)言中,數(shù)組都以0為起始元素,但是人的思維習(xí)慣于以1為起始。那為什么數(shù)組要以0為起始元素,歷史原因有很多,咱不說(shuō)。對(duì)咱們有用的,3個(gè)。第一,因?yàn)槲覀冞x擇左閉右開(kāi)區(qū)間,比如 [0,n),我們可以很容易通過(guò) n-0 得到數(shù)組中元素個(gè)數(shù)。這點(diǎn)大家要形成條件發(fā)射,看到數(shù)組,明確其個(gè)數(shù)。第二,閉區(qū)間很難去表示一個(gè)空數(shù)組,不信你試試,非常難受。第三,左閉右開(kāi)的區(qū)間,迭代器只需要最少的操作符。可以讓代碼寫(xiě)起來(lái)非常的舒服,STL的算法和容器中基本都是如此。
差一問(wèn)題(柵欄錯(cuò)誤)。建造一條直柵欄(即不圍圈),長(zhǎng)30米、每條柵欄柱間相隔3米,需要多少條柵欄柱? 求數(shù)組 A[i]到 A[j] 的平均值,A[i] 到 A[j] 的和應(yīng)該除以多少,是 j-i+1,還是 j-i?二分法中的 while 條件,到底是用 <= 還是 < ?這些都是差一問(wèn)題,這類問(wèn)題如何解決。利用最小的的輸入值測(cè)試代碼的執(zhí)行過(guò)程,長(zhǎng)期反復(fù),達(dá)到條件反射。這個(gè)過(guò)程一定是在大量的題目練習(xí)中掌握的,如果你到目前還在糾結(jié)這個(gè)問(wèn)題,請(qǐng)先扣心自問(wèn),是否刷過(guò)至少200道算法題。如果沒(méi)有,請(qǐng)不要糾結(jié),干就對(duì)了。如果有,來(lái)找我。
內(nèi)存溢出問(wèn)題。分為兩種,一種是因?yàn)檫\(yùn)算超出變量取值范圍發(fā)生的內(nèi)存溢出,比如二分法中的mid,就要使用 left+(right-left)>>1。另一種就是因?yàn)榇a不嚴(yán)謹(jǐn),比如遞歸沒(méi)有退出條件,while死循環(huán),等等導(dǎo)致內(nèi)存溢出。
初學(xué)者定義問(wèn)題。比如統(tǒng)計(jì)26個(gè)字母出現(xiàn)的次數(shù),初學(xué)者會(huì)用hashmap,其實(shí)這種已知值范圍的,定義成數(shù)組就可以了。其他類似數(shù)字0-9,每個(gè)月的天數(shù),都是一樣的
寫(xiě)一半忘記題意。這個(gè)根本原因還是因?yàn)樗季S脈絡(luò)不清晰導(dǎo)致的,有時(shí)候初學(xué)者還會(huì)遇到一個(gè)問(wèn)題。定義一個(gè)函數(shù),比如叫做 juge() 返回 bool 值,本來(lái)應(yīng)該是判斷某條件成立時(shí)返回true,但是用的時(shí)候卻以為,在條件不成立的時(shí)候返回true,最終導(dǎo)致結(jié)果錯(cuò)誤。
變量名錯(cuò)誤。不管是和方法參數(shù)中的變量名稱沖突,還是因?yàn)楸旧肀硪獠幻鳎罱K出現(xiàn)賦值錯(cuò)誤,或者編譯不通過(guò)。
運(yùn)算優(yōu)先級(jí)錯(cuò)誤。比如位運(yùn)算,各個(gè)語(yǔ)言中的優(yōu)先級(jí)定義是略有不同的。有時(shí)候需要加括號(hào),有時(shí)候不需要加。
特殊值的處理。一些找規(guī)律的題目,往往在等于0,等于1的時(shí)候,規(guī)律和其他的數(shù)不同,所以這種題目,需要對(duì)這種特殊值進(jìn)行特殊處理。
以上就是我挑選過(guò)的一些具有代表性的錯(cuò)誤示例(后續(xù)還會(huì)補(bǔ)充),同時(shí),算法指導(dǎo)篇我打算出一個(gè)系列,包括算法中常用技巧,常見(jiàn)錯(cuò)誤,題目常見(jiàn)誤導(dǎo) 等等,都是我的珍藏。今天的文章嘔心泣血,希望大家可以幫我轉(zhuǎn)發(fā),我想如果你身邊有學(xué)習(xí)計(jì)算機(jī)的朋友看到,他一定會(huì)感謝你。支持我的朋友們,還沒(méi)有關(guān)注的,快點(diǎn)來(lái)個(gè)關(guān)注。最后,山高水長(zhǎng),江湖路遠(yuǎn),珍重萬(wàn)千,下期再見(jiàn)!
-
算法
+關(guān)注
關(guān)注
23文章
4702瀏覽量
94948 -
數(shù)組
+關(guān)注
關(guān)注
1文章
419瀏覽量
26415
原文標(biāo)題:嘔心泣血算法指導(dǎo)篇(真正的干貨,怒懟那些說(shuō)算法沒(méi)用的人)
文章出處:【微信號(hào):TheAlgorithm,微信公眾號(hào):算法與數(shù)據(jù)結(jié)構(gòu)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
電機(jī)選型計(jì)算公式與知識(shí)點(diǎn)匯總
笙泉科技BLDC重點(diǎn)方案匯總 (@2025慕尼黑上海電子展)
最新!智慧燈桿七大應(yīng)用場(chǎng)景案例獨(dú)家匯總
PID控制算法的C語(yǔ)言實(shí)現(xiàn):PID算法原理
【「從算法到電路—數(shù)字芯片算法的電路實(shí)現(xiàn)」閱讀體驗(yàn)】+介紹基礎(chǔ)硬件算法模塊
U盤(pán)存儲(chǔ)并聯(lián),算法交互輸出
TMS320C6455/C6454功耗匯總

TMS320C6747/45/43功耗匯總

評(píng)論