非算法方向的你前端
面了多少次試?git
最後,由於不懂算法,程序員
死在了半路上?github
這些痛,面試
做爲技術創新型公司的小編——個推君算法
怎麼會不懂?編程
爲此,個推君特請了我司經驗豐富的面試官json
爲你奉上一份熱乎的面試寶典。後端
寶典可不是面試題哦設計模式
僅送給想認真鑽研的童鞋
幫你們梳理知識點
讓你們觸類旁通,
offer拿到手軟!
注:此處建議你們使用 C 語言來學習數據結構與算法。
1、數據結構
數據結構是算法的基礎。你們須要對數據結構有個清晰的概念,由於大部分的算法題均須要帶入數據結構的概念來處理。科班出身的程序員或多或少學習過數據結構。咱們推薦你們能夠重溫下這本書,溫故而知新。
時間複雜度與空間複雜度
在說算法以前和你們科普兩個重要的理論知識:算法的時間複雜度與空間複雜度。
時間複雜度
算法的時間複雜度,用來度量算法的運行時間,記做: T(n) = O(f(n))。它表示隨着 輸入大小n 的增大,算法執行須要的時間的增加速度能夠用 f(n) 來描述,而且會忽略常量部分。
舉個例子
int aFunc(void) { printf("Hello, World!\n"); // 須要執行 1 次 return 0; // 須要執行 1 次}
調用此方法,printf("Hello, World!\n"); 執行了一次,那麼咱們記做 T(n) = O(1)
int aFunc(int n) { for(int i = 0; i<n; i++) { // 須要執行 (n + 1) 次 printf("Hello, World!\n"); // 須要執行 n 次 } return 0;// 須要執行 1 次}
此處輸出語句被執行了 n 次,那麼咱們記做 T(n) = O(n)
再看一個
int aFunc(int n) { for(int i = 0; i<3*n; i++) { for(int j = 0; j<2*n; i++) {// 須要執行 (n) 次 printf("Hello, World!\n"); // 須要執行 n 次 } return 0; }
此處輸出語句被執行了 (3n)(2*n) 次,f(n)=6n^2,因爲咱們是忽略常量的,因此 T(n)=O(n^2)
空間複雜度
是對一個算法在運行過程當中臨時佔用存儲空間大小的量度,記作S(n)=O(f(n))。
int i = 1; int j = 2; ++i; j++; int m = i + j
代碼中的 i、j、m 所分配的空間都不隨着處理數據量變化,所以它的空間複雜度 S(n) = O(1)。
int[] m = new int[n]; for(i=1; i<=n; ++i){ j = i; j++; }
這段代碼中,第一行new了一個數組出來,這個數據佔用的大小爲n,這段代碼的2-6行,雖然有循環,但沒有再分配新的空間。所以,這段代碼的空間複雜度主要看第一行便可,即 S(n) = O(n)。
字符串
字符串或串(String)是由數字、字母、下劃線組成的一串字符。通常記爲 s=「a1a2···an」(n>=0)。它是編程語言中表示文本的數據類型。在程序設計中,字符串(string)爲符號或數值的一個連續序列,如符號串(一串字符)或二進制數字串(一串二進制數字)。
這個很少說了,敲代碼的都懂:一切皆可字符串。
數組
所謂數組,是有序的元素序列。若將有限個類型相同的變量的集合命名,那麼這個名稱爲數組名。組成數組的各個變量稱爲數組的份量,也稱爲數組的元素,有時也稱爲下標變量。用於區分數組的各個元素的數字編號稱爲下標。
在 C 語言中,數組可分爲一維數組、二維數組、多維數組等。而涉及到怎麼建立數組這邊就不細講了,由於各個語言的的申明方法以及使用方法都不太相同。
二維數組結構圖
舉例
一、int a[10]; 說明整型數組a,有10個元素。若要表示第10個元素,則使用a[9]。第一個則是a[0]。
二、float b[10],c[20]; 說明實型數組b,有10個元素,實型數組c,有20個元素。
三、char ch[20]; 說明字符數組ch,有20個元素。
鏈表(Node)
鏈表是一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是經過鏈表中的指針連接次序實現的。鏈表由一系列結點(鏈表中每個元素稱爲結點)組成,結點能夠在運行時動態生成。每一個結點包括兩個部分:一個是存儲數據元素的數據域,另外一個是存儲下一個結點地址的指針域。
鏈表是咱們比較經常使用的數據結構,若是在編程的過程當中,須要進行頻繁 增&刪 操做的,優先使用鏈式存儲結構。鏈表分爲咱們常見的單鏈表,還有雙鏈表以及循環鏈表。
單鏈表結構體
typedef struct LNode{ int data; //data中存放結點數據域(默認是int型) struct LNode *next; //指向後繼結點的指針}LNode;
單鏈表結構圖
棧(Stack)
棧做爲一種數據結構,是一種只能在一端進行插入和刪除操做的特殊線性表。它按照先進後出的原則存儲數據,先進入的數據被壓入棧底,最後的數據在棧頂,須要讀數據的時候從棧頂開始彈出數據(最後一個數據被第一個讀出來)。
棧具備記憶做用,對棧的插入與刪除操做中,不須要改變棧底指針。棧是一種先進後出的的數據結構,有入棧和入棧兩種常見操做。
結構體
typedef struct Stack{ // 棧 PNODE pTop; PNODE pBottom; }STACK,*PSTACK;
結構圖
隊列(Queue)
隊列是一種特殊的線性表,特殊之處在於它只容許在表的前端(front)進行刪除操做,而在表的後端(rear)進行插入操做,和棧同樣,隊列是一種操做受限制的線性表。進行插入操做的端稱爲隊尾,進行刪除操做的端稱爲隊頭。隊列中沒有元素時,稱爲空隊列。
隊列是一種先進先出的的數據結構,有入隊和出隊兩種常見操做,通常有順序隊列,雙向隊列,循環隊列。
結構體
typedef struct{ ELemType data[MaxSize]; int front, rear; }SqQueue;
結構圖
樹
它是由n(n>=1)個有限結點組成一個具備層次關係的集合。把它叫作「樹」是由於它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。
樹是一個大類,樹下面又分 二叉樹,平衡二叉樹,AVL 樹,字典樹,哈夫曼樹,紅黑樹,b 樹等,考到最多的是二叉樹與紅黑樹。
結構體
下圖是二叉樹的結構體定義。
typedef struct TNode{ TElemType data; struct TNode *lchild,*rchild; }TNode,*Tree;
二叉樹結構圖
圖(Graph)
圖是由頂點的有窮非空集合和頂點之間邊的集合組成, 一般表示爲: G(V,E), 其中,G表示一個圖,V是圖G中頂點的集合,E是圖G中邊的集合。
一個圖就是一些頂點的集合,這些頂點經過一系列邊結對(鏈接)。頂點用圓圈表示,邊就是這些圓圈之間的連線。頂點之間經過邊鏈接。頂點有時也稱爲節點或者交點,邊有時也稱爲連接。
結構圖
2、算法和數據操做
粗略地講了下數據結構的理論,終於要進入正餐了。算法題大體分爲如下幾大類。還有一些小類,好比位運算、回溯算法等就不一一舉例了。
查找
查找也能夠叫作搜索,這是算法分類裏面最最最普通的一個模塊,查找分爲 線性查找、數表查找、哈希查找。
掌握程度:查找類型的題目較爲豐富,每一種數據結構都有本身的查找類型的經典算法,好比 數組對應的最小值/最大值查找、字符串對應的關鍵字查找、鏈表對應的迴環判斷等、樹對應的深度/廣度優先,二叉樹對應的 前/中/後 遍歷,最 小/大 堆查找 。這些本質是都是和查找算法相關的。
排序
說到排序,你們應該都很是熟悉了。排序分爲內排序和外排序,由於外排序的出現頻率較低,這邊只進行內排序的分析。咱們常見的選擇、冒泡、快排均屬於內排序。
具體的算法能夠搜索百度。掌握程度:除了基數排序和希爾排序,其餘幾個排序算法均須要能夠默寫算法的程度,接着要進行實戰操做,刷完下面的這些題目,對排序算法會有個清晰的理解。
推薦範例:
https://leetcode.com/tag/sort/
遞歸
講到遞歸的時候,咱們在使用的過程當中,特別要對遞歸終止的條件有個清晰的把握,否則很容易陷入死循環當中
掌握程度:連接裏面的就 14 道題目,咱們建議所有刷一遍。值得注意的是,遞歸的題目通常來講會有多種解法。
推薦範例:
https://leetcode-cn.com/tag/recursion/
動態規劃
動態規劃,又名DP算法(取自其Dynamic Programming的縮寫)。它最初是運籌學的一個分支,用來求解決策過程最優化的數學方法。
動態規劃經常適用於有重疊子問題和最優子結構性質的問題。該方法所耗時間每每遠少於樸素解法。
動態規劃背後的基本思想很是簡單。大體上,若要解一個給定問題,咱們須要解其不一樣部分的問題(即子問題),再根據子問題的解來得出原問題的解。動態規劃每每用於優化遞歸問題,例如斐波那契數列。若是咱們運用遞歸的方式來求解會重複計算不少相同的子問題,而用動態規劃的思想則能夠減小不少計算量。好比咱們常見的爬樓梯的問題(https://blog.csdn.net/xiaoyyidiaodiao/article/details/73621545),但須要注意這個問題通常有多種解法。
掌握程度: 這類問題的類似度很是高,但願你們能夠達到舉一反三的境界
推薦範例:
買賣股票的最佳時機
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
爬樓梯
https://leetcode-cn.com/classic/problems/climbing-stairs/description/
貪心算法
貪心算法(又稱貪婪算法)是指在對問題求解時,老是作出在當前看來是最好的選擇。也就是說,它不從總體最優上加以考慮,所作出的是在某種意義上的局部最優解。好比咱們常見的爬樓梯問題。
掌握程度: 若是不是面試算法工程師的話,建議刷完 10 道相對簡單的題目便可。
推薦範例:貪心算法範例
https://leetcode-cn.com/tag/greedy
圖
圖這塊知識相對比較深奧,若是不是從事這方面研究的,咱們掌握理論和一些經典的算法便可。好比 普利姆算法,克魯斯卡爾算法等,這些都是很是經典的算法。咱們須要知道經典算法的設計邏輯以及優缺點。
掌握程度: 熟悉各種存儲結構,精通經典算法的實現思路
推薦範例:圖算法的範例
https://leetcode-cn.com/tag/graph/
3、LeetCode 正確的打開方式
你們有沒有發現上個章節頻繁的出現了 LeetCode 這個字眼,那麼這個網站究竟是什麼呢?在介紹以前還得和你們說下怎麼作好題目練習嗎。練題網站有力扣 (LeetCode 中文版)、清華 ACM、浙大 ACM。
這幾個網站算法學習生態圈都比較穩定,LeetCode 也是其中的一種,咱們推薦用力扣(力扣的小編看到的話記得給咱們加個雞腿)。這是一個寶藏網站,裏面的內容很是多,你們感興趣的話能夠深刻研究下。
這上面的題目一共 有1117 道。咱們能夠從不一樣的維度去刷題,好比難易度、問題分類、經過率等。咱們推薦根據數據結構的分類來作練習題。好比你今天學了二叉樹,那麼打開 LeetCode,耗時 2 天作十道簡單題的二叉樹便可。按照這種思路,差很少 1 個月便可完成上述所描述的題目。
以上經驗供你們參考。經過這個網站的可視化界面,能夠較直觀地對本身的學習有個回顧。推薦使用 C 語言,由於 C 語言更接近底層,編譯速度會快那麼零點幾毫秒。別小看幾毫秒的時間哦,由於在 LeetCode 上,算法耗時的最小單位是 1 毫秒。差一毫秒但是差異巨大的!
若是你寫的算法僅打敗了 40%的提交記錄,咱們建議你重寫提交下。算法魅力就在於此,毫秒必爭!一個好的算法可讓程序的編譯速度成幾何倍速的增長。也能夠直接點擊柱狀圖,會有當前算法耗時的最優算法解。
前面說了一些 LeetCode 的東西,在實際開發過程當中,咱們安利你們一個 IDEA 的 LeetCode 的插件,名字就叫 leetcode 。使用這個小插件,能夠幫助咱們快速編譯所寫的 demo。編寫的算法能夠直接在編譯器裏測試 & 提交。
leetcode:
https://github.com/shuzijun/leetcode-editor
雜談
說個現實的問題,除非是算法工程師,你們在開發的過程當中極少用到算法的,那是否是反向推論算法僅是停留在作題&面試,僅須要應付面試便可了?那是否是隻要瘋狂刷題就能夠了!!!
但咱們仍是建議你們在理論的基礎上加以實踐,並但願你們能夠更深刻地學習數據結構與算法,這會幫助咱們加深對計算機運行的理解。
我的練習題庫,咱們比較推薦:
https://github.com/LiuLei0571/LeetCode
書籍咱們推薦:《算法 4 》、《漫畫算法》、《大話設計模式》、《大話數據結構》、《劍指 offer》。
彩蛋
感謝各位看官閱讀到最後,大家的支持是咱們創做的源泉。最後給你們出一道咱們在實際開發中遇到的算法問題。
有如下一個 json 字符串,編寫一個算法測試此 json 是否有迴路。action_chains 裏面有若干個動做,入口動做是 actionid=1,接着去執行下一個動做,下一個動做的 actionid 是上一個動做的 do 參數,出口是 actionid=100。
{ "action_chains": [ { "actionid": "1", "do": "2" }, { "actionid": "2", "do": "3" }, { "actionid": "3", "do": "4" }, { "actionid": "4", "do": "5" }, { "actionid": "5" } ] }