聽說數據結構與算法中的復雜度很難理解?看看這個吧!


CSDN首發,歡迎加微信H653836923一起探討!

讓人迷惑的復雜度

小白: 慶哥啊,這個復雜度到底是個啥啊,我在上大學的時候學這塊的時候就很懵😂,不知道是個啥,理解起來很費勁,所以當時也沒有好好學習,自己的數據結構與算法這塊一直比較薄弱,准備好好再學學數據結構與算法嘞,這一個復雜度都難住我了🤣

慶哥: 的確啊,雖然就三個字,但是理解起來也確實有點費勁,我當時學習的時候也是有點理解不了,感覺看了很多解釋,總覺得迷迷糊糊的,還是不知道到底是個啥?😂

小白: 嗯嗯,是的,我也上網搜了很多的關於復雜度的分析的文章,有些文章覺得越看越迷糊,看着看着就看不下去了,然后我就去看看評論,想着會有人跟我一樣覺得看不懂,結果評論都是“寫的很好”😂,頓時覺得自己是不是太笨了🤣。

慶哥: 哈哈😁,太真實了,那今天咱倆就來好好學習下這個復雜度吧,絕對讓你對復雜度有個全新的認識😎

一起攻克復雜度吧

小白: 嗯嗯,那我得好好學習了,復雜度一直困擾着我,還真的是復雜啊😅,對了,有啥不明白的可是隨時提問你的哦,還請慶哥賜教😁

慶哥: 小事小事,那咱開始吧,首先我們要知道我們學習啥,那必須是復雜度啊,咋一看,三個字“復雜度”,應該是個名詞吧,所以嘞,首先我們得知道,它一定有他本身的概念,那好,我們看看復雜度是怎么定義的,怎么看?必須是百科的解釋啊,來,一起看看:

在計算機科學中,算法的時間復雜度(Time complexity)是一個函數,它定性描述該算法的運行時間。這是一個代表算法輸入值的字符串的長度的函數。時間復雜度常用大O符號表述,不包括這個函數的低階項和首項系數。使用這種方式時,時間復雜度可被稱為是漸近的,亦即考察輸入值大小趨近無窮時的情況。例如,如果一個算法對於任何大小為 n (必須比 n0 大)的輸入,它至多需要 5n3 + 3n 的時間運行完畢,那么它的漸近時間復雜度是 O(n3)。

看看,明白不?

小白: 說實話,看到這個真的有點懵啊😄,時間復雜度?跟復雜度一樣吧,什么函數,大O,低階項,漸進時間,這都是啥跟啥啊😂,是不是涉及數學啊,我數學可一直都不好啊🤣

慶哥: 別說你,我剛開始看這個的時候也是一臉茫然啊🙄,也許真的是因為咱倆太笨了😄,怪尷尬😂,不過我覺得這個解釋雖然很正確,但是有點官方,對已經只是到啥是時間復雜度的來說很不錯,但是對於新手和那些對復雜度概念有疑惑的人來說,真的不算友好,本身就理解的雲里霧里,看這個更迷茫了😃

小白: 對對對,也許它說的很對,但是這樣解釋就是覺得好迷茫,不知道到底是個啥😫,慶哥快給我解釋解釋吧😆

復雜度小探

慶哥: 嗯嗯,那接下來,我就把我的理解用大白話給你解釋一下吧😉,首先嘞,針對你上面的一些疑問咱先來說說,你要知道,咱們這里要探討的復雜度是數據結構與算法里面的概念,為啥要這樣說,中華文字,博大精深,一樣的字放在不同的場景里,那含義就不同了,所以咱必須先明確,我們這里講的 復雜度是數據結構與算法中的概念

好了,知道這個之后,我們還要知道,我們這里說復雜度,更為准確的來說是時間復雜度(還有空間復雜度嘞),所以后面咱們不說復雜度了(避免有歧義),就說時間復雜度(因為時間復雜度相對來說比較重要),先跟你說一聲,這個時間復雜度是針對於算法的,所以再全面點,我們實際應用中會說成某個算法的時間復雜度

小白: 是啊,復雜度,哎不對,是時間復雜度😁,就是跟算法有關的,數據結構與算法是塊超級難啃的骨頭啊😂

慶哥: 的確,數據結構與算法真的很重要,按時不可否認,真的不容易,很多程序員這塊的技能也都不是很強,數據結構與算法這塊牛了,那都是大神級別的啊😁,很多小渣渣早就放棄了,根本學不會啊😂

小白: 這太扎心了😁,我好難啊🤣

慶哥: 雖說數據結構與算法有一定難度,但是還沒有到了那種難得學不會的地步,需要你多花時間,多思考多練習,很多人第一步都不敢邁出去,當然難了

小白: 嗯嗯,確實,很多人聽到數據結構與算法,心里不自然的就覺得好難,然后就沒有學下去的欲望了,首先就被自己嚇到了,我要邁出第一步,跟着慶哥😂

慶哥: 哈哈,可以可以,我的這塊也是比較薄弱,咱們共同共同學習,那咱接着上面的說,經過上面說的,我們現在知道了,我們要搞定的就是時間復雜度,至於什么大O,低階項的,咱們一步步來。

理解時間復雜度的前提

那時間復雜度到底是個啥呢?我個人覺得啊,要想理解什么是時間復雜度,我們先要知道什么是算法,為啥,因為時間復雜度就是針對算法才有的一個概念,注意啦,我開始慢慢分析什么是時間復雜度了哦😁

小白: 嗯嗯,我在注意聽着嘞😀

慶哥: 好的,千萬別走神😎,為啥說理解時間復雜度的前提先要搞明白算法是啥呢?舉個例子,比如有人要給你介紹對象,你這人嘞比較在意身高,所以你就問“那貨多高啊😂”,別人說差不多一米6左右吧,這個時候你就知道她多高了,這個身高就是衡量一個人的,這個好理解吧,而這個時間復雜度就是用來衡量一個算法的。

你想想,為啥你要問身高,因為你比較在意身高,比如說身高達到一米六就是你的標准,那達到這個標准就是好啊,達不到就是不好,如果人家才一米五,你肯定說,不好,身高不行😂,所以身高也作為你衡量一個人好不好的指標,那對於算法嘞,一個算法也有好不好的說法,那用啥來衡量一個算法的好壞呢?

小白: 我知道,時間復雜度😁

慶哥: 嗯嗯,說的非常對,我們就可以用時間復雜度來衡量一個算法的好壞,當然,你衡量一個人的好壞,可以有很多指標,身高只是其中之一而已,同理,對於算法的好壞,也有好多指標,時間復雜度也只不過是其中之一罷了,像空間復雜度也可以來衡量一個算法的好壞,不過嘞我們這里主要探討的是時間復雜度對算法的影響。

小白: 慶哥啊,不是要說什么是算法嗎,這里好像把什么是時間復雜度給講講了吧😀

算法是啥

慶哥: 哈哈,別着急,到了這里,你應該知道了,時間復雜度是用來衡量算法的一個指標,那啥是算法嘞?

小白: 說實在的,我對算法也有點迷迷糊糊的,覺得算法就是一種方法吧😁

慶哥: 其實吧,概念性的東西沒有個定性答案,所以,按照你的理解沒啥問題,所謂算法,當然咱這里也是針對數據結構與算法這塊說的,它就是你解決問題采用的一種方式方法而已,我們這學編程的,終究是要落實到代碼中的,在代碼里來說,不同的算法,也就是你實現的代碼不一樣,但是達到的目的是相同的,用我們生活中的例子來說,那就是條條大路通羅馬啊

在這里插入圖片描述
就像這個圖一樣,我們從起點到終點,可以選擇的路線有很多,但是我們直觀來看,圖中紅色的路線是最近的,其實這里拿到編碼中來說,每一條路線都可以說成是一個算法,那這里的好壞就是哪個近哪個算法好啊,這應該沒啥難理解的吧。

這里的路線的遠近就可以看成是時間復雜度,路線近,我們可以說時間復雜度小,算法就好,路線遠,時間復雜度就高,算法相對來說就不好。

那為啥又說衡量一個算法的好壞,時間復雜度只是其中一個指標呢?難道這里的紅線路線就是最好的嗎?比如我們考慮道路問題,看下面的圖,紅線雖然近,但是路不好走,黑線雖然遠點,但是路好走,綜合下來,黑線是最佳路線。

在這里插入圖片描述
所以說,時間復雜度並不能作為一個衡量算法的唯一標准,因為還有其他影響算法的因素,只不過,時間復雜度的影響比較大,我們常規討論算法的好壞的時候經常使用時間復雜度來蘋果。

那么,說到這里,你知道了啥是算法和時間復雜度了吧?

小白: 嗯嗯,對他們的概念比較清晰了,其實算法這個玩意就是一個怎么做比較好的問題吧😁

慶哥: 是啊,所以算法也涉及到思想層面,上升到這個高度就有難度,值得研究了,因為怎么找一個最佳選擇,也就是最好的辦法是需要費腦筋的,有的人能想到,有的人就是想不到😂

小白: 哈哈,是這樣啊🤣,這里也知道了時間復雜度是用來衡量算法的一個指標了,也就是清楚概念了,不過不是有什么大O啥的嗎?就是它咋用啊😂

怎么表示時間復雜度嘞

慶哥: 不着急,咱繼續,經過上面的講解,我們大致清楚時間復雜度的概念了,以及它有啥用,那么應該怎樣來表示時間復雜度了,我們上面舉過身高的例子,那表示身高可以用多少多少cm啊,也就是160cm,那么時間復雜度該怎么表示嘞?

小白: 這個我知道,叫什么大O表示法,經常見表示哪個算法的時間復雜度是O(1)啊,或者O(n)之類的。

慶哥: 對的,就是用這種方式來表示時間復雜度,那你知道啥是大O表示法嗎?

小白: 這個就有點迷惑了,只知道這樣表示,但是要說啥是大O表示法,就有點懵😂

慶哥: 那咱來看關於大O表示法的一個概念解釋:

若存在函數f(n),使得當n趨近於無窮大時,T(n)/f(n)的極限值為不等於零的常數,則稱f(n)是T(n)
的同數量級函數。記作T(n)=O(f(n)),稱為O(f(n)),O為算法的漸進時間復雜度,簡稱為時間復雜度。
因為漸進時間復雜度用大寫O來表示,所以也被稱為大O表示法。

怎么樣,看得懂不😁

小白: 說實話,懵🤣

慶哥: 別說你懵,我也懵😂,沒辦法,咱們數學差啊,當初學數學的時候,有些概念就是理解不了,感覺說的好繞,那啥是大O標識法嘞,我覺得吧,沒必要去糾結它的概念性問題,你只需知道大O標識法的形式就是O(n),啥事大O啊,就是大寫字母O啊,然后一個括號,括號里面一個數,這個數可以事已知的,比如說1,其實也就只有1,也可以事未知的,比如n,也可以是2n,3n或者是n的平方(n^2)等等,反正未知的就是跟n相關的函數。

大O表示法為啥是n

小白: 有個疑問啊,這里為啥是n呢?😅

慶哥: 當然,這個n只是表示個未知數,我們之前表示未知數通常是x和y之類的,這里使用n只是個約定俗稱的事,也就是習慣成自然😂,大家之前都這樣表示,於是干脆就這樣吧,使用n來表示,其實吧,這個也跟我們寫代碼有關,比如我們寫一些循環代碼,經常出現n,像這樣:

public static int test2(int n) {
		if(n<=1) return n;
		
		int first = 0;
		int second = 1;
		for (int i = 1; i < n; i++) {
			second += first;
			first = second - first;
		}
		return second;
	}

看到沒,這段代碼中使用的n,對了,你知道怎么計算時間復雜度的嘛?

小白: 就是如何計算一個算法的時間復雜度嘛?我就不會這個😂,看了不少文章,好多都是在介紹啥是時間復雜度,可是就是不告訴我時間復雜度該怎么計算🤣

如何計算時間復雜度

慶哥: 😂,那咱來說說,因為跟你說了這個怎么計算時間復雜度的之后,你就會更明白為啥是n了,首先,你有沒有覺得時間復雜度跟時間有關😁

小白: 必須啊,人家就叫時間復雜度,我之前還以為時間復雜度就是多少多少秒之類的😂,結果是O(n)什么的🤣,這是咋算出來的啊

慶哥: 你這樣理解也正常,那是因為我們對時間這個概念固化了,想着時間那就是時分秒來計算的,這些都是時間單位,是我們熟知的時間單位,但是還有可能時間不是一時分秒來計算,時分秒是確切的,也有可能是抽象的,沒什么意思呢?我們弄個代碼來看下:

public static int test(int n) {
		
		return n++;
		
	}

咋樣,這段代碼簡單吧,就是給定一個值,然后加一返回,我們了解過什么是算法了,這段代碼其實就是一個算法,只不過超級簡單,它是用來計算一個值加一之后的結果的,那么我們來看怎么算它的時間復雜度,該怎么計算呢?

我們先約定好,在方法體內,每執行一次操作,我們記作1,也就是花費了一個時間單位,你看這里並沒有說是1秒還是1分,就說是一個時間單位,我們下面來看這段代碼。

在這段代碼中,就有一行代碼,也就是:

return n++;

它就算做一次操作,所以這里花費的是1個時間單位,也就是1,那它的時間復雜度就是O(1)

小白: 為啥啊?😂

慶哥: 不着急,我們繼續看下面的代碼:

public static int test(int n) {
		
		n=+5;
		return n++;
		
	}

與上面代碼不同的地方是這里多了一行代碼,把給定的值加上5,我們來看它的時間復雜度,首先是n=+5這行代碼被執行一次,花費了1個時間單位,下面那行代碼也是執行一次,也是花費了1個時間單位,那么總的就是花費兩個時間單位,所以它的時間復雜度就是O(1),怎么樣,有沒有發現點什么啊😂

小白: 無論是1個時間單位,還是2個時間單位,他們的時間復雜度始終是O(1),這是不是意味着,只要是這些常數個時間單位,時間復雜度始終都是O(1) 啊😁

慶哥: 對,很對,也就是說,只要代碼的總執行次數是個常數,那么時間復雜度就是O(1),我們緊接着來看O(n)的是咋回事,看下面這段代碼:

public static int test(int n) {
		int sum = 0;
		
		for (int i=0;i<n;i++) {
			sum=+i;
		}
		
		return sum;
		
	}

這里有個for循環,我們看看它的時間復雜度怎么計算,首先第一行代碼int sum = 0執行一次,花費1個時間單位,重點在下面的for循環這塊,因為這里的n是個不確定的值,經過分析,發現for (int i=0;i<n;i++)這行代碼會執行n+1次,也就是會花費n+1個時間單位,循環體內的sum=+1這行代碼則會執行n次,也就是會花費n個時間單位,然后最后一行代碼會執行一次,就是花費1個時間單位,總的來說就是:
1+n+1+n+1 = 3+2n,也就是總共花費3+2n個時間單位,那么它的時間復雜度就是O(n)。

你看看,覺得這個是規律是啥😎

小白: 這里得出的總時間單位就像函數一樣,比如f(n)=3+2n,不過我們之前喜歡用x,也就是f(x)=3+2x,只不過這里是n,看這里應該是把常數項去除,然后系數也去除,只保留個n,那么如果是f(n)=3+2n^2的話,時間復雜度是不是就是…

是不是就是O(n^2)啊😀

慶哥: 很聰明啊,確實是這樣,這里有個比較官方的解釋,也就是按照下面的規則去計算時間復雜度

  1. 如果運行時間是常數量級,則用常數1表示
  2. 只保留時間函數中的最高階項
  3. 如果最高階項存在,則省去最高階項前面的系數

這個明白吧,舉個例子,比如f(n)=3+2n^2+5n,那么3就是常數項,5n就是一階項。
2n^2就是二階項,在這里也是最高階項,那么按照規則時間復雜度是哈?😎

小白: 這還是O(n^2)吧😁

慶哥: 咋樣,現在知道怎么計算時間復雜度了吧,你有沒有總結什么規律呢?

總結

小白: 經過上面那幾個例子,我覺得這計算時間復雜度,主要就是看代碼中的每一行代碼一共執行了多少次,不確定的就是n,然后按照那三條規則來確定最終的時間復雜度,所以重點就是計算每行代碼執行了多少次,是不是這樣😅

慶哥: 是的,就可以這樣理解,就像你說的重點就是看每行代碼一共執行了多少次,不過有些復雜的代碼是不容易算出一共執行多少次的,容易算着算着就迷糊了🤣

小白: 哈哈,我也正在思考這個問題呢?咱這里舉的例子比較簡單,容易計算每行代碼執行多少次,但是代碼一旦比較繞,比較復雜那就不容易了吧😂

慶哥: 確實啊,不過隨着我們后續的學習,我們會學習一些經常使用的算法,那么這些算法的時間復雜度會直接給出,我們記着就行了,比如我們看看這個圖:

在這里插入圖片描述
這個就是不同時間復雜度的一個函數圖,可以簡單理解,越陡的花費時間單位越大,那算法性能也就越差,這個我們只要學過數學,一般都能理解吧😁

小白: 嗯嗯,是的,這個還是能夠明白的,你說有些常用算法的時間復雜度是給出的,有哪些呢?

慶哥: 這個啊,我覺得還是隨着我們后續的學習去逐步的了解,比如學到哪個算法,需要分析時間復雜度的時候我們再去說,那樣我們記憶的才牢固,不然,這里即使和你說了,也會很快忘掉😂

小白: 嗯嗯,有道理😁

慶哥: 對了,知道大O表示為啥是n了吧😅

小白: 嗯嗯 知道了😀

感謝閱讀

大家好,我是ithuangqing,一路走來積累了不少的學習經驗和方法,而且收集了大量的精品學習資源,現在維護了一個公眾號【編碼之外】,寓意就是在編碼之外也要不停的學習,主要分享java技術相關的原創文章,現在主要在寫數據結構與算法,計算機基礎,線程和並發以及虛擬機這塊的原創,另外針對小白還在連載一套《小白的java自學課》,力求通俗易懂,由淺入深。同時我也是個工具控,經常分享一些高效率的黑科技工具及網站

對了,公眾號還分享了很多我的學習心得,可以一起探討探討!

關注公眾號,后台回復“慶哥”,2019最新java自學資源立馬送上!更多原創精彩盡在【編碼之外】

在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM