算法之路到底該怎么走

1.引言——什么是算法

  • 算法(Algorithm)是指解題方案的準(zhǔn)確而完整的描述,是一系列解決問題的清晰指令,算法代表著用系統(tǒng)的方法描述解決問題的策略機(jī)制。

一大段話可能看起來很空洞或者可以說是乏味和枯燥。簡(jiǎn)單來說算法其實(shí)就是一個(gè)提出問題和解決問題的過程。如下圖所示,這是洛谷的一道題目,可以說是最最基礎(chǔ)的一道題,由此我們可以看到一道完整的算法題應(yīng)當(dāng)會(huì)以什么樣的方式呈現(xiàn)。A+B是小學(xué)一年級(jí)學(xué)習(xí)的加法內(nèi)容,但如何將我們熟悉的東西用程序設(shè)計(jì)語言的方式展現(xiàn),


洛谷的簡(jiǎn)單算法例題

其實(shí)就是學(xué)習(xí)算法一直需要面對(duì)的一個(gè)問題,對(duì)于較大的數(shù)據(jù),又該如何處理,也是我們一直需要去解決的問題之一。
下面給大家介紹三個(gè)常用的在線判題系統(tǒng)OJ(Online Judge):

下方為洛谷測(cè)評(píng)狀態(tài)示意圖:


洛谷測(cè)評(píng)狀態(tài)示意圖1.png
洛谷測(cè)評(píng)狀態(tài)示意圖2.png

后續(xù)我們可能會(huì)參加ACM國際大學(xué)生程序設(shè)計(jì)競(jìng)賽(ICPC)、中國大學(xué)生程序設(shè)計(jì)競(jìng)賽(CCPC)、藍(lán)橋杯程序設(shè)計(jì)大賽、中國高校計(jì)算機(jī)大賽——團(tuán)體程序設(shè)計(jì)天梯賽等等。

2.基礎(chǔ)算法

一棟大樓并非平地而起,地基的搭建往往是建造一棟樓最艱難的時(shí)刻?;A(chǔ)算法可以稱的上是整個(gè)算法學(xué)習(xí)的地基。在學(xué)習(xí)基礎(chǔ)算法時(shí),除了對(duì)知識(shí)的理解,還有很重要的一點(diǎn)是探索個(gè)人學(xué)習(xí)算法的方式。我認(rèn)為自己現(xiàn)在還沒有找到一個(gè)適合我的學(xué)習(xí)方法,目前也仍在探索中。
下面來大致分享一些基本算法。

2.1 暴力

我接觸到的第一個(gè)算法就是暴力。暴力如同他的字面意思,就是暴力求解,可以理解成小學(xué)奧數(shù)學(xué)過的枚舉法。指的是從所有可能的解的集合中一一枚舉各元素,用題目中給定的檢驗(yàn)條件判定哪些是無用的,哪些是有用的。能使命題成立的就是答案。
該算法可以求的一些情況較少的問題的解,若問題規(guī)模太大,該算法便不適用。正如枚舉法并不是時(shí)時(shí)適用一樣,暴力也被數(shù)據(jù)和時(shí)間局限,往往數(shù)據(jù)量過大,暴力便不適用了。因此他的優(yōu)缺點(diǎn)都很明顯,簡(jiǎn)單易想,在部分題目中或某題目中的局部使用有較好的效果。但運(yùn)算量過大。對(duì)于數(shù)據(jù)量較大的題目,效率較低,當(dāng)問題規(guī)模變大時(shí),循環(huán)的階數(shù)越大,執(zhí)行速度越慢,容易超時(shí)。

2.2 位運(yùn)算

單看每一個(gè)都很簡(jiǎn)單無非就是與(&),或(|),非(~),異或(xor),還有一些移位計(jì)算,并沒有什么難點(diǎn)。但當(dāng)深入學(xué)習(xí)之后,了解了二進(jìn)制狀態(tài)壓縮成對(duì)變換等等,就會(huì)逐漸體會(huì)到位運(yùn)算多么重要。在后續(xù)很多算法中,我們都需要用位運(yùn)算去實(shí)現(xiàn)一個(gè)算法,例如并查集需要用到lowbit等,位運(yùn)算更像是水,一時(shí)半會(huì)兒不喝沒有什么關(guān)系,但一旦干渴的時(shí)間長了,那是性命之憂,起初可能意識(shí)不到位運(yùn)算的作用,但當(dāng)你需要用到的時(shí)候,想不起來如何使用,也就只能干瞪眼了。


位運(yùn)算符號(hào)表

2.3遞推與遞歸

一個(gè)實(shí)際問題的各種可能情況構(gòu)成的集合通常被稱為“狀態(tài)空間”,而程序的運(yùn)行則是對(duì)于狀態(tài)空間的遍歷,算法和數(shù)據(jù)結(jié)構(gòu)則通過劃分、歸納、提取、抽象來幫助提高程序遍歷狀態(tài)空間的效率。遞推和遞歸就是程序遍歷狀態(tài)空間的兩種基本方式。
對(duì)于一個(gè)待求解的問題,當(dāng)它局限在某處邊界、某個(gè)小范圍或者某種特殊情形下時(shí),其答案往往時(shí)已知的。如果能夠?qū)⒃摻獯鸬膽?yīng)用場(chǎng)景擴(kuò)大到原問題的狀態(tài)空間,并且擴(kuò)展過程的每個(gè)步驟都具有相似性,就可以考慮遞推或遞歸求解。以已知的“問題邊界”為起點(diǎn)向“原問題”正向推導(dǎo)的擴(kuò)展方式就是遞推。


遞歸動(dòng)畫示意圖

2.4前綴和和差分

前綴和是一種重要的預(yù)處理,能大大降低查詢的時(shí)間復(fù)雜度。
根據(jù)下面兩張圖,相信大家一定能對(duì)一維和二維前綴和有一個(gè)初步理解。


一維前綴和示意圖
二維前綴和示意圖

前綴和和差分其實(shí)是一對(duì)互逆運(yùn)算,差分序列B的前綴和序列就是原序列A。前綴和序列S的差分序列也是原序列A。


差分示意圖

2.5 二分

二分法是一種隨處可見卻非常精妙的算法,經(jīng)常能為我們打開問題的突破口。二分的基礎(chǔ)用法是在單調(diào)序列或單調(diào)函數(shù)中進(jìn)行查找。因此當(dāng)問題答案具有單調(diào)性時(shí),就可以通過二分把求解轉(zhuǎn)換為判定(根據(jù)復(fù)雜度理論,判定難度小于求解),這使得二分法的運(yùn)用范圍非常廣泛。二分的實(shí)現(xiàn)方式多種多樣,但是其細(xì)節(jié)之處確實(shí)需要仔細(xì)考慮。

2.6 排序

我們經(jīng)常會(huì)用到排序算法,這里把它分成三類:

  • 選擇排序、插入排序、冒泡排序
  • 堆排序、歸并排序、快速排序
  • 計(jì)數(shù)排序、基數(shù)排序、桶排序

前兩類是基于比較的排序算法,第三類換了一種思路,不直接比較大小,而是對(duì)被排序的數(shù)值采取按位劃分、分類映射等處理方式。

2.7 貪心

貪心是一種在每次決策時(shí)采取當(dāng)前已一下最優(yōu)策略的算法,因此使用貪心法要求所有問題的整體最優(yōu)解可以由局部最優(yōu)性導(dǎo)出。

3.數(shù)據(jù)結(jié)構(gòu)

3.1 棧

棧是一種“后進(jìn)先出”的線性數(shù)據(jù)結(jié)構(gòu)。棧只有一端能夠進(jìn)出元素,一般被稱為一端為棧頂,另一端為棧底。添加或刪除元素時(shí),我們只能將其插入到棧頂(進(jìn)棧),或者把棧頂元素從棧中取出他(出棧)。

3.2 隊(duì)列

隊(duì)列是一種“先進(jìn)先出”的線性數(shù)據(jù)結(jié)構(gòu),一般來講,元素從右端進(jìn)入隊(duì)列(入隊(duì)),從左端離開隊(duì)列(出隊(duì))。于是我們稱隊(duì)列的左端為隊(duì)頭,右端為隊(duì)尾??梢栽贑++中用一個(gè)數(shù)組和兩個(gè)變量(記錄隊(duì)頭和隊(duì)尾位置)來實(shí)現(xiàn)隊(duì)列結(jié)構(gòu)的。


隊(duì)列示意圖

3.3 鏈表與鄰接表

數(shù)組是一種隨機(jī)訪問,但不支持在任意位置插入或刪除元素的數(shù)據(jù)結(jié)構(gòu)。與之對(duì)應(yīng),鏈表支持在任意位置插入或刪除,但只能按順序依次訪問其中的元素。我們可以用一個(gè)struct表示鏈表的節(jié)點(diǎn),其中可以存儲(chǔ)任意數(shù)據(jù)。另外用prev和next兩個(gè)指針指向前后相鄰的兩個(gè)節(jié)點(diǎn),構(gòu)成一個(gè)常見的雙向鏈表結(jié)構(gòu)。為避免左右兩端或者空鏈表中訪問過界,我們通常建立額外兩個(gè)節(jié)點(diǎn)head與tail代表鏈表頭尾,把實(shí)際數(shù)據(jù)節(jié)點(diǎn)存儲(chǔ)在head和tail之間,來減少鏈表邊界處的判斷,降低編程復(fù)雜度。

3.4 Hash表

又稱為散列表,一般由Hash函數(shù)(散列函數(shù))與鏈表結(jié)構(gòu)共同實(shí)現(xiàn)。Hash表一般包括兩個(gè)操作:

  • 計(jì)算Hash函數(shù)的值
  • 定位到對(duì)應(yīng)鏈表中依次遍歷比較

3.5 字符串

STL-string本質(zhì)是string是C++風(fēng)格的字符串,而string本質(zhì)上是一個(gè)類。有這樣的特點(diǎn):string 類內(nèi)部封裝了很多成員方法。
例如:查找find、拷貝copy、刪除delete、替換replace、插入insert
string管理char*所分配的內(nèi)存,不用擔(dān)心復(fù)制越界和取值越界等,由類內(nèi)部進(jìn)行負(fù)責(zé)。
還有KMP模式匹配和最小表示法等,在此不詳細(xì)敘述了。

3.6 并查集

并查集(Disjoint-Set)是一種可以動(dòng)態(tài)維護(hù)若干個(gè)不重疊的集合,并支持合并和查詢的數(shù)據(jù)結(jié)構(gòu)。
因此,可以想到并查集包括兩個(gè)基本操作:

  • Get:查詢一個(gè)元素屬于哪個(gè)集合
  • Merge:把兩個(gè)集合合并成一個(gè)集合

具有非常明顯的傳遞關(guān)系的題目,或者說并查集擅長動(dòng)態(tài)維護(hù)許多具有傳遞性的關(guān)系,能在無向圖中維護(hù)節(jié)點(diǎn)之間的連通性會(huì)用到并查集。(下圖為一道并查集經(jīng)典例題配圖,判斷是否有環(huán)狀結(jié)構(gòu))


判斷是否有環(huán)狀結(jié)構(gòu)

3.7 樹狀數(shù)組

什么是樹狀數(shù)組?顧名思義,就是用數(shù)組來模擬樹形結(jié)構(gòu)。
什么問題需要用到樹狀數(shù)組?大部分基于區(qū)間上的更新以及求和問題。


樹狀數(shù)組

4.搜索

4.1 深度優(yōu)先搜索(DFS)

深搜是一種與遞歸和棧緊密結(jié)合的算法。顧名思義就是按照深度優(yōu)先搜索的順序?qū)Α皢栴}狀態(tài)空間”進(jìn)行搜索的算法。使用遞歸實(shí)現(xiàn)的指數(shù)型、排列型和組合型枚舉。

4.2 廣度優(yōu)先搜索(BFS)

廣搜又稱為寬度優(yōu)先搜索,是一種與隊(duì)列緊密結(jié)合的算法。


經(jīng)典皇后問題

5.動(dòng)態(tài)規(guī)劃

動(dòng)態(tài)規(guī)劃對(duì)狀態(tài)空間的遍歷構(gòu)成一張有向無環(huán)圖,遍歷順序就是該有向無環(huán)圖的一個(gè)拓?fù)湫颉S邢驘o環(huán)圖中的節(jié)點(diǎn)對(duì)應(yīng)問題中的“狀態(tài)”,圖中的邊則對(duì)應(yīng)狀態(tài)轉(zhuǎn)移的“轉(zhuǎn)移”,轉(zhuǎn)移的選取就是動(dòng)態(tài)規(guī)劃中的“決策”。在很多情況下,動(dòng)態(tài)規(guī)劃用于解決最優(yōu)化問題。
“狀態(tài)”“階段”和“決策”是構(gòu)成動(dòng)態(tài)規(guī)劃算法的三要素,而“子問題重疊性”“無后效性”和“最優(yōu)子結(jié)構(gòu)”是問題能用動(dòng)態(tài)規(guī)劃求解的三個(gè)基本條件。

6.圖論

圖論涉及的內(nèi)容廣泛,形象,實(shí)際,知識(shí)體系綜合性較強(qiáng),還融合了之前學(xué)習(xí)的各類算法與數(shù)據(jù)結(jié)構(gòu)。個(gè)人認(rèn)為并非三言兩語就可以解釋清楚一些基本定義與概念的,故而不再詳細(xì)展開談。圖論的應(yīng)用價(jià)值非常高,一直是程序設(shè)計(jì)競(jìng)賽的重要關(guān)注點(diǎn)。

7.學(xué)習(xí)總結(jié)

上述也僅僅列出了一些常用算法,只能算是冰山一角,個(gè)人認(rèn)為算法并無難易,剛開始學(xué)并查集和樹狀數(shù)組的時(shí)候,我學(xué)了五天并查集,同一節(jié)課的視頻仔仔細(xì)細(xì)看了三遍,但還是覺得有的點(diǎn)很難理解(因?yàn)楫?dāng)時(shí)正是算法集訓(xùn)時(shí)期,我負(fù)責(zé)并查集與樹狀數(shù)組方面的授課),有一種自己依葫蘆畫瓢能寫出來,但給他人講解的話還是很含糊不清,但是當(dāng)我思考了足夠多次,很多問題就迎刃而解了。現(xiàn)在再回看備課是做過的例題以及看過的視頻,就會(huì)覺得很好理解。因此,我覺得算法和很多東西一樣,入門難,只要堅(jiān)持不懈,總有完全理解的一天。雖然本蒟蒻至今都不算完全入門,和校內(nèi)很多大佬還有很大差距,但相信堅(jiān)持一定會(huì)有收獲。
算法這項(xiàng)學(xué)習(xí)一定需要大量刷題和思考,必要時(shí)候需要借助他人力量,有很多自己一直不理解的點(diǎn),可以嘗試去問問大佬們,他們總是會(huì)給出一些讓我們覺得非常精妙的方法。
希望大家能夠愿意嘗試接觸算法,了解算法,學(xué)習(xí)算法,即使能夠堅(jiān)持學(xué)習(xí)算法的人少之又少,但堅(jiān)持的過程本身就是一種磨練。希望大家都能夠收獲絞勁腦汁AC時(shí)的自豪,看懂大佬題解時(shí)對(duì)其絕妙算法的贊嘆!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容