算法篇-初識算法
做者:星晴(當地小有名氣,小到只有本身知道的杰倫粉)java
1. 爲何算法如此重要?
咱們先不給算法是否重要就輕易下了一個結論,而且我也先不着急給算法下定義作解釋。算法
咱們如今先來看看一種現象: 哪一種語言是開發者們能夠一直高高興興用來編程的?一位美國的開發者經過Twitter對開發者們進行了一項調查 :你是否喜好XX語言 調查結果分析圖:
編程
基本上每月都會有一些相關對語言使用程度作了一個排名. 你們業見怪不怪了,也許你會好幾種語言, 但隨着工做時間增長和對技術深刻研究你會發現: 不是具體的技術而是算法這些基本的東西成爲了技術深刻的軟肋, 特別是在緊要關口 這個軟肋每每就會更疼。數組
如今不少算法已經被包裝到了語言、工具或者框架中, 人都是懶惰 ,既然有現成的東西不用 爲什麼要費力去創新。 這也就致使長期適用單一編程開發者視野上狹隘, 開發者很難有屬於本身的思惟方式. 這就是爲何有些人說本身作到必定程度後成了熟練「代碼工人」也不難解,起碼這個問題讓我感到很悲哀。數據結構
Ruby之父松本行弘[日本同行]就曾表示,注重的是算法而不是工具, 若是沒有本身的思惟方式和編程邏輯,很容易對某種具體的技術或者工具產生依賴性, 而這些編程工具和技術每每是國外開發, 假設有一天咱們沒有這些現成的工具和技術 咱們該怎麼辦? 豈不是成了一貧如洗了嗎?可是若是有了穩固的算法思惟編程世界裏東西都不可怕。框架
今天出了JAVA 明天出了一個C#, 後天還不知道要出了一個什麼XXX語言和新技術,因而乎你也成了那趕潮大軍中一員 一路疲憊被人牽着鼻子走, 到頭來你發現越深刻步伐走得越慢越是吃力,每每技術瞭解得越多、作得東西越深,這樣的體會越明顯, 借用一位網友的話說就是"內功」不到位. C# java Ruby 只是兩個你用來練習武功的招式, 「內功」則是這些花哨武功招式後本質, 招式能夠不少種,若是沒有 」內功」 招式變換再多也只是表面文章 罷了, 註定你是成不了一個名副其實的」武林高手」。數據結構和算法
看完了這個現象後,在回頭來講算法定義,算法說白了就是是解決問題的步驟,能夠把算法定義成解決一個分類問題的任意一種特殊的方法。函數
編程世界中 算法 + 數據結構 = 程序工具
總結一句:學好算法,走遍編程世界都不怕性能
2. 算法的性能標準-空間、時間複雜度
2.1 時間複雜度
爲了方便表達算法的時間複雜度,計算機科學家從數學界借鑑了一種簡潔又通用的方式,那就是大O記法。這種規範化語言使得咱們能夠輕鬆地指出一個算法的性能級別,也令學術交流變得簡單。
掌握了大O記法,就掌握了算法分析的專業工具。
大O記法:不關注算法所用的時間,只關注其所用的步數。
推導算法:大O推導法
一、用常數1取代運行時間中的全部加法常數 二、在修改後的運行次數函數中,只保留最高階項 三、若是最高階項存在且不是1,那麼咱們就去除於這個項相乘的常數。
常見的大O表達式:
-
O(1): 常數階
void func(){ int i=0;//執行1次 i++;//執行1次 i++;//執行1次 i++;//執行1次 } //共執行了4次,因此時間複雜度爲O(4);根據大O推導法,略去常數,因此此函數的時間複雜度爲O(1);
-
O(n): 線性階
void main(){ for(int i=0;i<n;i++) { func(); } } void func(){ printf("大O推導法");//執行1次 } //在main中,func共被執行了n次,因此main的時間複雜度爲O(n);
-
O(logn): 對數階
void main(){ for(int i=1;i<n;i++) { func(); i=2i; } } void func(){ printf("大O推導法");//執行1次 } /* 在main中, 由於i每次被乘2,因此,執行的算法爲 2的幾回相乘 大於 n,即 2^x>n,--> x= log2n , 在推導對數時間複雜度時,通常都是以10做爲對數的底數。 func共被執行了logn次,因此main的時間複雜度爲O(logn); */
-
O(n^2): 平方階
void main(){ for(int i=1;i<n;i++) { for(int j=1;j<n;j++) { func(); } } } void func(){ printf("大O推導法");//執行1次 } /* 在main中, func()共被執行了n^2,因此main的時間複雜度爲O(n^2); */
時間複雜度所耗費的時間是: O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) <O(2n) < O(n!) <O(nn)
2.2 空間複雜度
空間複雜度(Space Complexity)是對一個算法在運行過程當中臨時佔用存儲空間大小的量度,記作S(n)=O(f(n))。
既然時間複雜度不是用來計算程序具體耗時的,那麼我也應該明白,空間複雜度也不是用來計算程序實際佔用的空間的。 空間複雜度是對一個算法在運行過程當中臨時佔用存儲空間大小的一個量度,一樣反映的是一個趨勢,咱們用 S(n) 來定義。 空間複雜度分析和時間複雜度分析相似,你只須要把 **一次執行次數**等於**一個單位內存**把大 O 複雜度表示法重推一遍就會啦~
常見的大O表達式:
-
O(1): 常數階
若是算法執行所須要的臨時空間不隨着某個變量n的大小而變化,即此算法空間複雜度爲一個常量,可表示爲 O(1)
int i = 1; int j = 2; ++i; j++; int m = i + j; //代碼中的 i、j、m 所分配的空間都不隨着處理數據量變化,所以它的空間複雜度 S(n) = O(1)
-
O(n): 線性階
int[] m = new int[n] for(i=1; i<=n; ++i) { j = i; j++; } /**這段代碼中,第一行new了一個數組出來,這個數據佔用的大小爲n,這段代碼的2-6行,雖然有循環,但沒有再分配新的空間,所以,這段代碼的空間複雜度主要看第一行便可,即 S(n) = O(n) */
3. 總結
學會大O記法,咱們在比較算法時就有了一致的參考系。有了它,咱們就能夠在現實場景中測量各類數據結構和算法,寫出更快的代碼,更輕鬆地應對高負荷的環境。