A. 學編程需要哪些數學知識
1.學習方法:本人認為這比什麼都重要如果這個沒掌握的話,可能直接影響你的成敗。眾所周知。。計算機知識 尤其是編程涉及到的知識可以說浩如煙海---那麼面對這么多的知識該怎麼去學呢?
---重點:1重實踐,不要去想,把一個知識點完全徹底的掌握,那將是非常恐怖的,有編程經驗的朋友都知道,編程里每個知識點深糾起來的話是非常困難的,更不要說是新手了。。那麼知識點該掌握到什麼程度呢? 個人認為:1-知道它是做什麼 2-知道怎麼使用。 這就足夠了。。。。不要去管他的原理是什麼,能把東西做出來才是王道。。。
---重點:2多寫, 這個在編程界可以說是真理了,真正寫程序的人都知道,一段程序你理解了並不代表你就會寫了,那麼怎麼樣才能提高「寫」的能力呢? 本人認為要注意一下幾點 1- 練習多做是必然的。 2- 做練習時不要因為覺得代碼簡單就只看不敲,哪怕多敲一遍HelloWorld 都是有好處的。 3- 相似的代碼不要復制,我見過很多朋友,遇到兩段程序類似,就懶的敲直接粘貼過去修改。。。請記住這是軟體開發人員的做法,而你不是,目前你還只是一個學習者而已。所以 原則就是 能敲的就不要復制。
---重點:3把精力用在理解上而不要用在背上 寫程序的朋友都知道,函數---關鍵字---常用類什麼的,都非常的熟悉,為什麼我們背過嗎?沒有 寫的多了自然就記的牢了, 所以建議新手不要去死背什麼概念,或語法 一定要理解它的作用。。。
---重點:4 筆記,我認為這點很重要,我自學時全是看書,和視頻教程,然後總結對自己有用的東西。記在本上,而將來如果印象不深刻了由於是自己用自己理解的方式寫的,簡單翻一翻就能回憶起來,而如果,你忘了再去翻視頻 或 翻書的話。。那麼即使你曾經學過,也可能一時想不起來。。。
1.關於數學。。。這個問題,我覺得是目前爭論最多的話題,我見過N多人說 學編程要學XX數學---什麼微積分---什麼離散---嚇的新手連想都不敢想,我只想對這些人說一句,如果你懂,請你們幫助新手,如果你們不懂 請你們閉嘴 謝謝不要 誤人子弟。。。那麼下面我來 具體回答一下數學方面的問題。。。
1- 編程用數學嗎? 用! 回答是肯定的,但要看你是做哪方面的程序。 懂編程的都知道,現在編程基本分B/C構架,即:客戶端/瀏覽器端 與 C/S構架 即:客戶端/伺服器端 前者基本上就是JAVA PHP ASP.NET 等等。。。其中有多少地方用到了數學,如果還堅持沒數學學不了編程的朋友請站出來回答下我的問題。。。
至於C/S 如果不是做系統級的程序員 或 大型3D圖象處理 或者是音頻處理的軟體我請問又有多少地方用到了數學?如果你覺得x/y=z 這也算高等數學的話,我無話可說。。。。總結--除了3D等圖象處理編程 或 音頻處理編程 或系統級編程以外 其他編程對數學要求並不很高。。。。
2.關於英語, 我認為這個是個不可迴避的話題,學編程一點英語不懂我覺得不太現實,畢竟有很多文檔也是用英文寫的,而且程序員都知道,編程時經常要用簡單的英文,哪怕是定義個變數名,也要用英文起名, 沒見過哪個程序員定義的變數叫什麼aaa或bbb的。 那麼新手該怎麼面對英語呢, 我覺得很容易,按照書上或教程上去做就足夠了,1 編寫程序時 按規范要求去做,首先變數名,用見名知意思的英文單詞, 寫注釋時 也用英文短句。。。 拋異常時 也用英文來標注等等。。。。慢慢積累,時間久了你就會發現其實計算機里的英語 就只有那麼幾句而已。。。
3.關於學校 這個我也想提一下,有很多想以程序員為工作的朋友可能都考慮過找個培訓班---但我的建議是。不要去---起碼一般的不要去,為什麼?效果不好,就這么簡單,我親自到XXX著名編程培訓學校試聽過。。。結果很遺憾 一周才那麼幾天課,我3天閱讀的知識點比他們1個興趣 教的還多。。。而且上機和理論還是分開,新學的知識不能立刻上機實踐等等。。我覺得都是很嚴重的弊病。。。跟嚴重的那些所謂的學校給學生們造成了一種假象。。。只要在學校里考試合格了,出去就能做程序員,甚至軟體工程師了。。。最後他們將發現,原來他們在學校里學的 只是基礎中的基礎而已 - -
4. 自學的資料,我個人認為,自學第一重要的是 視頻教程,懂的人都知道,編程學習時重點並不完全是知識點,而是如何運用那些知識點,這也是項目經驗今天被人們這么看中的主要原因。。。所以視頻教程絕對是不二的選擇,現在網上的視頻教程非常之多 各種各樣的都有 具體怎麼找相信不用我教了 google 電驢 迅雷--我就是靠他們活過來的 。。。而且視頻教程還有一點是學校比不了的,那就是 你可以隨時看 重復看,一個知識點沒明白 你可以反復的聽10遍 20遍都沒問題, 學校恐怕就不行了吧。 另一個優點是可以在你狀態好時看, 大家都有狀態不好的時候,累了-困了 很正常,可在學校,誰管你? 老師講完了 聽不懂你自己的問題,而視頻呢,好辦 累了 先休息一會 有精神了 想怎麼看就怎麼看。。。我覺得 找到好的視頻教程。。比任何老師都重要。。至於出現問題不懂怎麼辦? 相信能來到著找到我這篇文章的朋友 都有辦法解決的。。
5.書 --- 我非常喜歡看視頻教程,但我堅決反對只看視頻不看書,為什麼?很簡單視頻傳授的是 寫程序的經驗 而書則是細膩的為你講解其中的原理。。所以我的建議是 先把一個知識的視頻看一遍,然後再把書翻一遍 然後自己再寫2遍 量變必然引起質變 我相信這是放之四海 而皆準的道理(指編程行業)
6.時間+態度 我認為這也很重要,很多人經常這樣問我,我1個月能學會編程么? 我半年能成為編程高手么? 我覺得有這樣心理的人比適合學編程。。。 學編程最忌心浮,一個知識點還沒弄明白 就想寫個項目出來 這是不可能的,這樣最後只能導致你自己喪失信心,編程要一步一步的來,相信我哪怕用一天時間才掌握了一個知識點,起碼比你用一天的時間 看完整本書強。。因為前者起碼你還是有點收獲的(指新手,老手兩天一本書很正常有經驗了嗎 - -) 這里我可以給大家一我的學習時間大家可做為參考。。。我是從0基礎開始一直到現在掌握j2ee基本所有的基礎開發技能 用時一年半,本人覺得不算慢 每天最少看書+練習5小時 每天不停這個是我的進度。
此文獻給想學編程卻又礙於各個方面左右不定的人,和正在學的初學者!
參考文獻:http://hi..com/vigorlin/profile!
B. 作為一個程序員,有哪些常用的演算法
常用的演算法有:遞推法、貪心法、列舉法、遞歸法、分治法和模擬法
原則:1. 扎實的基礎。數據結構、離散數學、編譯原理,這些是所有計算機科學的基礎,如果不掌握他們,很難寫出高水平的程序。據我的觀察,學計算機專業的人比學其他專業的人更能寫出高質量的軟體。程序人人都會寫,但當你發現寫到一定程度很難再提高的時候,就應該想想是不是要回過頭來學學這些最基本的理論。不要一開始就去學OOP,即使你再精通OOP,遇到一些基本演算法的時候可能也會束手無策。
2. 豐富的想像力。不要拘泥於固定的思維方式,遇到問題的時候要多想幾種解決問題的方案,試試別人從沒想過的方法。豐富的想像力是建立在豐富的知識的基礎上,除計算機以外,多涉獵其他的學科,比如天文、物理、數學等等。另外,多看科幻電影也是一個很好的途徑。
3. 最簡單的是最好的。這也許是所有科學都遵循的一條准則,如此復雜的質能互換原理在愛因斯坦眼裡不過是一個簡單得不能再簡單的公式:E=mc2。簡單的方法更容易被人理解,更容易實現,也更容易維護。遇到問題時要優先考慮最簡單的方案,只有簡單方案不能滿足要求時再考慮復雜的方案。
4. 不鑽牛角尖。當你遇到障礙的時候,不妨暫時遠離電腦,看看窗外的風景,聽聽輕音樂,和朋友聊聊天。當我遇到難題的時候會去玩游戲,而且是那種極暴力的打鬥類游戲,當負責游戲的那部分大腦細胞極度亢奮的時候,負責編程的那部分大腦細胞就得到了充分的休息。當重新開始工作的時候,我會發現那些難題現在竟然可以迎刃而解。
5. 對答案的渴求。人類自然科學的發展史就是一個渴求得到答案的過程,即使只能知道答案的一小部分也值得我們去付出。只要你堅定信念,一定要找到問題的答案,你才會付出精力去探索,即使最後沒有得到答案,在過程中你也會學到很多東西。
6. 多與別人交流。三人行必有我師,也許在一次和別人不經意的談話中,就可以迸出靈感的火花。多上上網,看看別人對同一問題的看法,會給你很大的啟發。
7. 良好的編程風格。注意養成良好的習慣,代碼的縮進編排,變數的命名規則要始終保持一致。大家都知道如何排除代碼中錯誤,卻往往忽視了對注釋的排錯。注釋是程序的一個重要組成部分,它可以使你的代碼更容易理解,而如果代碼已經清楚地表達了你的思想,就不必再加註釋了,如果注釋和代碼不一致,那就更加糟糕。
8. 韌性和毅力。這也許是"高手"和一般程序員最大的區別。A good programming is 99 weat and 1 ffee。高手們並不是天才,他們是在無數個日日夜夜中磨練出來的。成功能給我們帶來無比的喜悅,但過程卻是無比的枯燥乏味。你不妨做個測試,找個10000以內的素數表,把它們全都抄下來,然後再檢查三遍,如果能夠不間斷地完成這一工作,你就可以滿足這一條。
希望對你有幫助
C. 程序員必須掌握哪些演算法
一.基本演算法:
枚舉. (poj1753,poj2965)
貪心(poj1328,poj2109,poj2586)
遞歸和分治法.
遞推.
構造法.(poj3295)
模擬法.(poj1068,poj2632,poj1573,poj2993,poj2996)
二.圖演算法:
圖的深度優先遍歷和廣度優先遍歷.
最短路徑演算法(dijkstra,bellman-ford,floyd,heap+dijkstra)
(poj1860,poj3259,poj1062,poj2253,poj1125,poj2240)
最小生成樹演算法(prim,kruskal)
(poj1789,poj2485,poj1258,poj3026)
拓撲排序 (poj1094)
二分圖的最大匹配 (匈牙利演算法) (poj3041,poj3020)
最大流的增廣路演算法(KM演算法). (poj1459,poj3436)
三.數據結構.
串 (poj1035,poj3080,poj1936)
排序(快排、歸並排(與逆序數有關)、堆排) (poj2388,poj2299)
簡單並查集的應用.
哈希表和二分查找等高效查找法(數的Hash,串的Hash)
(poj3349,poj3274,POJ2151,poj1840,poj2002,poj2503)
哈夫曼樹(poj3253)
堆
trie樹(靜態建樹、動態建樹) (poj2513)
四.簡單搜索
深度優先搜索 (poj2488,poj3083,poj3009,poj1321,poj2251)
廣度優先搜索(poj3278,poj1426,poj3126,poj3087.poj3414)
簡單搜索技巧和剪枝(poj2531,poj1416,poj2676,1129)
五.動態規劃
背包問題. (poj1837,poj1276)
型如下表的簡單DP(可參考lrj的書 page149):
E[j]=opt{D+w(i,j)} (poj3267,poj1836,poj1260,poj2533)
E[i,j]=opt{D[i-1,j]+xi,D[i,j-1]+yj,D[i-1][j-1]+zij} (最長公共子序列) (poj3176,poj1080,poj1159)
C[i,j]=w[i,j]+opt{C[i,k-1]+C[k,j]}.(最優二分檢索樹問題)
六.數學
組合數學:
1.加法原理和乘法原理.
2.排列組合.
3.遞推關系.
(POJ3252,poj1850,poj1019,poj1942)
數論.
1.素數與整除問題
2.進制位.
3.同餘模運算.
(poj2635, poj3292,poj1845,poj2115)
計算方法.
1.二分法求解單調函數相關知識.(poj3273,poj3258,poj1905,poj3122)
七.計算幾何學.
幾何公式.
叉積和點積的運用(如線段相交的判定,點到線段的距離等). (poj2031,poj1039)
多邊型的簡單演算法(求面積)和相關判定(點在多邊型內,多邊型是否相交)
(poj1408,poj1584)
凸包. (poj2187,poj1113)
中級(校賽壓軸及省賽中等難度):
一.基本演算法:
C++的標准模版庫的應用. (poj3096,poj3007)
較為復雜的模擬題的訓練(poj3393,poj1472,poj3371,poj1027,poj2706)
二.圖演算法:
差分約束系統的建立和求解. (poj1201,poj2983)
最小費用最大流(poj2516,poj2516,poj2195)
雙連通分量(poj2942)
強連通分支及其縮點.(poj2186)
圖的割邊和割點(poj3352)
最小割模型、網路流規約(poj3308)
三.數據結構.
線段樹. (poj2528,poj2828,poj2777,poj2886,poj2750)
靜態二叉檢索樹. (poj2482,poj2352)
樹狀樹組(poj1195,poj3321)
RMQ. (poj3264,poj3368)
並查集的高級應用. (poj1703,2492)
KMP演算法. (poj1961,poj2406)
四.搜索
最優化剪枝和可行性剪枝
搜索的技巧和優化 (poj3411,poj1724)
記憶化搜索(poj3373,poj1691)
五.動態規劃
較為復雜的動態規劃(如動態規劃解特別的旅行商TSP問題等)
(poj1191,poj1054,poj3280,poj2029,poj2948,poj1925,poj3034)
記錄狀態的動態規劃. (POJ3254,poj2411,poj1185)
樹型動態規劃(poj2057,poj1947,poj2486,poj3140)
六.數學
組合數學:
1.容斥原理.
2.抽屜原理.
3.置換群與Polya定理(poj1286,poj2409,poj3270,poj1026).
4.遞推關系和母函數.
數學.
1.高斯消元法(poj2947,poj1487, poj2065,poj1166,poj1222)
2.概率問題. (poj3071,poj3440)
3.GCD、擴展的歐幾里德(中國剩餘定理) (poj3101)
計算方法.
1.0/1分數規劃. (poj2976)
2.三分法求解單峰(單谷)的極值.
3.矩陣法(poj3150,poj3422,poj3070)
4.迭代逼近(poj3301)
隨機化演算法(poj3318,poj2454)
雜題(poj1870,poj3296,poj3286,poj1095)
七.計算幾何學.
坐標離散化.
掃描線演算法(例如求矩形的面積和周長並,常和線段樹或堆一起使用)
(poj1765,poj1177,poj1151,poj3277,poj2280,poj3004)
多邊形的內核(半平面交)(poj3130,poj3335)
幾何工具的綜合應用.(poj1819,poj1066,poj2043,poj3227,poj2165,poj3429)
高級(regional中等難度):
一.基本演算法要求:
代碼快速寫成,精簡但不失風格
(poj2525,poj1684,poj1421,poj1048,poj2050,poj3306)
保證正確性和高效性. poj3434
二.圖演算法:
度限制最小生成樹和第K最短路. (poj1639)
最短路,最小生成樹,二分圖,最大流問題的相關理論(主要是模型建立和求解)
(poj3155, poj2112,poj1966,poj3281,poj1087,poj2289,poj3216,poj2446
最優比率生成樹. (poj2728)
最小樹形圖(poj3164)
次小生成樹.
無向圖、有向圖的最小環
三.數據結構.
trie圖的建立和應用. (poj2778)
LCA和RMQ問題(LCA(最近公共祖先問題) 有離線演算法(並查集+dfs) 和 在線演算法(RMQ+dfs)).(poj1330)
雙端隊列和它的應用(維護一個單調的隊列,常常在動態規劃中起到優化狀態轉移的目的). (poj2823)
左偏樹(可合並堆).
後綴樹(非常有用的數據結構,也是賽區考題的熱點).(poj3415,poj3294)
四.搜索
較麻煩的搜索題目訓練(poj1069,poj3322,poj1475,poj1924,poj2049,poj3426)
廣搜的狀態優化:利用M進制數存儲狀態、轉化為串用hash表判重、按位壓縮存儲狀態、雙向廣搜、A*演算法. (poj1768,poj1184,poj1872,poj1324,poj2046,poj1482)
深搜的優化:盡量用位運算、一定要加剪枝、函數參數盡可能少、層數不易過大、可以考慮雙向搜索或者是輪換搜索、IDA*演算法. (poj3131,poj2870,poj2286)
五.動態規劃
需要用數據結構優化的動態規劃.(poj2754,poj3378,poj3017)
四邊形不等式理論.
較難的狀態DP(poj3133)
六.數學
組合數學.
1.MoBius反演(poj2888,poj2154)
2.偏序關系理論.
博奕論.
1.極大極小過程(poj3317,poj1085)
2.Nim問題.
七.計算幾何學.
半平面求交(poj3384,poj2540)
可視圖的建立(poj2966)
點集最小圓覆蓋.
對踵點(poj2079)
D. 一文講透演算法中的時間復雜度和空間復雜度計算方式
作為一名「程序猿」,大家應該都聽過這么一句話:程序=數據結構+演算法。
這句話是由瑞士計算機科學家尼古拉斯·沃斯(Niklaus Wirth)在 1984 年獲得圖靈獎時說的一句話,這位大佬還以這句話為名出了一本書《Algorithms + Data Structures=Programs》,從此這句話就成為了大家耳熟能詳的一句名言。
隨著時間的推移,不管這句話是不是非常准確,但至少能說明數據結構與演算法對程序來說是非常核心的基礎,如果我們想要寫出更多優秀優雅的代碼,那麼數據結構與演算法是必須要掌握好的。
很多人可能覺得,我不會演算法,代碼一樣寫得很"溜",演算法這東西似乎用處不大。現在互聯網的發達,我們想要什麼幾乎都可以在網上找到現成的,各種框架功能十分強大,似乎看起來確實不用演算法也可以寫出「好代碼」。然而假如我們不懂演算法,比如項目中用到了排序,我們如何評估代碼的執行效率?再比如最常用的 ArrayList 和 LinkedList ,我們該如何選擇,又比如說我們需要去集合中找某一個數,又該如何寫出性能優秀的代碼呢?
同樣的代碼,如何判斷誰的代碼是優秀的代碼?可讀性,可擴展性,健壯性可能都可以用來判定,然而這些東西我覺得並不能直接體現出你代碼的優秀,因為對用戶而言,訪問你的代碼響應速度快那就是優秀的代碼,相反,動輒響應幾秒甚至更長時間的介面,恐怕就算你可讀性再好,再健壯也稱不上是好代碼。
所以說一段代碼是否優秀,最直接的判斷標准就是性能,而如果要寫出高性能的代碼,那麼就必須要了解演算法,而且拋開這個因素,但凡不想一輩子都寫 CRUD 代碼的,也需要去了解演算法,我們使用的很多框架和中間件底層都有數據結構和演算法的身影,學好演算法對我們源碼閱讀時理解其設計思想也是大有裨益的。
要說功利性的目的,那就是面試,目前很多大廠的面試,演算法基本必面,所以想進大廠的話,咱們也得好好學學演算法。
提到演算法,很多人的第一反應就是太難學了,學不會,或者說經常是看完就忘了,但是其實對於我們一個普通的開發者而言,因為並不需要我們去發明演算法,我們需要的僅僅只是去靈活的運用演算法,所以並不需要非常扎實的數據基礎,當然基本的數學常識還是要有的。
如果說需要去發明設計一款演算法,那就要去推導去證明演算法的可行性,這種是需要具有非常扎實的數學基礎的,一般人確實無法做到,然而我們普通程序員口中提到演算法無非是二分查找法,哈希演算法等,高級一點的就還有回溯,貪心,動態規劃等等,這些所謂的演算法都是已經有現成的公式了,我們要做的無非就是理解它,然後靈活的運用它。這就和我們以前學習數學公式一樣,給你一個公式,然後你去做題,做題的過程其實就是去靈活地運用這個公式。
演算法也是同理,都是有特定方法和特定思路的,我們也並不需要去推導證明這種方式為什麼可行,所以學習演算法沒有其他訣竅,就是先理解思路,然後多練,等熟練了,自然就可以靈活運用了,也不會說學了立刻就忘了。學完就忘無非兩個原因,一是沒理解,二是沒有練習鞏固。
數據結構與演算法經常是放在一起講,這兩者是沒辦法獨立的,因為演算法是為了達到某種目的的一種實現方式,而數據結構是一種載體,也就是說演算法必須依賴數據結構這種載體,否則就是空談。換句話說:數據結構是為演算法服務的,而演算法又需要作用在特定的數據結構之上。
一個演算法到底好不好,我們如何去評價?前面我們提到了,你的代碼好不好,最直觀的就是看響應速度,演算法也一樣,同樣實現一個目的(比如說排序),誰的演算法速度快,我們就可以認為誰的演算法更優,如果說兩種演算法實現的速度差不多,那麼我們還可以去評價演算法所佔用的空間,誰佔用的空間少,那麼就可以認為誰的演算法更優,這就是演算法的基礎:時間復雜度和空間復雜度。
學習演算法之前,我們必須要學會如何分析時間復雜度和空間復雜度(也就是「快」和「省」),否則自己寫出來的演算法自己都不知道演算法的效率。
接觸過演算法的都知道,演算法的時間復雜度是用大寫的「O」來表示的,比如: O(1) , O(n) , O(logn) , O(nlogn) , O(n²) 等等。
變數指的是變數,也就是一段代碼的執行時間是隨著變數的變化而變化的,而不變指的是常量,也就是不論我的變數如何改變,執行時間都不會改變。
接下來我們就實際的來分析下常用時間復雜度的例子來練習一下。
0(1) 復雜度演算法也稱之為常數階演算法。這里的 1 是用來代指常量,也就是說這個演算法的效率是固定的,無論你的數據量如何變化,效率都一樣,這種復雜度也是最優的一種演算法。
上面的示例中不論有多少行代碼,時間復雜度都是屬於常數階段。換言之:只要代碼不存在 循環 , 遞歸 等循環類調用,不論代碼有多少行,其復雜度都是常數階。
O(n) 復雜度演算法也稱之為線性階段。比如下面這個示例我們應該怎麼分析復雜度呢?
前面常量階沒分析是因為常量階比較容易理解,接下來我們就以線性階這個為例子來分析下具體是怎麼得到的。
我們假設每一行代碼的執行時間是 T ,那麼上面這段代碼的執行復雜度是多少呢?
答案很明顯,那就是 T+n*T ,也就是 (n+1)T ,而在演算法中有一個原則,那就是常量可以被忽略,所以就得到了 nT ,換成大 O 表示法就是 O(n) 。
這只是一個簡略的計算過程,大家也不用較真說每行代碼執行時間可能不一樣之類的,也不要較真說 for 循環佔用了一行,下面的大括弧也佔用了一行,如果要較真這個,那我建議可以去想一下 1=1 為什麼等於 2 。
演算法中的復雜度反應的只是一個趨勢,這里 O(n) 反應的就是一個趨勢,也就是說隨著 n 的變化,演算法的執行時間是會降低的。
知道了上面的線性階,那麼平方階就很好理解了,雙層循環就是平方階,同理,三次循環就是立方階, k 次循環就是 k 次方階。
O(logn) 也稱之為對數階,對數階也很常見,像二分查找,二叉樹之類的問題中會見到比較多的對數階復雜度,但是對數階也是比較難理解的一種演算法復雜度。
下面我們還是來看一個例子:
這段代碼又該如何分析復雜度呢?這段代碼最關鍵的就是要分析出 while 循環中到底循環了多少次,我們觀察這個循環,發現 i 並不是逐一遞增,而是不斷地翻倍: 1->2->4->8->16->32->64 一直到等於 n 為什麼才會結束,所以我們得到了這樣的一個公式: 2^x=n 。
也就是說我們只要計算出 x 的值,就得到了循環次數,而根據高中的數學知識我們可以得到 x=log2n ( 2 在下面,是底數,試了幾種方法都打不出來,放棄了),所以根據上面線性階的分析方法,我們省略常量,就得到了示例中的演算法復雜度為 O(log2n) 。
同樣的分析方式,下面的例子,我們可以很快地分析出復雜度就為 O(log3n) :
上面得到的 log3n 我們可以再做進一步的轉換: log3n=log32 * log2n ,而 log32 (注意這幾個地方的情況 3 是底數,在下面) 是一個常量,常量可以省略,所以也就得到了: O(log3n)=O(log2n) 。同樣的道理,不論底數是多少,其實最終都可以轉化成和 O(log2n) 相等,正因為如此,為了方便,我們演算法中通常就會省略底數,直接寫作 O(logn) 。
上面的數學公式大家如果忘了或者看不懂也沒關系,只要記住不論對數的底數是多少,我們都算作 O(logn) ,而對於一個演算法的復雜度是否是對數階,還有一個簡易的判斷方法: 當循環中下標以指定倍數形式衰減,那麼這就是一個對數階 。
如果理解了上面的對數階,那麼這種線性對數階就非常好理解了,只需要在對數階的演算法中再嵌一層循環就是線性對數階:
分析了前面這些最常用的時間復雜度,其實我們可以得到以下規律:
除了上面常用的復雜度之外,另外還有指數階,階層階,根號階等,這些接觸的相對會較少,我們就不特意做分析了,如果大家感興趣的話,可以自己去了解下。
前面我們分析的都是只有一段代碼比較復雜的情況下得到的復雜度結果,那麼假如我一個演算法中,有多段代碼都比較復雜呢?這時候復雜度該如何分析?
我們先看下面這個例子:
這個例子中有三個循環,首先第一個,是一個常量,那麼根據前面的結論,不論這個常量是多大,都屬於常量級,所以第一個循環中的復雜度為 O(1) ,第二個和第三個循環我們前面也分析過,復雜度分別為 O(n) 和 O(n²) 。
也就是這一段代碼中有三段代碼產生了三種不同復雜度,而且這三個復雜度可以很明顯得到的大小關系為: O(1)<o(n)<o(n²) span=""> </o(n)<o(n²)> ,像這種在同一個演算法中有明確大小關系的,我們就可以直接取最大值作為這個演算法的復雜度,所以這個例子中演算法的復雜度就是 O(n²) 。
接下來我們再來看一個例子:
這個例子我們同樣對三段循環分別分析可以分別得到如下復雜度: O(1) , O(m) , O(n) 。這時候我們只能知道 O(1) 最小可以忽略,但是後面兩個無法卻無法確定大小,所以這時候我們需要取兩段循環復雜度之和來作為演算法的復雜度,所以可以得到這個例子的演算法復雜度為: O(m+n) 。
上面分析的時間復雜度都是比較簡單的,實際演算法中可能會比示例中復雜的多,而且我們示例中只要是循環都是無腦循環,也就是一定從頭循環到尾,然而實際中我們有時候並不需要從頭循環到尾,可能中途就會結束循環,所以我們根據實際情況,又可以將時間復雜度從以下四個方面來進一步分析:
這四種類型的時間復雜度在這里只會介紹前面三種,因為第四種比較復雜,而且使用場景也非常有限,而且對於這四種復雜度的分析,大家也作為了解就可以,不敢興趣的朋友們可以跳過這一小部分,因為在絕大部分情況我們只需要分析最壞復雜度就行,也就是假設循環全部執行完畢場景下的時間復雜度。
我們通過一個例子來理解下最好時間復雜度:
這個方法就是在一個指定數組中找到指定元素的下標,找不到就返回 -1 ,這個方法比較簡單,應該比較好理解。
注意這個方法中的循環體,如果找到元素,那麼就直接返回,這就會有一個現象,那就是我這個循環體到底會循環多少次是不確定的,可能是 1 次,也可能是 n (假設數組的長度) 次,所以假如我們要找的元素就在數組中的第一個位置,那麼我循環一次就找到了,這個演算法的復雜度就是 O(1) ,這就是最好情況時間復雜度。
理解了最好時間復雜度,那麼最壞時間復雜度也很好理解了,那就是數組中不存在我要找到元素,或者說最後一個值才是我要找的元素,那麼這樣我就必須循環完整個數組,那麼時間復雜度就是 O(n) ,這也就是最壞時間復雜度。
最好時間復雜度和最壞時間復雜度畢竟只有特殊情況才會發生,概率還是相對較小,所以我們很容易就想到我們也需要有一個平均時間復雜度。
我們簡單的來分析一下,為了便於分析,我們假設一個元素在數組和不在數組中的概率都為 1/2 ,然後假如在數組在,那麼又假設元素出現在每個位置的概率也是一樣的,也就是每個位置出現元素的概率為: 1/n 。
所以最終得到的平均時間復雜度應該等於元素在數組中和元素不在數組中兩種情況相加。
因為元素在數組中的概率為 1/2 ,然後在每個位置出現的概率也為 1/n 。假如元素出現在第一個位置,復雜度為 1*(1/2n) ;假如元素出現在第二個位置,復雜度為 2 * (1/2n) ,最終得到當前場景下時間復雜度為: 1*(1/2n) + 2 * (1/2n) + ... + n*(1/2n) =(n+1)/4。
前面已經假定了元素不在數組中的概率為 1/2 ,所以當前場景下的時間復雜度為: n * (1/2) ,因為元素不在數組中,那麼這個演算法必然會將整個循環執行完畢,也就循環是 n 次。
最後我們把兩種情況的復雜度之和相加就得到了平均時間復雜度: (n+1)/4 + n/2 = (3n+1)/4 ,最終我們將常數類的系數忽略掉,就得到了平均時間復雜度為 O(n) 。
均攤時間復雜度的演算法需要使用攤還分析法,計算方式相對有點復雜,而且使用場景很有限,本文就不做過多介紹了。
空間復雜度全稱就是漸進空間復雜度,用來表示演算法的存儲空間與數據規模之間的增長關系。和時間復雜度一樣,空間復雜度也是用大 O 進行表示。
其實學會了分析時間復雜度,那麼空間復雜度的分析就簡單了,主要就看我們在一個演算法當中到底有沒有使用到了額外的空間來進行存儲數據,然後判斷這個額外空間的大小會不會隨著 n 的變化而變化,從而得到空間復雜度。
我們來看一個給數組賦值例子,假設這就是一個演算法,我們可以來分析下這個演算法的空間復雜度:
一開始定義了一個變數,這里需要空間,但是這是一個常量級的(不隨 n 的變化而變化),然後再定義了一個數組,數組的長度為 n ,這里數組也需要佔用空間,而且數組的空間是隨著 n 的變化而變化的,其餘代碼沒有佔用額外空間,所以我們就可以認為上面示例中的空間復雜度為 O(n) 。
對於演算法的空間復雜度也可以簡單的進行總結一下:
本文主要講述了為什麼要學習演算法,也簡單減少了數據結構與演算法之間的關系,隨後主要介紹了演算法中的入門知識:時間復雜度和空間復雜度。想要學好演算法,必須要掌握如何分析一個演算法的時間復雜度和空間復雜度,只有自己會分析這兩個個衡量演算法主要性能的標准,才能更好的寫出性能優秀的演算法,同時我們也講到了最好時間復雜度,最壞時間復雜度,平均時間復雜度和均攤時間復雜度,不過這四種復雜度的計算方式大家作為了解即可,等實際確實需要使用到再來回顧也不遲。
E. 為什麼計算機也懂先算乘法除法
計算機里的加減乘除運演算法則,是程序員根據數學里的運演算法則,寫入程序,告訴計算機的,計算機才懂得先算乘法,除法的。
F. C語言中自定義函數與系統函數的區別
自定義,顧名思義就是根據程序員的實際需要,自己定義的函數,函數有自己定義的參數,功能和返回值,自定義函數的目的是更好更方便的幫程序員更快的完成所需要的程序設計。
系統函數就是系統自帶的函數,它是通用的,就是所有程序員都能使用的函數,它有規定的介面,參數形式,功能和返回值,是編譯軟體實現定義好的函數,供所有程序員調用。
特點:
系統函數是通用的,所有程序員都知道,都能用,使編譯平台有普適性,但是不能滿足各種程序員特定的變成需求。
自定義函數是自我定義的,只有每個程序員自己知道,完成自己想要完成的特定意義的函數,它一般是對系統函數的集成編程。比如從屏幕上獲取一個字母,並判斷它是大寫還是小寫,如果大寫,就列印「大寫」,是小寫就列印「小寫」,我們需要先用getchar()獲取屏幕上的字母,再進行判斷,再進行輸出,如果以後我們很多程序都需要用到這三個步驟,那麼我們可以寫一個函數把他們三個步驟集成到一起,有自己的參數和返回值,那麼我們就利用了系統函數構造成了我們自定義的函數。
希望對你有幫助,歡迎再次提問
G. 軟體開發的程序員需要掌握多的數學知識
需要數學,但是這個數學不是說你現在學的數學這點知識,而是你是邏輯思維,如果你僅僅是想成為一個程序員,只是一個寫代碼的人,那你數學不需要太好,但是,如果你真想好好從事計算機這方面,尤其是想軟體開發,你必須得學好數學,計算機本來就是從數學里分支出來的,你越往上走也就越接近數學,你相信嗎,一個計算機的頂級專家不會寫代碼的人大有人在,什麼是程序。有一本書是,程序=數據結構+演算法。任何一門語言給你兩個月你都能把基本的學的差不多,就想蓋房子,寫代碼的程序員就相當於磚匠,你永遠成不了設計師。一個大的正規的項目,有80%的時間是在設計,設計有哪些模塊,用什麼技術,怎麼架構這個項目,怎麼通信等等。。。。而等設計完了20%的時間給程序員把代碼寫出來。寫了這么多,你自己好好想想,隨便問一個高手,看看那個會告訴你計算機不需要數學,
需要注意的是,數學課本里的具體知識、公式,而是一種數學的思維方式、邏輯思維能力。最後祝你能夠堅持走這條路,好運。