對於任何一個程序來講,均可以從三個方面進行分析,分別是 輸入、處理、輸出,也即 IPO(Input、Process、Output),這種分析方法對硬件和軟件程序都是適用的。算法
數據的來源(Input):能夠是硬件傳感器收集的,也能夠是從網上爬取的...。數據的輸出(Output):能夠顯示在網頁上,安卓APP上,電子屏幕上...。而最重要的是程序處理,能夠對數據進行簡單的處理,也能夠對數據進行挖掘...。編程
就拿最簡單的 Hello World 程序來講,也是由這三方面組成的,輸出函數(處理)幫你處理輸入的 「Hello World」 字符串(輸入),而後再幫你將這些字符輸出顯示到控制檯上(輸出)。大到如今熱門的技術,物聯網、大數據,人工智能等,作的也無非都是上面三個方面內的事情,關於這些,讀者能夠思考一下。bash
評價一個程序的複雜程度,關鍵也是看程序中處理數據的這部分,對數據處理就要用到算法了。什麼?你說你沒有用過算法,其實從你編程開始的第一句 「Hello World」 就註定編程和算法分不開了,一個輸出函數背後也一樣有算法呀,只是你在使用算法的時候並無意識到你在用算法罷了(嘿嘿,就是這麼神奇~)。微信
算法是一個程序的靈魂,就比如沒有蛋的泡麪是沒有靈魂的同樣。一個好的算法能夠有不少的應用,好比在美劇《硅谷》中,主角發明了一種壓縮算法,將它用在音樂、雲存儲、視頻等方面都大獲成功。雖然故事是虛構的,可是在一方面也說明了算法的重要性。數據結構
分析一個算法的複雜度,也是在分析一個算法的好壞優劣,簡單高效的算法纔是咱們應該追求的,而複雜低效的算法則是咱們須要改進的。算法的複雜度包括 時間複雜度 和 空間複雜度,下面將用盡可能少的概念來幫你搞懂這兩個度。函數
一、什麼是算法的時間複雜度?大數據
討論算法的時間複雜度,也是在討論程序使用該算法運行的時間。常常聽到身邊的同窗說個人電腦好慢呀,打開個軟件都要一分多鐘,急死人了。這從算法的角度看,只能說電腦硬件比較差,不必定說程序寫的很垃圾(固然也有可能有程序的鍋),一樣的程序可能在別人配置高的電腦上打開就很快。因此你看單純從程序運行須要的時間長短上,並不能反映算法的優劣,由於這和運行的設備也有很大關係(計算機計算主要用到的是 CPU 和 GPU)。人工智能
算法的時間複雜度並不能以具體的時間數值爲單位(如1秒鐘,1分鐘等),那算法複雜度中的時間單位是什麼呢?這個時間單位其實更像是程序中執行的次數或者步驟數。spa
舉個栗子,當你忘記東西放哪裏了,可能會把全部的抽屜都找一遍,假如你有 n 個抽屜,那麼找完 n 個抽屜就能夠找到你的東西了,每一個抽屜都找了一遍,就找了 n 遍。算法的時間複雜度(運行時間)用大 O 表示(不須要關心大 O 表示法怎麼來的,就是個名字),把你找東西的這個過程寫成程序,算法的時間複雜度就是 O(n),是否是感受算法其實就在咱們中間。code
在上面這個例子中,最好的狀況是,當你找完第一個抽屜,你就找到你的東西了,這固然是最好的了,用大 O 表示法表示就是 O(1),可是這樣的狀況存在偶然性,並不能表明算法的複雜度;最壞的狀況是,直到你找完最後一個抽屜,累的要死,你才找到你的東西,用大 O 表示法表示就是 O(n)。位於最壞和最好之間的狀況是,當你找到中間一個抽屜時,你找到的你的東西了,用大 O 表示法表示就是 O(n/2)。
那麼這三種狀況,哪種應該表明算法的時間複雜度呢?最好的狀況畢竟是小几率事件,不具備普適性,確定是不能表明算法真實的時間複雜度。平均的狀況,確實在必定程度上能夠反映出算法的時間複雜度,可是學過數學的咱們知道,平均值容易受到極端值的影響(在評委打分時也常常是去掉最高分和最低分),因此平均狀況也不是很合適。而最壞的狀況卻能夠給咱們一種保證,咱們內心也能夠有一個預期,這個算法在最差的狀況下表現如何(就像咱們作事也經常考慮最壞的狀況同樣),因此咱們用最壞狀況下的時間複雜度來衡量算法的時間複雜度。
對於大 O 括號內的參數(或者稱爲操做數)的係數,每每被咱們主動忽略,如一個計算出所需次數爲 n/2 的算法,用大 O 表示法表示是 O(n),而對於計算次數是個常數(如 1,5, 9)的算法,用大 O 表示法表示都是 O(1),這點是須要咱們注意一下的。爲何這樣作呢?由於對於計算次數是 n2/2 + n/2 + 5 這樣的算法,起決定做用的是 n,而不是 n 前面的係數,當 n 爲無窮大時,n 前面係數的影響就微不足道了,最終這個算法的時間複雜度用大 O 表示法爲 O(n2 + n)。
二、什麼是算法的空間複雜度?
程序運行時確定是要消耗空間資源的,寄存器、內存和磁盤等。輸入和輸出這兩部分佔用空間是必需的,因此程序處理的空間指的是程序運行算法時所需的那部分空間。先來看個例子,交換兩個數的值,相信你們都作過吧,通常的方法是找一箇中間變量存儲其中一個數的值,再讓一個數等於另外一個數的值,另一個數等於中間變量的值,就像下面的僞代碼這樣。
// 交換 a 和 b
temp = a
a = b
b = temp
複製代碼
棧這種數據結構,我都應該很熟悉,特色是先進後出(FILO),交換的這兩個數確定都是要放在棧中的,但因爲引入了中間變量實現,因此在程序棧中還要有中間變量的空間,一個變量佔用一塊棧空間(想象一下),咱們用一個格子來表示,就像下面這樣,中間變量也要佔用一個格子(其實這個格子在其餘棧中叫作 幀,如 Java虛擬機的本地方法棧和虛擬機棧,幀又是一種數據結構)。
由於這個值交換算法用到了中間變量,而中間變量又要佔用一個格子,因此這個算法的空間複雜度用大 O 表示法表示就是 O(1)。
相比較而言,算法的空間複雜度比較簡單,因此咱們在討論一個算法時,更多的是討論算法的時間複雜度。
三、一些常見的大 O 運行時間
這裏提到的算法,將在後面的文章中討論,感興趣的小夥伴不妨先搜索瞭解一下。
上面幾種大 O 運行時間,反應在圖中以下(注意:圖中曲線並不必定從原點開始畫的,只須要知道算法運行時間的大概走勢就能夠了):
算法的速度,指的並非時間,而是增速,反應的在圖中就是曲線的斜率,能夠看到,隨着輸入的增長,有的算法所須要的時間越來長,也就是使用這種算法的程序會愈來愈慢。
四、小結
算法的複雜度和須要的時間、空間都有關係,咱們更多談論的是算法的時間複雜度,算法的時間複雜度不是以秒爲單位,算法運行的速度是從其增速的角度度量的,也便是輸入越多,算法運行的時間改變的快慢。一個好的算法應該是時間複雜度和空間複雜度都比較低,通俗的說就是花最少的時間和精力達到最好的效果,可是這兩樣每每是很難同時作到的,這就須要咱們犧牲同樣來作到儘量的更好。
——本文轉自個人微信公衆號《編程心路》。